Merge branch 'charlesmchen/contactCellViewAccessories'
This commit is contained in:
commit
e93d13ac49
@ -8,11 +8,11 @@ public typealias CVStackViewConfig = OWSStackView.Config
|
||||
|
||||
// MARK: -
|
||||
|
||||
class CVStackView {
|
||||
public extension UIStackView {
|
||||
|
||||
public static func measure(config: CVStackViewConfig,
|
||||
subviewSizes: [CGSize],
|
||||
verboseLogging: Bool = false) -> CGSize {
|
||||
static func measure(config: CVStackViewConfig,
|
||||
subviewSizes: [CGSize],
|
||||
verboseLogging: Bool = false) -> CGSize {
|
||||
|
||||
let spacingCount = max(0, subviewSizes.count - 1)
|
||||
|
||||
@ -56,6 +56,21 @@ class CVStackView {
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func apply(config: CVStackViewConfig) {
|
||||
if self.axis != config.axis {
|
||||
self.axis = config.axis
|
||||
}
|
||||
if self.alignment != config.alignment {
|
||||
self.alignment = config.alignment
|
||||
}
|
||||
if self.spacing != config.spacing {
|
||||
self.spacing = config.spacing
|
||||
}
|
||||
if self.layoutMargins != config.layoutMargins {
|
||||
self.layoutMargins = config.layoutMargins
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@ -380,25 +380,38 @@ class MessageDetailViewController: OWSTableViewController2 {
|
||||
|
||||
private func buildAccessoryView(text: String,
|
||||
displayUDIndicator: Bool,
|
||||
transaction: SDSAnyReadTransaction) -> UIView {
|
||||
let label = UILabel()
|
||||
label.textColor = Theme.ternaryTextColor
|
||||
label.text = text
|
||||
transaction: SDSAnyReadTransaction) -> ContactCellAccessoryView {
|
||||
let label = CVLabel()
|
||||
label.textAlignment = .right
|
||||
label.font = .ows_dynamicTypeFootnoteClamped
|
||||
let labelConfig = CVLabelConfig(text: text,
|
||||
font: .ows_dynamicTypeFootnoteClamped,
|
||||
textColor: Theme.ternaryTextColor)
|
||||
labelConfig.applyForRendering(label: label)
|
||||
let labelSize = CVText.measureLabel(config: labelConfig, maxWidth: .greatestFiniteMagnitude)
|
||||
|
||||
let shouldShowUD = preferences.shouldShowUnidentifiedDeliveryIndicators(transaction: transaction)
|
||||
|
||||
guard displayUDIndicator && shouldShowUD else { return label }
|
||||
guard displayUDIndicator && shouldShowUD else {
|
||||
return ContactCellAccessoryView(accessoryView: label, size: labelSize)
|
||||
}
|
||||
|
||||
let imageView = UIImageView()
|
||||
let imageView = CVImageView()
|
||||
imageView.setTemplateImageName(Theme.iconName(.sealedSenderIndicator), tintColor: Theme.ternaryTextColor)
|
||||
let imageSize = CGSize.square(20)
|
||||
|
||||
let hStack = UIStackView(arrangedSubviews: [imageView, label])
|
||||
hStack.axis = .horizontal
|
||||
hStack.spacing = 8
|
||||
|
||||
return hStack
|
||||
let hStack = ManualStackView(name: "hStack")
|
||||
let hStackConfig = CVStackViewConfig(axis: .horizontal,
|
||||
alignment: .center,
|
||||
spacing: 8,
|
||||
layoutMargins: .zero)
|
||||
let hStackMeasurement = hStack.configure(config: hStackConfig,
|
||||
subviews: [imageView, label],
|
||||
subviewInfos: [
|
||||
imageSize.asManualSubviewInfo(hasFixedSize: true),
|
||||
labelSize.asManualSubviewInfo
|
||||
])
|
||||
let hStackSize = hStackMeasurement.measuredSize
|
||||
return ContactCellAccessoryView(accessoryView: hStack, size: hStackSize)
|
||||
}
|
||||
|
||||
private func buildValueLabel(name: String, value: String) -> UILabel {
|
||||
|
||||
@ -530,7 +530,7 @@ extension BaseGroupMemberViewController: RecipientPickerDelegate {
|
||||
|
||||
func recipientPicker(_ recipientPickerViewController: RecipientPickerViewController,
|
||||
accessoryViewForRecipient recipient: PickedRecipient,
|
||||
transaction: SDSAnyReadTransaction) -> UIView? {
|
||||
transaction: SDSAnyReadTransaction) -> ContactCellAccessoryView? {
|
||||
guard let address = recipient.address else {
|
||||
owsFailDebug("Missing address.")
|
||||
return nil
|
||||
@ -549,7 +549,7 @@ extension BaseGroupMemberViewController: RecipientPickerDelegate {
|
||||
let isPreExistingMember = groupMemberViewDelegate.groupMemberViewIsPreExistingMember(recipient,
|
||||
transaction: transaction)
|
||||
|
||||
let imageView = UIImageView()
|
||||
let imageView = CVImageView()
|
||||
if isPreExistingMember {
|
||||
imageView.setTemplateImageName("check-circle-solid-24", tintColor: Theme.washColor)
|
||||
} else if isCurrentMember {
|
||||
@ -560,17 +560,13 @@ extension BaseGroupMemberViewController: RecipientPickerDelegate {
|
||||
} else {
|
||||
imageView.setTemplateImageName("empty-circle-outline-24", tintColor: .ows_gray25)
|
||||
}
|
||||
return imageView
|
||||
return ContactCellAccessoryView(accessoryView: imageView, size: .square(24))
|
||||
}
|
||||
|
||||
func recipientPicker(_ recipientPickerViewController: RecipientPickerViewController,
|
||||
attributedSubtitleForRecipient recipient: PickedRecipient,
|
||||
transaction: SDSAnyReadTransaction) -> NSAttributedString? {
|
||||
|
||||
guard let groupMemberViewDelegate = groupMemberViewDelegate else {
|
||||
owsFailDebug("Missing delegate.")
|
||||
return nil
|
||||
}
|
||||
guard let address = recipient.address else {
|
||||
owsFailDebug("Recipient missing address.")
|
||||
return nil
|
||||
|
||||
@ -40,7 +40,7 @@ protocol RecipientPickerDelegate: AnyObject {
|
||||
@objc
|
||||
optional func recipientPicker(_ recipientPickerViewController: RecipientPickerViewController,
|
||||
accessoryViewForRecipient recipient: PickedRecipient,
|
||||
transaction: SDSAnyReadTransaction) -> UIView?
|
||||
transaction: SDSAnyReadTransaction) -> ContactCellAccessoryView?
|
||||
|
||||
@objc
|
||||
optional func recipientPicker(_ recipientPickerViewController: RecipientPickerViewController,
|
||||
@ -172,7 +172,7 @@ extension RecipientPickerViewController {
|
||||
|
||||
func item(forRecipient recipient: PickedRecipient) -> OWSTableItem {
|
||||
let tableView = self.tableView
|
||||
|
||||
|
||||
switch recipient.identifier {
|
||||
case .address(let address):
|
||||
return OWSTableItem(
|
||||
|
||||
@ -510,7 +510,10 @@ private class SafetyNumberCell: ContactTableViewCell {
|
||||
|
||||
configuration.forceDarkAppearance = (theme == .translucentDark)
|
||||
|
||||
configuration.accessoryView = button
|
||||
let buttonSize = button.sizeThatFits(.square(.greatestFiniteMagnitude))
|
||||
let buttonWrapper = ManualLayoutView.wrapSubviewUsingIOSAutoLayout(button)
|
||||
configuration.accessoryView = ContactCellAccessoryView(accessoryView: buttonWrapper,
|
||||
size: buttonSize)
|
||||
|
||||
if let verificationState = item.verificationState, verificationState == .noLongerVerified {
|
||||
let previouslyVerified = NSMutableAttributedString()
|
||||
|
||||
@ -181,11 +181,10 @@ public class GroupMemberRequestsAndInvitesViewController: OWSTableViewController
|
||||
contents.addSection(section)
|
||||
}
|
||||
|
||||
private func buildMemberRequestButtons(address: SignalServiceAddress) -> UIView {
|
||||
private func buildMemberRequestButtons(address: SignalServiceAddress) -> ContactCellAccessoryView {
|
||||
let buttonHeight: CGFloat = 28
|
||||
|
||||
let denyButton = OWSButton()
|
||||
denyButton.autoSetDimensions(to: CGSize(square: buttonHeight))
|
||||
denyButton.layer.cornerRadius = buttonHeight / 2
|
||||
denyButton.clipsToBounds = true
|
||||
denyButton.setBackgroundImage(UIImage(color: Theme.secondaryBackgroundColor), for: .normal)
|
||||
@ -196,7 +195,6 @@ public class GroupMemberRequestsAndInvitesViewController: OWSTableViewController
|
||||
}
|
||||
|
||||
let approveButton = OWSButton()
|
||||
approveButton.autoSetDimensions(to: CGSize(square: buttonHeight))
|
||||
approveButton.layer.cornerRadius = buttonHeight / 2
|
||||
approveButton.clipsToBounds = true
|
||||
approveButton.setBackgroundImage(UIImage(color: Theme.secondaryBackgroundColor), for: .normal)
|
||||
@ -206,10 +204,25 @@ public class GroupMemberRequestsAndInvitesViewController: OWSTableViewController
|
||||
self?.approveMemberRequest(address: address)
|
||||
}
|
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [denyButton, approveButton])
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = 16
|
||||
return stackView
|
||||
let denyWrapper = ManualLayoutView.wrapSubviewUsingIOSAutoLayout(denyButton)
|
||||
let approveWrapper = ManualLayoutView.wrapSubviewUsingIOSAutoLayout(approveButton)
|
||||
|
||||
let denyButtonSize = CGSize.square(buttonHeight)
|
||||
let approveButtonSize = CGSize.square(buttonHeight)
|
||||
|
||||
let stackView = ManualStackView(name: "stackView")
|
||||
let stackConfig = CVStackViewConfig(axis: .horizontal,
|
||||
alignment: .center,
|
||||
spacing: 16,
|
||||
layoutMargins: .zero)
|
||||
let stackMeasurement = stackView.configure(config: stackConfig,
|
||||
subviews: [denyWrapper, approveWrapper],
|
||||
subviewInfos: [
|
||||
denyButtonSize.asManualSubviewInfo,
|
||||
approveButtonSize.asManualSubviewInfo
|
||||
])
|
||||
let stackSize = stackMeasurement.measuredSize
|
||||
return ContactCellAccessoryView(accessoryView: stackView, size: stackSize)
|
||||
}
|
||||
|
||||
private func approveMemberRequest(address: SignalServiceAddress) {
|
||||
|
||||
@ -62,9 +62,9 @@ class ReplaceAdminViewController: OWSTableViewController2 {
|
||||
localUserDisplayMode: .asUser,
|
||||
transaction: transaction)
|
||||
|
||||
let imageView = UIImageView()
|
||||
let imageView = CVImageView()
|
||||
imageView.setTemplateImageName("empty-circle-outline-24", tintColor: .ows_gray25)
|
||||
configuration.accessoryView = imageView
|
||||
configuration.accessoryView = ContactCellAccessoryView(accessoryView: imageView, size: .square(24))
|
||||
|
||||
cell.configure(configuration: configuration, transaction: transaction)
|
||||
}
|
||||
|
||||
@ -687,21 +687,33 @@ private class ConversationPickerCell: ContactTableViewCell {
|
||||
return container
|
||||
}()
|
||||
|
||||
func buildAccessoryView(disappearingMessagesConfig: OWSDisappearingMessagesConfiguration?) -> UIView {
|
||||
func buildAccessoryView(disappearingMessagesConfig: OWSDisappearingMessagesConfiguration?) -> ContactCellAccessoryView {
|
||||
|
||||
let selectionWrapper = ManualLayoutView.wrapSubviewUsingIOSAutoLayout(selectionView)
|
||||
|
||||
guard let disappearingMessagesConfig = disappearingMessagesConfig,
|
||||
disappearingMessagesConfig.isEnabled else {
|
||||
return selectionView
|
||||
return ContactCellAccessoryView(accessoryView: selectionWrapper,
|
||||
size: selectionBadgeSize)
|
||||
}
|
||||
|
||||
let timerView = DisappearingTimerConfigurationView(durationSeconds: disappearingMessagesConfig.durationSeconds)
|
||||
timerView.tintColor = Theme.middleGrayColor
|
||||
timerView.autoSetDimensions(to: CGSize(square: 44))
|
||||
timerView.setCompressionResistanceHigh()
|
||||
let timerSize = CGSize(square: 44)
|
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [timerView, selectionView])
|
||||
stackView.alignment = .center
|
||||
stackView.setCompressionResistanceHigh()
|
||||
return stackView
|
||||
let stackView = ManualStackView(name: "stackView")
|
||||
let stackConfig = OWSStackView.Config(axis: .horizontal,
|
||||
alignment: .center,
|
||||
spacing: 0,
|
||||
layoutMargins: .zero)
|
||||
let stackMeasurement = stackView.configure(config: stackConfig,
|
||||
subviews: [timerView, selectionWrapper],
|
||||
subviewInfos: [
|
||||
timerSize.asManualSubviewInfo,
|
||||
selectionBadgeSize.asManualSubviewInfo
|
||||
])
|
||||
let stackSize = stackMeasurement.measuredSize
|
||||
return ContactCellAccessoryView(accessoryView: stackView, size: stackSize)
|
||||
}
|
||||
|
||||
lazy var unselectedBadgeView: UIView = {
|
||||
|
||||
@ -4,6 +4,19 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
public class ContactCellAccessoryView: NSObject {
|
||||
let accessoryView: UIView
|
||||
let size: CGSize
|
||||
|
||||
public init(accessoryView: UIView, size: CGSize) {
|
||||
self.accessoryView = accessoryView
|
||||
self.size = size
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
public class ContactCellConfiguration: NSObject {
|
||||
public let content: ConversationContent
|
||||
@ -24,7 +37,7 @@ public class ContactCellConfiguration: NSObject {
|
||||
public var customName: String?
|
||||
|
||||
@objc
|
||||
public var accessoryView: UIView?
|
||||
public var accessoryView: ContactCellAccessoryView?
|
||||
|
||||
@objc
|
||||
public var attributedSubtitle: NSAttributedString?
|
||||
@ -184,24 +197,22 @@ public class ContactCellView: ManualStackView {
|
||||
alignment: .leading,
|
||||
spacing: 0,
|
||||
layoutMargins: .zero)
|
||||
let textStackMeasurement = ManualStackView.measure(config: textStackConfig,
|
||||
subviewInfos: textStackSubviewInfos)
|
||||
textStack.configure(config: textStackConfig,
|
||||
measurement: textStackMeasurement,
|
||||
subviews: textStackSubviews)
|
||||
let textStackMeasurement = textStack.configure(config: textStackConfig,
|
||||
subviews: textStackSubviews,
|
||||
subviewInfos: textStackSubviewInfos)
|
||||
rootStackSubviews.append(textStack)
|
||||
rootStackSubviewInfos.append(textStackMeasurement.measuredSize.asManualSubviewInfo)
|
||||
}
|
||||
|
||||
if let accessoryMessage = configuration.accessoryMessage {
|
||||
accessoryLabel.text = accessoryMessage
|
||||
owsAssertDebug(configuration.accessoryView == nil)
|
||||
configuration.accessoryView = accessoryLabel
|
||||
let labelSize = accessoryLabel.sizeThatFits(.square(.greatestFiniteMagnitude))
|
||||
configuration.accessoryView = ContactCellAccessoryView(accessoryView: accessoryLabel,
|
||||
size: labelSize)
|
||||
}
|
||||
if let accessoryView = configuration.accessoryView {
|
||||
rootStackSubviews.append(accessoryView)
|
||||
let accessorySize = accessoryView.sizeThatFits(.square(.greatestFiniteMagnitude))
|
||||
rootStackSubviewInfos.append(accessorySize.asManualSubviewInfo(hasFixedSize: true))
|
||||
rootStackSubviews.append(accessoryView.accessoryView)
|
||||
rootStackSubviewInfos.append(accessoryView.size.asManualSubviewInfo(hasFixedSize: true))
|
||||
}
|
||||
|
||||
let rootStackConfig = ManualStackView.Config(axis: .horizontal,
|
||||
|
||||
@ -303,14 +303,17 @@ public class CVText {
|
||||
}
|
||||
|
||||
let result: CGSize
|
||||
switch mode {
|
||||
case .layoutManager:
|
||||
result = measureLabelUsingLayoutManager(config: config, maxWidth: maxWidth)
|
||||
case .view:
|
||||
result = measureLabelUsingView(config: config, maxWidth: maxWidth)
|
||||
if config.text.stringValue.isEmpty {
|
||||
result = .zero
|
||||
} else {
|
||||
switch mode {
|
||||
case .layoutManager:
|
||||
result = measureLabelUsingLayoutManager(config: config, maxWidth: maxWidth)
|
||||
case .view:
|
||||
result = measureLabelUsingView(config: config, maxWidth: maxWidth)
|
||||
}
|
||||
owsAssertDebug(result.isNonEmpty)
|
||||
}
|
||||
owsAssertDebug(result.width > 0)
|
||||
owsAssertDebug(result.height > 0)
|
||||
|
||||
if cacheMeasurements {
|
||||
labelCache.set(key: cacheKey, value: result.ceil)
|
||||
|
||||
@ -703,3 +703,18 @@ public struct ManualStackMeasurement: Equatable {
|
||||
ManualStackMeasurement(measuredSize: measuredSize, subviewInfos: [])
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public extension ManualStackView {
|
||||
@discardableResult
|
||||
func configure(
|
||||
config: Config,
|
||||
subviews: [UIView],
|
||||
subviewInfos: [ManualStackSubviewInfo]
|
||||
) -> Measurement {
|
||||
let measurement = ManualStackView.measure(config: config, subviewInfos: subviewInfos)
|
||||
self.configure(config: config, measurement: measurement, subviews: subviews)
|
||||
return measurement
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user