From 50c49baca8c5cee8c53a7f2120e2c0c7c6edc48b Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 4 May 2018 17:13:42 -0400 Subject: [PATCH] Update design of 'approve contact share' view. --- Signal.xcodeproj/project.pbxproj | 14 +- .../ContactViewController.swift | 180 ++---------------- .../src/ViewControllers/ContactsPicker.swift | 2 + SignalMessaging/Views/TappableStackView.swift | 33 ++++ SignalMessaging/Views/TappableView.swift | 33 ++++ .../ApproveContactShareViewController.swift | 163 +++------------- .../attachments/ContactFieldView.swift | 114 ++++++++++- SignalMessaging/categories/UIView+OWS.m | 5 +- 8 files changed, 243 insertions(+), 301 deletions(-) create mode 100644 SignalMessaging/Views/TappableStackView.swift create mode 100644 SignalMessaging/Views/TappableView.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 6941e6a98..ff258445f 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -164,6 +164,8 @@ 347850711FDAEB17007B8332 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 3478506F1FDAEB16007B8332 /* OWSUserProfile.m */; }; 347850721FDAEB17007B8332 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 347850701FDAEB16007B8332 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; 348BB254209CD4B80047AEC2 /* ContactFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348BB253209CD4B80047AEC2 /* ContactFieldView.swift */; }; + 348BB25A209CF8E50047AEC2 /* TappableStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348BB258209CF8E40047AEC2 /* TappableStackView.swift */; }; + 348BB25B209CF8E50047AEC2 /* TappableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348BB259209CF8E50047AEC2 /* TappableView.swift */; }; 3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496744C2076768700080B5F /* OWSMessageBubbleView.m */; }; 3496744F2076ACD000080B5F /* LongTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496744E2076ACCE00080B5F /* LongTextViewController.swift */; }; 34A55F3720485465002CC6DE /* OWS2FARegistrationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */; }; @@ -762,6 +764,8 @@ 3478506F1FDAEB16007B8332 /* OWSUserProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSUserProfile.m; sourceTree = ""; }; 347850701FDAEB16007B8332 /* OWSUserProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSUserProfile.h; sourceTree = ""; }; 348BB253209CD4B80047AEC2 /* ContactFieldView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContactFieldView.swift; path = SignalMessaging/attachments/ContactFieldView.swift; sourceTree = SOURCE_ROOT; }; + 348BB258209CF8E40047AEC2 /* TappableStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TappableStackView.swift; path = SignalMessaging/Views/TappableStackView.swift; sourceTree = SOURCE_ROOT; }; + 348BB259209CF8E50047AEC2 /* TappableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TappableView.swift; path = SignalMessaging/Views/TappableView.swift; sourceTree = SOURCE_ROOT; }; 348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceSleepManager.swift; sourceTree = ""; }; 3495BC911F1426B800B478F5 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = translations/ar.lproj/Localizable.strings; sourceTree = ""; }; 3496744B2076768600080B5F /* OWSMessageBubbleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageBubbleView.h; sourceTree = ""; }; @@ -1586,15 +1590,17 @@ isa = PBXGroup; children = ( 45F3AEB51DFDE7900080CE33 /* AvatarImageView.swift */, - 76EB052E18170B33006006FC /* ContactTableViewCell.h */, - 76EB052F18170B33006006FC /* ContactTableViewCell.m */, + 346129D11FD2085A00532771 /* CommonStrings.swift */, 340CB2221EAC155C0001CAA1 /* ContactsViewHelper.h */, 340CB2231EAC155C0001CAA1 /* ContactsViewHelper.m */, - 346129D11FD2085A00532771 /* CommonStrings.swift */, + 76EB052E18170B33006006FC /* ContactTableViewCell.h */, + 76EB052F18170B33006006FC /* ContactTableViewCell.m */, 45BE4EA12012AD2000935E59 /* DisappearingTimerConfigurationView.swift */, 451573952061B49500803601 /* GradientView.swift */, 346129CF1FD207F200532771 /* OWSAlerts.swift */, 454A965E1FD60EA2008D2A0E /* OWSFlatButton.swift */, + 348BB258209CF8E40047AEC2 /* TappableStackView.swift */, + 348BB259209CF8E50047AEC2 /* TappableView.swift */, 3400C7971EAFB772008A8584 /* ThreadViewHelper.h */, 3400C7981EAFB772008A8584 /* ThreadViewHelper.m */, ); @@ -3083,6 +3089,8 @@ 451F8A461FD715BA005CB9DA /* OWSGroupAvatarBuilder.m in Sources */, 347850591FD9972E007B8332 /* SwiftSingletons.swift in Sources */, 344F248720069ECB00CFB4F4 /* ModalActivityIndicatorViewController.swift in Sources */, + 348BB25B209CF8E50047AEC2 /* TappableView.swift in Sources */, + 348BB25A209CF8E50047AEC2 /* TappableStackView.swift in Sources */, 346129961FD1E30000532771 /* OWSDatabaseMigration.m in Sources */, 344D6CEC20069E070042AF96 /* NewNonContactConversationViewController.m in Sources */, 346129FB1FD5F31400532771 /* OWS101ExistingUsersBlockOnIdentityChange.m in Sources */, diff --git a/Signal/src/ViewControllers/ContactViewController.swift b/Signal/src/ViewControllers/ContactViewController.swift index 9914c33e5..425a58b0e 100644 --- a/Signal/src/ViewControllers/ContactViewController.swift +++ b/Signal/src/ViewControllers/ContactViewController.swift @@ -9,36 +9,6 @@ import Reachability import ContactsUI import MessageUI -class TappableView: UIView { - let actionBlock : (() -> Void) - - // MARK: - Initializers - - @available(*, unavailable, message: "use other constructor instead.") - required init?(coder aDecoder: NSCoder) { - fatalError("Unimplemented") - } - - required init(actionBlock : @escaping () -> Void) { - self.actionBlock = actionBlock - super.init(frame: CGRect.zero) - - self.isUserInteractionEnabled = true - self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped))) - } - - func wasTapped(sender: UIGestureRecognizer) { - Logger.info("\(logTag) \(#function)") - - guard sender.state == .recognized else { - return - } - actionBlock() - } -} - -// MARK: - - class ContactViewController: OWSViewController, CNContactViewControllerDelegate { enum ContactViewMode { @@ -372,37 +342,35 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate // } for phoneNumber in contactShare.phoneNumbers { - let formattedPhoneNumber = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber.phoneNumber) - - rows.append(createSimpleFieldRow(name: phoneNumber.localizedLabel(), - value: formattedPhoneNumber, - actionBlock: { - guard let url = NSURL(string: "tel:\(phoneNumber.phoneNumber)") else { - owsFail("\(ContactViewController.logTag) could not open phone number.") - return - } - UIApplication.shared.openURL(url as URL) + rows.append(ContactFieldView.contactFieldView(forPhoneNumber: phoneNumber, + layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin), + actionBlock: { + guard let url = NSURL(string: "tel:\(phoneNumber.phoneNumber)") else { + owsFail("\(ContactViewController.logTag) could not open phone number.") + return + } + UIApplication.shared.openURL(url as URL) })) } for email in contactShare.emails { - rows.append(createSimpleFieldRow(name: email.localizedLabel(), - value: email.email, - actionBlock: { - guard let url = NSURL(string: "mailto:\(email.email)") else { - owsFail("\(ContactViewController.logTag) could not open email.") - return - } - UIApplication.shared.openURL(url as URL) + rows.append(ContactFieldView.contactFieldView(forEmail: email, + layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin), + actionBlock: { + guard let url = NSURL(string: "mailto:\(email.email)") else { + owsFail("\(ContactViewController.logTag) could not open email.") + return + } + UIApplication.shared.openURL(url as URL) })) } for address in contactShare.addresses { - rows.append(createAddressFieldRow(name: address.localizedLabel(), - address: address, - actionBlock: { [weak self] _ in - guard let strongSelf = self else { return } - strongSelf.didPressAddress(address: address) + rows.append(ContactFieldView.contactFieldView(forAddress: address, + layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin), + actionBlock: { [weak self] _ in + guard let strongSelf = self else { return } + strongSelf.didPressAddress(address: address) })) } @@ -432,112 +400,6 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate return row } - private func createSimpleFieldRow(name: String, value: String?, actionBlock : @escaping () -> Void) -> UIView { - let row = TappableView(actionBlock: actionBlock) - row.layoutMargins.left = 0 - row.layoutMargins.right = 0 - - let stackView = UIStackView() - stackView.axis = .vertical - stackView.alignment = .leading - stackView.spacing = 3 - row.addSubview(stackView) - stackView.autoPinTopToSuperviewMargin() - stackView.autoPinBottomToSuperviewMargin() - stackView.autoPinLeadingToSuperviewMargin(withInset: hMargin) - stackView.autoPinTrailingToSuperviewMargin(withInset: hMargin) - - let nameLabel = UILabel() - nameLabel.text = name - nameLabel.font = UIFont.ows_dynamicTypeSubheadline - nameLabel.textColor = UIColor.black - nameLabel.lineBreakMode = .byTruncatingTail - stackView.addArrangedSubview(nameLabel) - - let valueLabel = UILabel() - valueLabel.text = value - valueLabel.font = UIFont.ows_dynamicTypeBody - valueLabel.textColor = UIColor.ows_materialBlue - valueLabel.lineBreakMode = .byTruncatingTail - stackView.addArrangedSubview(valueLabel) - - return row - } - - private func createAddressFieldRow(name: String, address: OWSContactAddress, actionBlock : @escaping () -> Void) -> UIView { - let row = TappableView(actionBlock: actionBlock) - row.layoutMargins.left = 0 - row.layoutMargins.right = 0 - - let stackView = UIStackView() - stackView.axis = .vertical - stackView.alignment = .leading - stackView.spacing = 3 - stackView.layoutMargins = .zero - row.addSubview(stackView) - stackView.autoPinTopToSuperviewMargin() - stackView.autoPinBottomToSuperviewMargin() - stackView.autoPinLeadingToSuperviewMargin(withInset: hMargin) - stackView.autoPinTrailingToSuperviewMargin(withInset: hMargin) - - let nameLabel = UILabel() - nameLabel.text = name - nameLabel.font = UIFont.ows_dynamicTypeSubheadline - nameLabel.textColor = UIColor.black - nameLabel.lineBreakMode = .byTruncatingTail - stackView.addArrangedSubview(nameLabel) - - let tryToAddNameValue: ((String, String?) -> Void) = { (propertyName, propertyValue) in - guard let propertyValue = propertyValue else { - return - } - guard propertyValue.count > 0 else { - return - } - - let row = UIStackView() - row.axis = .horizontal - row.alignment = .leading - row.spacing = 10 - row.layoutMargins = .zero - - let nameLabel = UILabel() - nameLabel.text = propertyName - nameLabel.font = UIFont.ows_dynamicTypeBody - nameLabel.textColor = UIColor.black - nameLabel.lineBreakMode = .byTruncatingTail - row.addArrangedSubview(nameLabel) - nameLabel.setContentHuggingHigh() - nameLabel.setCompressionResistanceHigh() - - let valueLabel = UILabel() - valueLabel.text = propertyValue - valueLabel.font = UIFont.ows_dynamicTypeBody - valueLabel.textColor = UIColor.ows_materialBlue - valueLabel.lineBreakMode = .byTruncatingTail - row.addArrangedSubview(valueLabel) - - stackView.addArrangedSubview(row) - } - - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_STREET", comment: "Label for the 'street' field of a contact's address."), - address.street) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POBOX", comment: "Label for the 'pobox' field of a contact's address."), - address.pobox) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_NEIGHBORHOOD", comment: "Label for the 'neighborhood' field of a contact's address."), - address.neighborhood) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_CITY", comment: "Label for the 'city' field of a contact's address."), - address.city) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_REGION", comment: "Label for the 'region' field of a contact's address."), - address.region) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POSTCODE", comment: "Label for the 'postcode' field of a contact's address."), - address.postcode) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_COUNTRY", comment: "Label for the 'country' field of a contact's address."), - address.country) - - return row - } - // TODO: Use real assets. private func createCircleActionButton(text: String, actionBlock : @escaping () -> Void) -> UIView { let buttonSize = CGFloat(50) diff --git a/Signal/src/ViewControllers/ContactsPicker.swift b/Signal/src/ViewControllers/ContactsPicker.swift index 24c49bbd2..255c9c9bb 100644 --- a/Signal/src/ViewControllers/ContactsPicker.swift +++ b/Signal/src/ViewControllers/ContactsPicker.swift @@ -244,6 +244,8 @@ public class ContactsPicker: OWSViewController, UITableViewDelegate, UITableView } open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + Logger.verbose("\(logTag) \(#function)") + let cell = tableView.cellForRow(at: indexPath) as! ContactCell let selectedContact = cell.contact! diff --git a/SignalMessaging/Views/TappableStackView.swift b/SignalMessaging/Views/TappableStackView.swift new file mode 100644 index 000000000..8fa9e212e --- /dev/null +++ b/SignalMessaging/Views/TappableStackView.swift @@ -0,0 +1,33 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +public class TappableStackView: UIStackView { + let actionBlock : (() -> Void) + + // MARK: - Initializers + + @available(*, unavailable, message: "use other constructor instead.") + public required init(coder aDecoder: NSCoder) { + fatalError("Unimplemented") + } + + public required init(actionBlock : @escaping () -> Void) { + self.actionBlock = actionBlock + super.init(frame: CGRect.zero) + + self.isUserInteractionEnabled = true + self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped))) + } + + func wasTapped(sender: UIGestureRecognizer) { + Logger.info("\(logTag) \(#function)") + + guard sender.state == .recognized else { + return + } + actionBlock() + } +} diff --git a/SignalMessaging/Views/TappableView.swift b/SignalMessaging/Views/TappableView.swift new file mode 100644 index 000000000..e440e6554 --- /dev/null +++ b/SignalMessaging/Views/TappableView.swift @@ -0,0 +1,33 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +public class TappableView: UIView { + let actionBlock : (() -> Void) + + // MARK: - Initializers + + @available(*, unavailable, message: "use other constructor instead.") + public required init?(coder aDecoder: NSCoder) { + fatalError("Unimplemented") + } + + public required init(actionBlock : @escaping () -> Void) { + self.actionBlock = actionBlock + super.init(frame: CGRect.zero) + + self.isUserInteractionEnabled = true + self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped))) + } + + func wasTapped(sender: UIGestureRecognizer) { + Logger.info("\(logTag) \(#function)") + + guard sender.state == .recognized else { + return + } + actionBlock() + } +} diff --git a/SignalMessaging/attachments/ApproveContactShareViewController.swift b/SignalMessaging/attachments/ApproveContactShareViewController.swift index 597c195cc..141466285 100644 --- a/SignalMessaging/attachments/ApproveContactShareViewController.swift +++ b/SignalMessaging/attachments/ApproveContactShareViewController.swift @@ -97,7 +97,7 @@ class ContactShareAddress: ContactShareFieldBase { // MARK: - -class ContactShareFieldView: UIView { +class ContactShareFieldView: UIStackView { let field: ContactShareField @@ -108,7 +108,7 @@ class ContactShareFieldView: UIView { // MARK: - Initializers @available(*, unavailable, message: "use init(call:) constructor instead.") - required init?(coder aDecoder: NSCoder) { + required init(coder aDecoder: NSCoder) { fatalError("Unimplemented") } @@ -125,11 +125,14 @@ class ContactShareFieldView: UIView { } let hSpacing = CGFloat(10) - let hMargin = CGFloat(0) + let hMargin = CGFloat(16) func createContents() { - self.layoutMargins.left = 0 - self.layoutMargins.right = 0 + self.axis = .horizontal + self.spacing = hSpacing + self.alignment = .center + self.layoutMargins = UIEdgeInsets(top: 0, left: hMargin, bottom: 0, right: hMargin) + self.isLayoutMarginsRelativeArrangement = true let checkbox = UIButton(type: .custom) self.checkbox = checkbox @@ -142,28 +145,12 @@ class ContactShareFieldView: UIView { checkbox.isSelected = field.isIncluded() // Disable the checkbox; the entire row is hot. checkbox.isUserInteractionEnabled = false - addSubview(checkbox) - checkbox.autoPinEdge(toSuperviewEdge: .leading, withInset: hMargin) - checkbox.autoVCenterInSuperview() + self.addArrangedSubview(checkbox) checkbox.setCompressionResistanceHigh() checkbox.setContentHuggingHigh() - let nameLabel = UILabel() - nameLabel.text = field.localizedLabel() - nameLabel.font = UIFont.ows_dynamicTypeCaption1 - nameLabel.textColor = UIColor.black - nameLabel.lineBreakMode = .byTruncatingTail - addSubview(nameLabel) - nameLabel.autoPinTopToSuperviewMargin() - nameLabel.autoPinLeading(toTrailingEdgeOf: checkbox, offset: hSpacing) - nameLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin) - let previewView = previewViewBlock() - addSubview(previewView) - previewView.autoPinEdge(.top, to: .bottom, of: nameLabel, withOffset: 3) - previewView.autoPinBottomToSuperviewMargin() - previewView.autoPinLeading(toTrailingEdgeOf: checkbox, offset: hSpacing) - previewView.autoPinTrailingToSuperviewMargin(withInset: hMargin) + self.addArrangedSubview(previewView) } func wasTapped(sender: UIGestureRecognizer) { @@ -215,27 +202,26 @@ public class ApproveContactShareViewController: OWSViewController, EditContactSh // TODO: Avatar + let previewInsets = UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0) + for phoneNumber in contactShare.phoneNumbers { let field = ContactSharePhoneNumber(phoneNumber) - let fieldView = ContactShareFieldView(field: field, previewViewBlock: { [weak self] _ in - guard let strongSelf = self else { return UIView() } - return strongSelf.previewView(forPhoneNumber: phoneNumber) + let fieldView = ContactShareFieldView(field: field, previewViewBlock: { + return ContactFieldView.contactFieldView(forPhoneNumber: phoneNumber, layoutMargins: previewInsets, actionBlock: nil) }) fieldViews.append(fieldView) } for email in contactShare.emails { let field = ContactShareEmail(email) - let fieldView = ContactShareFieldView(field: field, previewViewBlock: { [weak self] _ in - guard let strongSelf = self else { return UIView() } - return strongSelf.previewView(forEmail: email) + let fieldView = ContactShareFieldView(field: field, previewViewBlock: { + return ContactFieldView.contactFieldView(forEmail: email, layoutMargins: previewInsets, actionBlock: nil) }) fieldViews.append(fieldView) } for address in contactShare.addresses { let field = ContactShareAddress(address) - let fieldView = ContactShareFieldView(field: field, previewViewBlock: { [weak self] _ in - guard let strongSelf = self else { return UIView() } - return strongSelf.previewView(forAddress: address) + let fieldView = ContactShareFieldView(field: field, previewViewBlock: { + return ContactFieldView.contactFieldView(forAddress: address, layoutMargins: previewInsets, actionBlock: nil) }) fieldViews.append(fieldView) } @@ -347,126 +333,33 @@ public class ApproveContactShareViewController: OWSViewController, EditContactSh func createNameRow() -> UIView { let nameVMargin = CGFloat(16) - let row = UIView() - row.layoutMargins = UIEdgeInsets(top: nameVMargin, left: 0, bottom: nameVMargin, right: 0) + let stackView = TappableStackView(actionBlock: { [weak self] _ in + guard let strongSelf = self else { return } + strongSelf.didPressEditName() + }) - let stackView = UIStackView() stackView.axis = .horizontal stackView.alignment = .center - stackView.layoutMargins = .zero + stackView.layoutMargins = UIEdgeInsets(top: nameVMargin, left: hMargin, bottom: nameVMargin, right: hMargin) stackView.spacing = 10 - row.addSubview(stackView) - stackView.autoPinEdgesToSuperviewMargins() + stackView.isLayoutMarginsRelativeArrangement = true let nameLabel = UILabel() self.nameLabel = nameLabel nameLabel.text = contactShare.displayName - nameLabel.font = UIFont.ows_dynamicTypeBody - nameLabel.textColor = UIColor.ows_materialBlue + nameLabel.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight() + nameLabel.textColor = UIColor.black nameLabel.lineBreakMode = .byTruncatingTail stackView.addArrangedSubview(nameLabel) let editNameLabel = UILabel() editNameLabel.text = NSLocalizedString("CONTACT_EDIT_NAME_BUTTON", comment: "Label for the 'edit name' button in the contact share approval view.") - editNameLabel.font = UIFont.ows_dynamicTypeCaption1 - editNameLabel.textColor = UIColor.black + editNameLabel.font = UIFont.ows_dynamicTypeBody + editNameLabel.textColor = UIColor.ows_materialBlue stackView.addArrangedSubview(editNameLabel) editNameLabel.setContentHuggingHigh() editNameLabel.setCompressionResistanceHigh() - // Icon - let iconName = (self.view.isRTL() ? "system_disclosure_indicator_rtl" : "system_disclosure_indicator") - guard let iconImage = UIImage(named: iconName) else { - owsFail("\(logTag) missing icon.") - return row - } - let iconView = UIImageView(image: iconImage.withRenderingMode(.alwaysTemplate)) - iconView.contentMode = .scaleAspectFit - iconView.tintColor = UIColor.black.withAlphaComponent(0.6) - stackView.addArrangedSubview(iconView) - iconView.setContentHuggingHigh() - iconView.setCompressionResistanceHigh() - - row.isUserInteractionEnabled = true - row.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didPressEditName))) - - return row - } - - func previewView(forPhoneNumber phoneNumber: OWSContactPhoneNumber) -> UIView { - let label = UILabel() - label.text = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber.phoneNumber) - label.font = UIFont.ows_dynamicTypeCaption1 - label.textColor = UIColor.ows_materialBlue - label.lineBreakMode = .byTruncatingTail - return label - } - - func previewView(forEmail email: OWSContactEmail) -> UIView { - let label = UILabel() - label.text = email.email - label.font = UIFont.ows_dynamicTypeCaption1 - label.textColor = UIColor.ows_materialBlue - label.lineBreakMode = .byTruncatingTail - return label - } - - func previewView(forAddress address: OWSContactAddress) -> UIView { - - let stackView = UIStackView() - stackView.axis = .vertical - stackView.alignment = .leading - stackView.spacing = 0 - stackView.layoutMargins = .zero - - let tryToAddNameValue: ((String, String?) -> Void) = { (name, value) in - guard let value = value else { - return - } - guard value.count > 0 else { - return - } - let row = UIView.container() - - let nameLabel = UILabel() - nameLabel.text = name - nameLabel.font = UIFont.ows_dynamicTypeCaption1 - nameLabel.textColor = UIColor.black - nameLabel.lineBreakMode = .byTruncatingTail - row.addSubview(nameLabel) - nameLabel.autoPinLeadingToSuperviewMargin() - nameLabel.autoPinHeightToSuperview() - nameLabel.setContentHuggingHigh() - nameLabel.setCompressionResistanceHigh() - - let valueLabel = UILabel() - valueLabel.text = value - valueLabel.font = UIFont.ows_dynamicTypeCaption1 - valueLabel.textColor = UIColor.ows_materialBlue - valueLabel.lineBreakMode = .byTruncatingTail - row.addSubview(valueLabel) - valueLabel.autoPinLeading(toTrailingEdgeOf: nameLabel, offset: 10) - valueLabel.autoPinTrailingToSuperviewMargin() - valueLabel.autoPinHeightToSuperview() - - stackView.addArrangedSubview(row) - } - - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_STREET", comment: "Label for the 'street' field of a contact's address."), - address.street) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POBOX", comment: "Label for the 'pobox' field of a contact's address."), - address.pobox) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_NEIGHBORHOOD", comment: "Label for the 'neighborhood' field of a contact's address."), - address.neighborhood) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_CITY", comment: "Label for the 'city' field of a contact's address."), - address.city) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_REGION", comment: "Label for the 'region' field of a contact's address."), - address.region) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POSTCODE", comment: "Label for the 'postcode' field of a contact's address."), - address.postcode) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_COUNTRY", comment: "Label for the 'country' field of a contact's address."), - address.country) - return stackView } diff --git a/SignalMessaging/attachments/ContactFieldView.swift b/SignalMessaging/attachments/ContactFieldView.swift index ac4ef71f1..0a64b0f63 100644 --- a/SignalMessaging/attachments/ContactFieldView.swift +++ b/SignalMessaging/attachments/ContactFieldView.swift @@ -44,8 +44,8 @@ public class ContactFieldView: UIView { addSpacerRow() } self.addSubview(row) - row.autoPinLeadingToSuperviewMargin(withInset: hMargin) - row.autoPinTrailingToSuperviewMargin(withInset: hMargin) + row.autoPinLeadingToSuperviewMargin() + row.autoPinTrailingToSuperviewMargin() if let lastRow = lastRow { row.autoPinEdge(.top, to: .bottom, of: lastRow, withOffset: 0) } else { @@ -60,4 +60,114 @@ public class ContactFieldView: UIView { lastRow?.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0) } + + public class func contactFieldView(forPhoneNumber phoneNumber: OWSContactPhoneNumber, layoutMargins: UIEdgeInsets, actionBlock : (() -> Void)?) -> UIView { + let formattedPhoneNumber = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber.phoneNumber) + return simpleFieldView(name: phoneNumber.localizedLabel(), value: formattedPhoneNumber, layoutMargins: layoutMargins, actionBlock: actionBlock) + } + + public class func contactFieldView(forEmail email: OWSContactEmail, layoutMargins: UIEdgeInsets, actionBlock : (() -> Void)?) -> UIView { + return simpleFieldView(name: email.localizedLabel(), value: email.email, layoutMargins: layoutMargins, actionBlock: actionBlock) + } + + private class func simpleFieldView(name: String, value: String?, layoutMargins: UIEdgeInsets, actionBlock : (() -> Void)?) -> UIView { + var stackView: UIStackView + if let actionBlock = actionBlock { + stackView = TappableStackView(actionBlock: actionBlock) + } else { + stackView = UIStackView() + } + stackView.axis = .vertical + stackView.alignment = .leading + stackView.spacing = 3 + stackView.layoutMargins = layoutMargins + stackView.isLayoutMarginsRelativeArrangement = true + + let nameLabel = UILabel() + nameLabel.text = name + nameLabel.font = UIFont.ows_dynamicTypeSubheadline + nameLabel.textColor = UIColor.black + nameLabel.lineBreakMode = .byTruncatingTail + stackView.addArrangedSubview(nameLabel) + + let valueLabel = UILabel() + valueLabel.text = value + valueLabel.font = UIFont.ows_dynamicTypeBody + valueLabel.textColor = UIColor.ows_materialBlue + valueLabel.lineBreakMode = .byTruncatingTail + stackView.addArrangedSubview(valueLabel) + + return stackView + } + + public class func contactFieldView(forAddress address: OWSContactAddress, layoutMargins: UIEdgeInsets, actionBlock : (() -> Void)?) -> UIView { + var stackView: UIStackView + if let actionBlock = actionBlock { + stackView = TappableStackView(actionBlock: actionBlock) + } else { + stackView = UIStackView() + } + stackView.axis = .vertical + stackView.alignment = .leading + stackView.spacing = 3 + stackView.layoutMargins = layoutMargins + stackView.isLayoutMarginsRelativeArrangement = true + + let nameLabel = UILabel() + nameLabel.text = address.localizedLabel() + nameLabel.font = UIFont.ows_dynamicTypeSubheadline + nameLabel.textColor = UIColor.black + nameLabel.lineBreakMode = .byTruncatingTail + stackView.addArrangedSubview(nameLabel) + + let tryToAddNameValue: ((String, String?) -> Void) = { (propertyName, propertyValue) in + guard let propertyValue = propertyValue else { + return + } + guard propertyValue.count > 0 else { + return + } + + let row = UIStackView() + row.axis = .horizontal + row.alignment = .leading + row.spacing = 10 + row.layoutMargins = .zero + + let nameLabel = UILabel() + nameLabel.text = propertyName + nameLabel.font = UIFont.ows_dynamicTypeBody + nameLabel.textColor = UIColor.black + nameLabel.lineBreakMode = .byTruncatingTail + row.addArrangedSubview(nameLabel) + nameLabel.setContentHuggingHigh() + nameLabel.setCompressionResistanceHigh() + + let valueLabel = UILabel() + valueLabel.text = propertyValue + valueLabel.font = UIFont.ows_dynamicTypeBody + valueLabel.textColor = UIColor.ows_materialBlue + valueLabel.lineBreakMode = .byTruncatingTail + row.addArrangedSubview(valueLabel) + + stackView.addArrangedSubview(row) + } + + tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_STREET", comment: "Label for the 'street' field of a contact's address."), + address.street) + tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POBOX", comment: "Label for the 'pobox' field of a contact's address."), + address.pobox) + tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_NEIGHBORHOOD", comment: "Label for the 'neighborhood' field of a contact's address."), + address.neighborhood) + tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_CITY", comment: "Label for the 'city' field of a contact's address."), + address.city) + tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_REGION", comment: "Label for the 'region' field of a contact's address."), + address.region) + tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POSTCODE", comment: "Label for the 'postcode' field of a contact's address."), + address.postcode) + tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_COUNTRY", comment: "Label for the 'country' field of a contact's address."), + address.country) + + return stackView + } } diff --git a/SignalMessaging/categories/UIView+OWS.m b/SignalMessaging/categories/UIView+OWS.m index a31942e27..14e68d3da 100644 --- a/SignalMessaging/categories/UIView+OWS.m +++ b/SignalMessaging/categories/UIView+OWS.m @@ -459,12 +459,13 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value) - (void)logFrameWithLabel:(NSString *)label { - DDLogVerbose(@"%@ %@ frame: %@, hidden: %d, opacity: %f", + DDLogVerbose(@"%@ %@ frame: %@, hidden: %d, opacity: %f, layoutMargins: %@", self.logTag, label, NSStringFromCGRect(self.frame), self.hidden, - self.layer.opacity); + self.layer.opacity, + NSStringFromUIEdgeInsets(self.layoutMargins)); } - (void)logFrameLater