From 5807ba69ca2de628d00fe3ad2ed81468e9da6e99 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 9 Apr 2018 17:00:44 -0400 Subject: [PATCH 1/4] attempt 1: QMV doesn't have intrinsic content size, and requires a fixed width to compute it's size, which we don't currently have. // FREEBIE --- Signal/src/Signal-Bridging-Header.h | 1 + Signal/src/views/QuotedReplyPreview.swift | 122 +++++++++++----------- 2 files changed, 64 insertions(+), 59 deletions(-) diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index ac4265c58..a7775a9da 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -29,6 +29,7 @@ #import "OWSMessageCell.h" #import "OWSNavigationController.h" #import "OWSProgressView.h" +#import "OWSQuotedMessageView.h" #import "OWSWebRTCDataProtos.pb.h" #import "PinEntryView.h" #import "PrivacySettingsTableViewController.h" diff --git a/Signal/src/views/QuotedReplyPreview.swift b/Signal/src/views/QuotedReplyPreview.swift index dbe9ee0fe..34588b8de 100644 --- a/Signal/src/views/QuotedReplyPreview.swift +++ b/Signal/src/views/QuotedReplyPreview.swift @@ -20,6 +20,8 @@ class QuotedReplyPreview: UIView { init(quotedReply: OWSQuotedReplyModel) { super.init(frame: .zero) + let quotedMessageView = OWSQuotedMessageView(forPreview: quotedReply) + let isQuotingSelf = quotedReply.authorId == TSAccountManager.localNumber() // used for stripe and author @@ -29,40 +31,40 @@ class QuotedReplyPreview: UIView { // used for text and cancel let foregroundColor: UIColor = .darkGray - let authorLabel: UILabel = UILabel() - authorLabel.textColor = authorColor - if isQuotingSelf { - authorLabel.text = NSLocalizedString("MEDIA_GALLERY_SENDER_NAME_YOU", comment: "") - } else { - authorLabel.text = Environment.current().contactsManager.displayName(forPhoneIdentifier: quotedReply.authorId) - } - authorLabel.font = .ows_dynamicTypeHeadline - - let bodyLabel: UILabel = UILabel() - bodyLabel.textColor = foregroundColor - bodyLabel.font = .ows_dynamicTypeFootnote - - bodyLabel.text = { - if let contentType = quotedReply.contentType { - let emoji = TSAttachmentStream.emoji(forMimeType: contentType) - return "\(emoji) \(quotedReply.body ?? "")" - } else { - return quotedReply.body - } - }() - - let thumbnailView: UIView? = { - if let image = quotedReply.thumbnailImage { - let imageView = UIImageView(image: image) - imageView.contentMode = .scaleAspectFill - imageView.autoPinToSquareAspectRatio() - imageView.layer.cornerRadius = 3.0 - imageView.clipsToBounds = true - - return imageView - } - return nil - }() +// let authorLabel: UILabel = UILabel() +// authorLabel.textColor = authorColor +// if isQuotingSelf { +// authorLabel.text = NSLocalizedString("MEDIA_GALLERY_SENDER_NAME_YOU", comment: "") +// } else { +// authorLabel.text = Environment.current().contactsManager.displayName(forPhoneIdentifier: quotedReply.authorId) +// } +// authorLabel.font = .ows_dynamicTypeHeadline +// +// let bodyLabel: UILabel = UILabel() +// bodyLabel.textColor = foregroundColor +// bodyLabel.font = .ows_dynamicTypeFootnote +// +// bodyLabel.text = { +// if let contentType = quotedReply.contentType { +// let emoji = TSAttachmentStream.emoji(forMimeType: contentType) +// return "\(emoji) \(quotedReply.body ?? "")" +// } else { +// return quotedReply.body +// } +// }() +// +// let thumbnailView: UIView? = { +// if let image = quotedReply.thumbnailImage { +// let imageView = UIImageView(image: image) +// imageView.contentMode = .scaleAspectFill +// imageView.autoPinToSquareAspectRatio() +// imageView.layer.cornerRadius = 3.0 +// imageView.clipsToBounds = true +// +// return imageView +// } +// return nil +// }() let cancelButton: UIButton = UIButton(type: .custom) // FIXME proper image asset/size @@ -71,35 +73,37 @@ class QuotedReplyPreview: UIView { cancelButton.imageView?.tintColor = foregroundColor cancelButton.addTarget(self, action: #selector(didTapCancel), for: .touchUpInside) - let quoteStripe: UIView = UIView() - quoteStripe.backgroundColor = authorColor - - let textColumn = UIView.container() - textColumn.addSubview(authorLabel) - textColumn.addSubview(bodyLabel) - - authorLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom) - authorLabel.autoPinEdge(.bottom, to: .top, of: bodyLabel) - bodyLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .top) +// let quoteStripe: UIView = UIView() +// quoteStripe.backgroundColor = authorColor +// +// let textColumn = UIView.container() +// textColumn.addSubview(authorLabel) +// textColumn.addSubview(bodyLabel) +// +// authorLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom) +// authorLabel.autoPinEdge(.bottom, to: .top, of: bodyLabel) +// bodyLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .top) +// +// let contentViews: [UIView] = [textColumn, thumbnailView, cancelButton].flatMap { return $0 } - let contentViews: [UIView] = [textColumn, thumbnailView, cancelButton].flatMap { return $0 } + let contentViews: [UIView] = [quotedMessageView, cancelButton] let contentRow = UIStackView(arrangedSubviews: contentViews) contentRow.axis = .horizontal self.addSubview(contentRow) - self.addSubview(quoteStripe) - - // Layout - - let kQuoteStripeWidth: CGFloat = 4 - self.layoutMargins = UIEdgeInsets(top: 6, - left: kQuoteStripeWidth + 8, - bottom: 2, - right: 4) - - quoteStripe.autoPinEdge(toSuperviewEdge: .leading) - quoteStripe.autoPinHeightToSuperview() - quoteStripe.autoSetDimension(.width, toSize: kQuoteStripeWidth) - +// self.addSubview(quoteStripe) +// +// // Layout +// +// let kQuoteStripeWidth: CGFloat = 4 +// self.layoutMargins = UIEdgeInsets(top: 6, +// left: kQuoteStripeWidth + 8, +// bottom: 2, +// right: 4) +// +// quoteStripe.autoPinEdge(toSuperviewEdge: .leading) +// quoteStripe.autoPinHeightToSuperview() +// quoteStripe.autoSetDimension(.width, toSize: kQuoteStripeWidth) +// contentRow.autoPinEdgesToSuperviewMargins() cancelButton.autoSetDimensions(to: CGSize(width: 40, height: 40)) From 5287ab8f8c7f2e4718b417916ad0b9028812ef26 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 9 Apr 2018 17:15:56 -0400 Subject: [PATCH 2/4] Try 2: no more stack view // FREEBIE --- .../Cells/OWSQuotedMessageView.m | 2 + Signal/src/views/QuotedReplyPreview.swift | 73 +++++++++++-------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 2b6dd8ec0..fb7a3d02c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -66,6 +66,8 @@ NS_ASSUME_NONNULL_BEGIN _quotedMessage = quotedMessage; _displayableQuotedText = displayableQuotedText; + [self createContents]; + return self; } diff --git a/Signal/src/views/QuotedReplyPreview.swift b/Signal/src/views/QuotedReplyPreview.swift index 34588b8de..0dfa1f328 100644 --- a/Signal/src/views/QuotedReplyPreview.swift +++ b/Signal/src/views/QuotedReplyPreview.swift @@ -73,38 +73,47 @@ class QuotedReplyPreview: UIView { cancelButton.imageView?.tintColor = foregroundColor cancelButton.addTarget(self, action: #selector(didTapCancel), for: .touchUpInside) -// let quoteStripe: UIView = UIView() -// quoteStripe.backgroundColor = authorColor +//// let quoteStripe: UIView = UIView() +//// quoteStripe.backgroundColor = authorColor +//// +//// let textColumn = UIView.container() +//// textColumn.addSubview(authorLabel) +//// textColumn.addSubview(bodyLabel) +//// +//// authorLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom) +//// authorLabel.autoPinEdge(.bottom, to: .top, of: bodyLabel) +//// bodyLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .top) +//// +//// let contentViews: [UIView] = [textColumn, thumbnailView, cancelButton].flatMap { return $0 } // -// let textColumn = UIView.container() -// textColumn.addSubview(authorLabel) -// textColumn.addSubview(bodyLabel) -// -// authorLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom) -// authorLabel.autoPinEdge(.bottom, to: .top, of: bodyLabel) -// bodyLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .top) -// -// let contentViews: [UIView] = [textColumn, thumbnailView, cancelButton].flatMap { return $0 } - - let contentViews: [UIView] = [quotedMessageView, cancelButton] - let contentRow = UIStackView(arrangedSubviews: contentViews) - contentRow.axis = .horizontal - self.addSubview(contentRow) -// self.addSubview(quoteStripe) -// -// // Layout -// -// let kQuoteStripeWidth: CGFloat = 4 -// self.layoutMargins = UIEdgeInsets(top: 6, -// left: kQuoteStripeWidth + 8, -// bottom: 2, -// right: 4) -// -// quoteStripe.autoPinEdge(toSuperviewEdge: .leading) -// quoteStripe.autoPinHeightToSuperview() -// quoteStripe.autoSetDimension(.width, toSize: kQuoteStripeWidth) -// - contentRow.autoPinEdgesToSuperviewMargins() +// let contentViews: [UIView] = [quotedMessageView, cancelButton] +// let contentRow = UIStackView(arrangedSubviews: contentViews) +// contentRow.axis = .horizontal +// self.addSubview(contentRow) +//// self.addSubview(quoteStripe) +//// +//// // Layout +//// +//// let kQuoteStripeWidth: CGFloat = 4 +//// self.layoutMargins = UIEdgeInsets(top: 6, +//// left: kQuoteStripeWidth + 8, +//// bottom: 2, +//// right: 4) +//// +//// quoteStripe.autoPinEdge(toSuperviewEdge: .leading) +//// quoteStripe.autoPinHeightToSuperview() +//// quoteStripe.autoSetDimension(.width, toSize: kQuoteStripeWidth) +//// +// contentRow.autoPinEdgesToSuperviewMargins() + + self.layoutMargins = .zero + + self.addSubview(quotedMessageView) + self.addSubview(cancelButton) + + quotedMessageView.autoPinEdges(toSuperviewMarginsExcludingEdge: .trailing) + cancelButton.autoPinEdges(toSuperviewMarginsExcludingEdge: .leading) + cancelButton.autoPinEdge(.leading, to: .trailing, of: quotedMessageView) cancelButton.autoSetDimensions(to: CGSize(width: 40, height: 40)) } @@ -113,7 +122,7 @@ class QuotedReplyPreview: UIView { // Used by stack view to determin size. override var intrinsicContentSize: CGSize { - return CGSize(width: 0, height: 30) + return CGSize(width: 0, height: 50) } // MARK: Actions From 520dad25bb1b511523a1cc1fd158641c2fc6f6c4 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 9 Apr 2018 17:48:31 -0400 Subject: [PATCH 3/4] WIP making OWSQuotedMessageView work with preview // FREEBIE --- .../ConversationView/Cells/OWSQuotedMessageView.m | 15 ++++++++++----- Signal/src/views/QuotedReplyPreview.swift | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index fb7a3d02c..cdfc5437c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable, readonly) DisplayableText *displayableQuotedText; @property (nonatomic, nullable) OWSBubbleStrokeView *boundsStrokeView; +@property (nonatomic, readonly) BOOL isForPreview; @end @@ -35,8 +36,9 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssert(quotedMessage); - return - [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage displayableQuotedText:displayableQuotedText]; + return [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage + displayableQuotedText:displayableQuotedText + isForPreview:NO]; } + (OWSQuotedMessageView *)quotedMessageViewForPreview:(OWSQuotedReplyModel *)quotedMessage @@ -48,12 +50,14 @@ NS_ASSUME_NONNULL_BEGIN displayableQuotedText = [DisplayableText displayableText:quotedMessage.body]; } - return - [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage displayableQuotedText:displayableQuotedText]; + return [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage + displayableQuotedText:displayableQuotedText + isForPreview:YES]; } - (instancetype)initWithQuotedMessage:(OWSQuotedReplyModel *)quotedMessage displayableQuotedText:(nullable DisplayableText *)displayableQuotedText + isForPreview:(BOOL)isForPreview { self = [super init]; @@ -65,6 +69,7 @@ NS_ASSUME_NONNULL_BEGIN _quotedMessage = quotedMessage; _displayableQuotedText = displayableQuotedText; + _isForPreview = isForPreview; [self createContents]; @@ -260,7 +265,7 @@ NS_ASSUME_NONNULL_BEGIN } UILabel *quotedTextLabel = [UILabel new]; - quotedTextLabel.numberOfLines = 3; + quotedTextLabel.numberOfLines = self.isForPreview ? 1 : 3; quotedTextLabel.lineBreakMode = NSLineBreakByWordWrapping; quotedTextLabel.text = text; quotedTextLabel.textColor = textColor; diff --git a/Signal/src/views/QuotedReplyPreview.swift b/Signal/src/views/QuotedReplyPreview.swift index 0dfa1f328..02ce456ca 100644 --- a/Signal/src/views/QuotedReplyPreview.swift +++ b/Signal/src/views/QuotedReplyPreview.swift @@ -21,6 +21,7 @@ class QuotedReplyPreview: UIView { super.init(frame: .zero) let quotedMessageView = OWSQuotedMessageView(forPreview: quotedReply) + quotedMessageView.backgroundColor = .clear let isQuotingSelf = quotedReply.authorId == TSAccountManager.localNumber() @@ -116,6 +117,9 @@ class QuotedReplyPreview: UIView { cancelButton.autoPinEdge(.leading, to: .trailing, of: quotedMessageView) cancelButton.autoSetDimensions(to: CGSize(width: 40, height: 40)) + + // TODO this is arbitrary and breaks with dynamic type + self.autoSetDimension(.height, toSize: 70) } // MARK: UIViewOverrides From 314e3cbf03625cb7f3c6c0f9136dec9664c4351e Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 9 Apr 2018 18:13:44 -0400 Subject: [PATCH 4/4] Drive quote authoring with QuotedMessageView // FREEBIE --- Signal/src/views/QuotedReplyPreview.swift | 118 ++++++---------------- 1 file changed, 32 insertions(+), 86 deletions(-) diff --git a/Signal/src/views/QuotedReplyPreview.swift b/Signal/src/views/QuotedReplyPreview.swift index 02ce456ca..dcb6dc3e9 100644 --- a/Signal/src/views/QuotedReplyPreview.swift +++ b/Signal/src/views/QuotedReplyPreview.swift @@ -13,100 +13,40 @@ protocol QuotedReplyPreviewDelegate: class { class QuotedReplyPreview: UIView { public weak var delegate: QuotedReplyPreviewDelegate? + private let quotedReply: OWSQuotedReplyModel + private var quotedMessageView: OWSQuotedMessageView + private var heightConstraint: NSLayoutConstraint! + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } init(quotedReply: OWSQuotedReplyModel) { + self.quotedReply = quotedReply + self.quotedMessageView = OWSQuotedMessageView(forPreview: quotedReply) + super.init(frame: .zero) - let quotedMessageView = OWSQuotedMessageView(forPreview: quotedReply) - quotedMessageView.backgroundColor = .clear + self.heightConstraint = self.autoSetDimension(.height, toSize: 0) - let isQuotingSelf = quotedReply.authorId == TSAccountManager.localNumber() + updateContents() - // used for stripe and author - // FIXME actual colors TBD - let authorColor: UIColor = isQuotingSelf ? .ows_materialBlue : .black + NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: .UIContentSizeCategoryDidChange, object: nil) + } - // used for text and cancel - let foregroundColor: UIColor = .darkGray + func updateContents() { + subviews.forEach { $0.removeFromSuperview() } + self.quotedMessageView = OWSQuotedMessageView(forPreview: quotedReply) -// let authorLabel: UILabel = UILabel() -// authorLabel.textColor = authorColor -// if isQuotingSelf { -// authorLabel.text = NSLocalizedString("MEDIA_GALLERY_SENDER_NAME_YOU", comment: "") -// } else { -// authorLabel.text = Environment.current().contactsManager.displayName(forPhoneIdentifier: quotedReply.authorId) -// } -// authorLabel.font = .ows_dynamicTypeHeadline -// -// let bodyLabel: UILabel = UILabel() -// bodyLabel.textColor = foregroundColor -// bodyLabel.font = .ows_dynamicTypeFootnote -// -// bodyLabel.text = { -// if let contentType = quotedReply.contentType { -// let emoji = TSAttachmentStream.emoji(forMimeType: contentType) -// return "\(emoji) \(quotedReply.body ?? "")" -// } else { -// return quotedReply.body -// } -// }() -// -// let thumbnailView: UIView? = { -// if let image = quotedReply.thumbnailImage { -// let imageView = UIImageView(image: image) -// imageView.contentMode = .scaleAspectFill -// imageView.autoPinToSquareAspectRatio() -// imageView.layer.cornerRadius = 3.0 -// imageView.clipsToBounds = true -// -// return imageView -// } -// return nil -// }() + quotedMessageView.backgroundColor = .clear let cancelButton: UIButton = UIButton(type: .custom) - // FIXME proper image asset/size + let buttonImage: UIImage = #imageLiteral(resourceName: "quoted-message-cancel").withRenderingMode(.alwaysTemplate) cancelButton.setImage(buttonImage, for: .normal) - cancelButton.imageView?.tintColor = foregroundColor + cancelButton.imageView?.tintColor = .darkGray cancelButton.addTarget(self, action: #selector(didTapCancel), for: .touchUpInside) -//// let quoteStripe: UIView = UIView() -//// quoteStripe.backgroundColor = authorColor -//// -//// let textColumn = UIView.container() -//// textColumn.addSubview(authorLabel) -//// textColumn.addSubview(bodyLabel) -//// -//// authorLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom) -//// authorLabel.autoPinEdge(.bottom, to: .top, of: bodyLabel) -//// bodyLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .top) -//// -//// let contentViews: [UIView] = [textColumn, thumbnailView, cancelButton].flatMap { return $0 } -// -// let contentViews: [UIView] = [quotedMessageView, cancelButton] -// let contentRow = UIStackView(arrangedSubviews: contentViews) -// contentRow.axis = .horizontal -// self.addSubview(contentRow) -//// self.addSubview(quoteStripe) -//// -//// // Layout -//// -//// let kQuoteStripeWidth: CGFloat = 4 -//// self.layoutMargins = UIEdgeInsets(top: 6, -//// left: kQuoteStripeWidth + 8, -//// bottom: 2, -//// right: 4) -//// -//// quoteStripe.autoPinEdge(toSuperviewEdge: .leading) -//// quoteStripe.autoPinHeightToSuperview() -//// quoteStripe.autoSetDimension(.width, toSize: kQuoteStripeWidth) -//// -// contentRow.autoPinEdgesToSuperviewMargins() - self.layoutMargins = .zero self.addSubview(quotedMessageView) @@ -118,20 +58,26 @@ class QuotedReplyPreview: UIView { cancelButton.autoSetDimensions(to: CGSize(width: 40, height: 40)) - // TODO this is arbitrary and breaks with dynamic type - self.autoSetDimension(.height, toSize: 70) - } - - // MARK: UIViewOverrides - - // Used by stack view to determin size. - override var intrinsicContentSize: CGSize { - return CGSize(width: 0, height: 50) + updateHeight() } // MARK: Actions + @objc func didTapCancel(_ sender: Any) { self.delegate?.quotedReplyPreviewDidPressCancel(self) } + + // MARK: Sizing + + func updateHeight() { + let size = self.quotedMessageView.size(forMaxWidth: CGFloat.infinity) + self.heightConstraint.constant = size.height + } + + func contentSizeCategoryDidChange(_ notification: Notification) { + Logger.debug("\(self.logTag) in \(#function)") + + updateContents() + } }