Compare commits
3 Commits
9b949af390
...
2426989161
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 2426989161 | |
Shadowfacts | 1439c8b162 | |
Shadowfacts | 5125cc3397 |
|
@ -185,6 +185,8 @@
|
|||
D686BBE324FBF8110068E6AA /* WrappedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */; };
|
||||
D68E525B24A3D77E0054355A /* TuskerRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */; };
|
||||
D68E525D24A3E8F00054355A /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525C24A3E8F00054355A /* SearchViewController.swift */; };
|
||||
D68E6F59253C9969001A1B4C /* MultiSourceEmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */; };
|
||||
D68E6F5F253C9B2D001A1B4C /* BaseEmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */; };
|
||||
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */; };
|
||||
D690797324A4EF9700023A34 /* UIBezierPath+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */; };
|
||||
D693DE5723FE1A6A0061E07D /* EnhancedNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */; };
|
||||
|
@ -523,6 +525,8 @@
|
|||
D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedProgressView.swift; sourceTree = "<group>"; };
|
||||
D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerRootViewController.swift; sourceTree = "<group>"; };
|
||||
D68E525C24A3E8F00054355A /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = "<group>"; };
|
||||
D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiSourceEmojiLabel.swift; sourceTree = "<group>"; };
|
||||
D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseEmojiLabel.swift; sourceTree = "<group>"; };
|
||||
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPageViewController.swift; sourceTree = "<group>"; };
|
||||
D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBezierPath+Helpers.swift"; sourceTree = "<group>"; };
|
||||
D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnhancedNavigationViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1278,7 +1282,9 @@
|
|||
D620483323D3801D008A63EF /* LinkTextView.swift */,
|
||||
D620483523D38075008A63EF /* ContentTextView.swift */,
|
||||
D620483723D38190008A63EF /* StatusContentTextView.swift */,
|
||||
D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */,
|
||||
D6969E9F240C8384002843CE /* EmojiLabel.swift */,
|
||||
D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */,
|
||||
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */,
|
||||
04ED00B021481ED800567C53 /* SteppedProgressView.swift */,
|
||||
D627943A23A55BA600D38C68 /* NavigableTableViewCell.swift */,
|
||||
|
@ -1866,6 +1872,7 @@
|
|||
D627944A23A6AD6100D38C68 /* BookmarksTableViewController.swift in Sources */,
|
||||
D64BC19223C271D9000D0238 /* MastodonActivity.swift in Sources */,
|
||||
D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */,
|
||||
D68E6F59253C9969001A1B4C /* MultiSourceEmojiLabel.swift in Sources */,
|
||||
D63F9C6E241D2D85004C03CF /* CompositionAttachment.swift in Sources */,
|
||||
D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */,
|
||||
D6EBF01523C55C0900AE061B /* UIApplication+Scenes.swift in Sources */,
|
||||
|
@ -1942,6 +1949,7 @@
|
|||
D64BC18A23C16487000D0238 /* UnpinStatusActivity.swift in Sources */,
|
||||
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */,
|
||||
D6757A7C2157E01900721E32 /* XCBManager.swift in Sources */,
|
||||
D68E6F5F253C9B2D001A1B4C /* BaseEmojiLabel.swift in Sources */,
|
||||
D6F1F84D2193B56E00F5FE67 /* Cache.swift in Sources */,
|
||||
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */,
|
||||
0427037C22B316B9000D31B6 /* SilentActionPrefs.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// BaseEmojiLabel.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 10/18/20.
|
||||
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
|
||||
|
||||
protocol BaseEmojiLabel: class {
|
||||
var emojiIdentifier: String? { get set }
|
||||
var emojiRequests: [ImageCache.Request] { get set }
|
||||
var emojiFont: UIFont { get }
|
||||
var emojiTextColor: UIColor { get }
|
||||
}
|
||||
|
||||
extension BaseEmojiLabel {
|
||||
func replaceEmojis(in string: String, emojis: [Emoji], identifier: String, completion: @escaping (NSAttributedString) -> Void) {
|
||||
let matches = emojiRegex.matches(in: string, options: [], range: NSRange(location: 0, length: string.utf16.count))
|
||||
guard !matches.isEmpty else {
|
||||
completion(NSAttributedString(string: string))
|
||||
return
|
||||
}
|
||||
|
||||
let emojiImages = MultiThreadDictionary<String, UIImage>(name: "BaseEmojiLabel Emoji Images")
|
||||
var foundEmojis = false
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
for emoji in emojis {
|
||||
// only make requests for emojis that are present in the text to avoid making unnecessary network requests
|
||||
guard matches.contains(where: { (match) in
|
||||
let matchShortcode = (string as NSString).substring(with: match.range(at: 1))
|
||||
return emoji.shortcode == matchShortcode
|
||||
}) else {
|
||||
continue
|
||||
}
|
||||
|
||||
foundEmojis = true
|
||||
|
||||
group.enter()
|
||||
let request = ImageCache.emojis.get(emoji.url) { (data) in
|
||||
defer { group.leave() }
|
||||
guard let data = data, let image = UIImage(data: data) else {
|
||||
return
|
||||
}
|
||||
emojiImages[emoji.shortcode] = image
|
||||
}
|
||||
if let request = request {
|
||||
emojiRequests.append(request)
|
||||
}
|
||||
}
|
||||
|
||||
guard foundEmojis else {
|
||||
completion(NSAttributedString(string: string))
|
||||
return
|
||||
}
|
||||
|
||||
group.notify(queue: .main) { [weak self] in
|
||||
// if e.g. the account changes before all emojis are loaded, don't bother trying to set them
|
||||
guard let self = self, self.emojiIdentifier == identifier else { return }
|
||||
|
||||
let mutAttrString = NSMutableAttributedString(string: string)
|
||||
// replaces the emojis starting from the end of the string as to not alter the indices of preceeding emojis
|
||||
for match in matches.reversed() {
|
||||
let shortcode = (string as NSString).substring(with: match.range(at: 1))
|
||||
guard let emojiImage = emojiImages[shortcode] else {
|
||||
continue
|
||||
}
|
||||
|
||||
let attachment = NSTextAttachment(emojiImage: emojiImage, in: self.emojiFont, with: self.emojiTextColor)
|
||||
let attachmentStr = NSAttributedString(attachment: attachment)
|
||||
mutAttrString.replaceCharacters(in: match.range, with: attachmentStr)
|
||||
}
|
||||
|
||||
completion(mutAttrString)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,12 +9,12 @@
|
|||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
|
||||
class EmojiLabel: UILabel, BaseEmojiLabel {
|
||||
|
||||
class EmojiLabel: UILabel {
|
||||
|
||||
private var emojiIdentifier: String?
|
||||
private var emojiRequests: [ImageCache.Request] = []
|
||||
var emojiIdentifier: String?
|
||||
var emojiRequests: [ImageCache.Request] = []
|
||||
var emojiFont: UIFont { font }
|
||||
var emojiTextColor: UIColor { textColor }
|
||||
|
||||
func setEmojis(_ emojis: [Emoji], identifier: String) {
|
||||
guard emojis.count > 0, let attributedText = attributedText else { return }
|
||||
|
@ -23,53 +23,9 @@ class EmojiLabel: UILabel {
|
|||
emojiRequests.forEach { $0.cancel() }
|
||||
emojiRequests = []
|
||||
|
||||
let matches = emojiRegex.matches(in: attributedText.string, options: [], range: attributedText.fullRange)
|
||||
guard !matches.isEmpty else { return }
|
||||
|
||||
let emojiImages = MultiThreadDictionary<String, UIImage>(name: "EmojiLabel Emoji Images")
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
for emoji in emojis {
|
||||
// only make requests for emojis that are present in the text to avoid making unnecessary network requests
|
||||
guard matches.contains(where: { (match) in
|
||||
let matchShortcode = (attributedText.string as NSString).substring(with: match.range(at: 1))
|
||||
return emoji.shortcode == matchShortcode
|
||||
}) else {
|
||||
continue
|
||||
}
|
||||
|
||||
group.enter()
|
||||
let request = ImageCache.emojis.get(emoji.url) { (data) in
|
||||
defer { group.leave() }
|
||||
guard let data = data, let image = UIImage(data: data) else {
|
||||
return
|
||||
}
|
||||
emojiImages[emoji.shortcode] = image
|
||||
}
|
||||
if let request = request {
|
||||
emojiRequests.append(request)
|
||||
}
|
||||
}
|
||||
|
||||
group.notify(queue: .main) { [weak self] in
|
||||
// if e.g. the account changes before all emojis are loaded, don't bother trying to set them
|
||||
replaceEmojis(in: attributedText.string, emojis: emojis, identifier: identifier) { [weak self] (newAttributedText) in
|
||||
guard let self = self, self.emojiIdentifier == identifier else { return }
|
||||
|
||||
let mutAttrString = NSMutableAttributedString(attributedString: attributedText)
|
||||
// replaces the emojis starting from the end of the string as to not alter the indicies of preceeding emojis
|
||||
for match in matches.reversed() {
|
||||
let shortcode = (mutAttrString.string as NSString).substring(with: match.range(at: 1))
|
||||
guard let emojiImage = emojiImages[shortcode] else {
|
||||
continue
|
||||
}
|
||||
|
||||
let attachment = NSTextAttachment(emojiImage: emojiImage, in: self.font, with: self.textColor)
|
||||
let attachmentStr = NSAttributedString(attachment: attachment)
|
||||
mutAttrString.replaceCharacters(in: match.range, with: attachmentStr)
|
||||
}
|
||||
|
||||
self.attributedText = mutAttrString
|
||||
self.attributedText = newAttributedText
|
||||
self.setNeedsLayout()
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// MultiSourceEmojiLabel.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 10/18/20.
|
||||
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
|
||||
|
||||
class MultiSourceEmojiLabel: UILabel, BaseEmojiLabel {
|
||||
var emojiIdentifier: String?
|
||||
var emojiRequests = [ImageCache.Request]()
|
||||
var emojiFont: UIFont { font }
|
||||
var emojiTextColor: UIColor { textColor }
|
||||
|
||||
var combiner: (([NSAttributedString]) -> NSAttributedString)?
|
||||
|
||||
func setEmojis(pairs: [(String, [Emoji])], identifier: String) {
|
||||
guard pairs.count > 0 else { return }
|
||||
|
||||
self.emojiIdentifier = identifier
|
||||
emojiRequests.forEach { $0.cancel() }
|
||||
emojiRequests = []
|
||||
|
||||
var attributedStrings = pairs.map { NSAttributedString(string: $0.0) }
|
||||
|
||||
func recombine() {
|
||||
if let combiner = self.combiner {
|
||||
self.attributedText = combiner(attributedStrings)
|
||||
}
|
||||
}
|
||||
|
||||
recombine()
|
||||
|
||||
for (index, (string, emojis)) in pairs.enumerated() {
|
||||
self.replaceEmojis(in: string, emojis: emojis, identifier: identifier) { (attributedString) in
|
||||
attributedStrings[index] = attributedString
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self, self.emojiIdentifier == identifier else { return }
|
||||
recombine()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -19,7 +19,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
|||
@IBOutlet weak var verticalStackView: UIStackView!
|
||||
@IBOutlet weak var actionAvatarStackView: UIStackView!
|
||||
@IBOutlet weak var timestampLabel: UILabel!
|
||||
@IBOutlet weak var actionLabel: UILabel!
|
||||
@IBOutlet weak var actionLabel: MultiSourceEmojiLabel!
|
||||
@IBOutlet weak var statusContentLabel: UILabel!
|
||||
|
||||
var group: NotificationGroup!
|
||||
|
@ -35,14 +35,12 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
|||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
actionLabel.combiner = self.updateActionLabel
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
||||
}
|
||||
|
||||
@objc func updateUIForPreferences() {
|
||||
// todo: is this compactMap necessary?
|
||||
let people = group.notifications.compactMap { mastodonController.persistentContainer.account(for: $0.account.id) }
|
||||
updateActionLabel(people: people)
|
||||
|
||||
for case let imageView as UIImageView in actionAvatarStackView.arrangedSubviews {
|
||||
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: imageView)
|
||||
}
|
||||
|
@ -100,8 +98,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
|||
NSLayoutConstraint.activate(imageViews.map { $0.widthAnchor.constraint(equalTo: $0.heightAnchor) })
|
||||
|
||||
updateTimestamp()
|
||||
|
||||
updateActionLabel(people: people)
|
||||
actionLabel.setEmojis(pairs: people.map { ($0.displayOrUserName, $0.emojis) }, identifier: group.id)
|
||||
|
||||
let doc = try! SwiftSoup.parse(status.content)
|
||||
statusContentLabel.text = try! doc.text()
|
||||
|
@ -135,7 +132,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
|||
}
|
||||
}
|
||||
|
||||
func updateActionLabel(people: [AccountMO]) {
|
||||
func updateActionLabel(names: [NSAttributedString]) -> NSAttributedString {
|
||||
let verb: String
|
||||
switch group.kind {
|
||||
case .favourite:
|
||||
|
@ -145,18 +142,27 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
|||
default:
|
||||
fatalError()
|
||||
}
|
||||
let peopleStr: String
|
||||
|
||||
// todo: figure out how to localize this
|
||||
// todo: update to use managed objects
|
||||
switch people.count {
|
||||
let str = NSMutableAttributedString(string: "\(verb) by ")
|
||||
switch names.count {
|
||||
case 1:
|
||||
peopleStr = people.first!.displayName
|
||||
str.append(names.first!)
|
||||
case 2:
|
||||
peopleStr = people.first!.displayName + " and " + people.last!.displayName
|
||||
str.append(names.first!)
|
||||
str.append(NSAttributedString(string: " and "))
|
||||
str.append(names.last!)
|
||||
default:
|
||||
peopleStr = people.dropLast().map { $0.displayName }.joined(separator: ", ") + ", and " + people.last!.displayName
|
||||
for (index, name) in names.enumerated() {
|
||||
str.append(name)
|
||||
if index < names.count - 2 {
|
||||
str.append(NSAttributedString(string: ", "))
|
||||
} else if index == names.count - 2 {
|
||||
str.append(NSAttributedString(string: ", and "))
|
||||
}
|
||||
}
|
||||
}
|
||||
actionLabel.text = "\(verb) by \(peopleStr)"
|
||||
return str
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17504.1"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -29,17 +31,17 @@
|
|||
</constraints>
|
||||
</stackView>
|
||||
<view contentMode="scaleToFill" horizontalHuggingPriority="249" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5Ef-5g-b23">
|
||||
<rect key="frame" x="197.5" y="0.0" width="0.0" height="30"/>
|
||||
<rect key="frame" x="197.5" y="0.0" width="0.5" height="30"/>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JN0-Bf-3qx">
|
||||
<rect key="frame" x="205.5" y="0.0" width="24.5" height="30"/>
|
||||
<rect key="frame" x="206" y="0.0" width="24" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="light" pointSize="17"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Actioned by Person 1, Person 2, and Person 3" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fkn-Gk-ngr">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Actioned by Person 1, Person 2, and Person 3" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fkn-Gk-ngr" customClass="MultiSourceEmojiLabel" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="34" width="230" height="41"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
|
@ -48,7 +50,7 @@
|
|||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lc7-zZ-HrZ">
|
||||
<rect key="frame" x="0.0" y="79" width="230" height="74"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
|
@ -83,4 +85,9 @@
|
|||
<point key="canvasLocation" x="-394.20289855072468" y="56.584821428571423"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<systemColor name="secondaryLabelColor">
|
||||
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -16,7 +16,7 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
|||
|
||||
@IBOutlet weak var avatarStackView: UIStackView!
|
||||
@IBOutlet weak var timestampLabel: UILabel!
|
||||
@IBOutlet weak var actionLabel: UILabel!
|
||||
@IBOutlet weak var actionLabel: MultiSourceEmojiLabel!
|
||||
|
||||
var group: NotificationGroup!
|
||||
|
||||
|
@ -30,13 +30,12 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
|||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
actionLabel.combiner = self.updateActionLabel
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
||||
}
|
||||
|
||||
@objc func updateUIForPreferences() {
|
||||
let people = group.notifications.compactMap { mastodonController.persistentContainer.account(for: $0.account.id) }
|
||||
updateActionLabel(people: people)
|
||||
|
||||
for case let imageView as UIImageView in avatarStackView.arrangedSubviews {
|
||||
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: imageView)
|
||||
}
|
||||
|
@ -47,7 +46,9 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
|||
|
||||
let people = group.notifications.compactMap { mastodonController.persistentContainer.account(for: $0.account.id) }
|
||||
|
||||
updateActionLabel(people: people)
|
||||
actionLabel.setEmojis(pairs: people.map {
|
||||
($0.displayOrUserName, $0.emojis)
|
||||
}, identifier: group.id)
|
||||
updateTimestamp()
|
||||
|
||||
avatarStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
||||
|
@ -71,20 +72,27 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
|||
}
|
||||
}
|
||||
|
||||
func updateActionLabel(people: [AccountMO]) {
|
||||
// todo: custom emoji in people display names
|
||||
func updateActionLabel(names: [NSAttributedString]) -> NSAttributedString {
|
||||
// todo: figure out how to localize this
|
||||
let peopleStr: String
|
||||
switch people.count {
|
||||
let str = NSMutableAttributedString(string: "Followed by ")
|
||||
switch names.count {
|
||||
case 1:
|
||||
peopleStr = people.first!.displayOrUserName
|
||||
str.append(names.first!)
|
||||
case 2:
|
||||
peopleStr = people.first!.displayOrUserName + " and " + people.last!.displayOrUserName
|
||||
str.append(names.first!)
|
||||
str.append(NSAttributedString(string: " and "))
|
||||
str.append(names.last!)
|
||||
default:
|
||||
peopleStr = people.dropLast().map { $0.displayOrUserName }.joined(separator: ", ") + ", and " + people.last!.displayOrUserName
|
||||
|
||||
for (index, name) in names.enumerated() {
|
||||
str.append(name)
|
||||
if index < names.count - 2 {
|
||||
str.append(NSAttributedString(string: ", "))
|
||||
} else if index == names.count - 2 {
|
||||
str.append(NSAttributedString(string: ", and "))
|
||||
}
|
||||
}
|
||||
}
|
||||
actionLabel.text = "Followed by \(peopleStr)"
|
||||
return str
|
||||
}
|
||||
|
||||
func updateTimestamp() {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17504.1"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -29,17 +31,17 @@
|
|||
</constraints>
|
||||
</stackView>
|
||||
<view contentMode="scaleToFill" horizontalHuggingPriority="249" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eEp-GR-rtF">
|
||||
<rect key="frame" x="205.5" y="0.0" width="0.0" height="30"/>
|
||||
<rect key="frame" x="205.5" y="0.0" width="0.5" height="30"/>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Iub-HC-orP">
|
||||
<rect key="frame" x="205.5" y="0.0" width="24.5" height="30"/>
|
||||
<rect key="frame" x="206" y="0.0" width="24" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="light" pointSize="17"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Followed by Person 1 and Person 2" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bHA-9x-pcO">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Followed by Person 1 and Person 2" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bHA-9x-pcO" customClass="MultiSourceEmojiLabel" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="30" width="230" height="46"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
|
@ -48,7 +50,7 @@
|
|||
</subviews>
|
||||
</stackView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="person.badge.plus.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="7gy-KD-YT1">
|
||||
<rect key="frame" x="36" y="12.5" width="30" height="30.5"/>
|
||||
<rect key="frame" x="34" y="12.5" width="32" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="30" id="gvV-4g-2Xr"/>
|
||||
<constraint firstAttribute="height" constant="30" id="lS8-fq-ptY"/>
|
||||
|
@ -74,6 +76,9 @@
|
|||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="person.badge.plus.fill" catalog="system" width="64" height="58"/>
|
||||
<image name="person.badge.plus.fill" catalog="system" width="128" height="124"/>
|
||||
<systemColor name="secondaryLabelColor">
|
||||
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17124"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17504.1"/>
|
||||
<capability name="Image references" minToolsVersion="12.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
|
@ -97,10 +97,10 @@
|
|||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IF9-9U-Gk0" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="176" width="343" height="193"/>
|
||||
<rect key="frame" x="0.0" y="176" width="343" height="0.0"/>
|
||||
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="IF9-9U-Gk0" secondAttribute="height" multiplier="16:9" id="5oh-eK-J5d"/>
|
||||
<constraint firstAttribute="height" secondItem="IF9-9U-Gk0" secondAttribute="width" multiplier="9:16" priority="999" id="5oh-eK-J5d"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ejU-sO-Og5">
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17124"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17504.1"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
|
@ -115,55 +115,14 @@
|
|||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<view hidden="YES" contentMode="scaleToFill" verticalCompressionResistancePriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="nbq-yr-2mA" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="169.5" width="277" height="156"/>
|
||||
<rect key="frame" x="0.0" y="169.5" width="277" height="0.0"/>
|
||||
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="nbq-yr-2mA" secondAttribute="height" multiplier="16:9" id="Rvt-zs-fkd"/>
|
||||
<constraint firstAttribute="height" secondItem="nbq-yr-2mA" secondAttribute="width" multiplier="9:16" priority="999" id="Rvt-zs-fkd"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw">
|
||||
<rect key="frame" x="0.0" y="169.5" width="343" height="26"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa">
|
||||
<rect key="frame" x="0.0" y="0.0" width="86" height="26"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Reply"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="replyPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="ybz-3W-jAa"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="x0t-TR-jJ4">
|
||||
<rect key="frame" x="86" y="0.0" width="85.5" height="26"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Favorite"/>
|
||||
<state key="normal" image="star.fill" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="favoritePressed" destination="iN0-l3-epB" eventType="touchUpInside" id="8Q8-Rz-k02"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6tW-z8-Qh9">
|
||||
<rect key="frame" x="171.5" y="0.0" width="86" height="26"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Reblog"/>
|
||||
<state key="normal" image="repeat" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="reblogPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="Wa2-ZA-TBo"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="982-J4-NGl">
|
||||
<rect key="frame" x="257.5" y="0.0" width="85.5" height="26"/>
|
||||
<accessibility key="accessibilityConfiguration" label="More Actions"/>
|
||||
<state key="normal" image="ellipsis" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="morePressed" destination="iN0-l3-epB" eventType="touchUpInside" id="WT4-fi-usq"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="26" id="PnB-XK-9U0"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="oie-wK-IpU">
|
||||
<rect key="frame" x="0.0" y="54" width="50" height="22"/>
|
||||
<subviews>
|
||||
|
@ -186,20 +145,78 @@
|
|||
</imageView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TUP-Nz-5Yh">
|
||||
<rect key="frame" x="0.0" y="169.5" width="335" height="26"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa">
|
||||
<rect key="frame" x="0.0" y="0.0" width="84" height="26"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Reply"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="replyPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="ybz-3W-jAa"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6tW-z8-Qh9">
|
||||
<rect key="frame" x="167.5" y="0.0" width="84" height="26"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Reblog"/>
|
||||
<state key="normal" image="repeat" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="reblogPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="Wa2-ZA-TBo"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="982-J4-NGl">
|
||||
<rect key="frame" x="251.5" y="0.0" width="83.5" height="26"/>
|
||||
<accessibility key="accessibilityConfiguration" label="More Actions"/>
|
||||
<state key="normal" image="ellipsis" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="morePressed" destination="iN0-l3-epB" eventType="touchUpInside" id="WT4-fi-usq"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="x0t-TR-jJ4">
|
||||
<rect key="frame" x="84" y="0.0" width="83.5" height="26"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Favorite"/>
|
||||
<state key="normal" image="star.fill" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="favoritePressed" destination="iN0-l3-epB" eventType="touchUpInside" id="8Q8-Rz-k02"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="26" id="1FK-Er-G11"/>
|
||||
<constraint firstAttribute="bottom" secondItem="rKF-yF-KIa" secondAttribute="bottom" id="KyG-2C-MgN"/>
|
||||
<constraint firstItem="x0t-TR-jJ4" firstAttribute="top" secondItem="TUP-Nz-5Yh" secondAttribute="top" id="L3w-JH-eeG"/>
|
||||
<constraint firstItem="6tW-z8-Qh9" firstAttribute="top" secondItem="TUP-Nz-5Yh" secondAttribute="top" id="N7j-f4-gvP"/>
|
||||
<constraint firstItem="982-J4-NGl" firstAttribute="leading" secondItem="6tW-z8-Qh9" secondAttribute="trailing" id="VQo-DJ-C7L"/>
|
||||
<constraint firstItem="982-J4-NGl" firstAttribute="top" secondItem="TUP-Nz-5Yh" secondAttribute="top" id="W53-1a-fKu"/>
|
||||
<constraint firstItem="x0t-TR-jJ4" firstAttribute="leading" secondItem="rKF-yF-KIa" secondAttribute="trailing" id="WPd-A2-6Ju"/>
|
||||
<constraint firstItem="rKF-yF-KIa" firstAttribute="width" secondItem="x0t-TR-jJ4" secondAttribute="width" id="X7m-pJ-oje"/>
|
||||
<constraint firstItem="rKF-yF-KIa" firstAttribute="leading" secondItem="TUP-Nz-5Yh" secondAttribute="leading" placeholder="YES" id="aFR-Ew-99S"/>
|
||||
<constraint firstAttribute="bottom" secondItem="982-J4-NGl" secondAttribute="bottom" id="eXy-3h-51w"/>
|
||||
<constraint firstAttribute="bottom" secondItem="x0t-TR-jJ4" secondAttribute="bottom" id="euN-Nf-rwh"/>
|
||||
<constraint firstItem="6tW-z8-Qh9" firstAttribute="leading" secondItem="x0t-TR-jJ4" secondAttribute="trailing" id="oAK-VG-bbp"/>
|
||||
<constraint firstAttribute="bottom" secondItem="6tW-z8-Qh9" secondAttribute="bottom" id="tpf-Q3-V3l"/>
|
||||
<constraint firstAttribute="trailing" secondItem="982-J4-NGl" secondAttribute="trailing" id="uQG-FZ-F7u"/>
|
||||
<constraint firstItem="rKF-yF-KIa" firstAttribute="width" secondItem="982-J4-NGl" secondAttribute="width" id="vir-iq-biv"/>
|
||||
<constraint firstItem="rKF-yF-KIa" firstAttribute="width" secondItem="6tW-z8-Qh9" secondAttribute="width" id="vqw-d7-VtZ"/>
|
||||
<constraint firstItem="rKF-yF-KIa" firstAttribute="top" secondItem="TUP-Nz-5Yh" secondAttribute="top" id="wWH-J7-egM"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="gIY-Wp-RSk" firstAttribute="leading" secondItem="QMP-j2-HLn" secondAttribute="trailing" constant="8" id="0Tm-v7-Ts4"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="QMP-j2-HLn" secondAttribute="bottom" constant="8" id="2Ao-Gj-fY3"/>
|
||||
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="trailing" secondItem="ve3-Y1-NQH" secondAttribute="trailingMargin" id="3l0-tE-Ak1"/>
|
||||
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="top" secondItem="gIY-Wp-RSk" secondAttribute="bottom" id="4KL-a3-qyf"/>
|
||||
<constraint firstItem="oie-wK-IpU" firstAttribute="top" secondItem="QMP-j2-HLn" secondAttribute="bottom" constant="4" id="7Mp-WS-FhY"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Zlb-yt-NTw" secondAttribute="bottom" id="HOe-6l-ES0"/>
|
||||
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="oie-wK-IpU" secondAttribute="bottom" id="7Xp-Sa-Rfk"/>
|
||||
<constraint firstItem="QMP-j2-HLn" firstAttribute="top" secondItem="ve3-Y1-NQH" secondAttribute="top" id="PC4-Bi-QXm"/>
|
||||
<constraint firstItem="oie-wK-IpU" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="QKi-ny-jOJ"/>
|
||||
<constraint firstItem="Zlb-yt-NTw" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="aUm-Uo-wkY"/>
|
||||
<constraint firstItem="Zlb-yt-NTw" firstAttribute="top" secondItem="gIY-Wp-RSk" secondAttribute="bottom" id="dOI-nF-1L9"/>
|
||||
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="QZ2-iO-ckC"/>
|
||||
<constraint firstItem="gIY-Wp-RSk" firstAttribute="top" secondItem="QMP-j2-HLn" secondAttribute="top" id="fEd-wN-kuQ"/>
|
||||
<constraint firstItem="gIY-Wp-RSk" firstAttribute="leading" secondItem="oie-wK-IpU" secondAttribute="trailing" constant="8" id="fqd-p6-oGe"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Zlb-yt-NTw" secondAttribute="trailing" id="gxD-EE-ISA"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="gIY-Wp-RSk" secondAttribute="trailing" id="hKk-kO-wFT"/>
|
||||
<constraint firstAttribute="bottom" secondItem="TUP-Nz-5Yh" secondAttribute="bottom" id="rmQ-QM-Llu"/>
|
||||
<constraint firstItem="QMP-j2-HLn" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="zeW-tQ-uJl"/>
|
||||
</constraints>
|
||||
</view>
|
||||
|
|
Loading…
Reference in New Issue