From 5ada961ec7b1e3badfd1a389b9d310d83104198b Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 19 Apr 2018 15:27:46 -0400 Subject: [PATCH 1/9] unread badge to spec (footnote: 13) // FREEBIE --- Signal/src/ViewControllers/HomeView/HomeViewCell.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 4109d65eb..19cf81bd4 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -120,7 +120,7 @@ NS_ASSUME_NONNULL_BEGIN [self.snippetLabel setCompressionResistanceHorizontalLow]; self.unreadLabel = [UILabel new]; - self.unreadLabel.font = [UIFont ows_dynamicTypeCaption1Font]; + self.unreadLabel.font = self.dateTimeFont; self.unreadLabel.textColor = [UIColor whiteColor]; self.unreadLabel.lineBreakMode = NSLineBreakByTruncatingTail; self.unreadLabel.textAlignment = NSTextAlignmentCenter; From 91f4f38c02953f6f7364d8a5db948c5c354c94b6 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 19 Apr 2018 15:27:54 -0400 Subject: [PATCH 2/9] snippet to spec (subheading: 15pt) // FREEBIE --- Signal/src/ViewControllers/HomeView/HomeViewCell.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 19cf81bd4..36c8352ca 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -318,7 +318,7 @@ NS_ASSUME_NONNULL_BEGIN - (UIFont *)snippetFont { - return [UIFont ows_dynamicTypeFootnoteFont]; + return [UIFont ows_dynamicTypeSubheadlineFont]; } - (UIFont *)nameFont From c693beb763552ad6a9932fa17729d6c1c9b7680d Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 19 Apr 2018 15:39:56 -0400 Subject: [PATCH 3/9] dateTime to spec read should be gray unread should be bold and black // FREEBIE --- .../ViewControllers/HomeView/HomeViewCell.m | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 36c8352ca..5098087e9 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -186,8 +186,15 @@ NS_ASSUME_NONNULL_BEGIN self.snippetLabel.attributedText = [self attributedSnippetForThread:thread blockedPhoneNumberSet:blockedPhoneNumberSet]; - self.dateTimeLabel.attributedText = [self attributedStringForDate:thread.lastMessageDate]; - self.dateTimeLabel.textColor = hasUnreadMessages ? [UIColor ows_materialBlueColor] : [UIColor ows_darkGrayColor]; + self.dateTimeLabel.text = [self stringForDate:thread.lastMessageDate]; + + if (hasUnreadMessages) { + self.dateTimeLabel.textColor = [UIColor ows_blackColor]; + self.dateTimeLabel.font = self.dateTimeFont.ows_mediumWeight; + } else { + self.dateTimeLabel.textColor = [UIColor lightGrayColor]; + self.dateTimeLabel.font = self.dateTimeFont; + } NSUInteger unreadCount = [[OWSMessageUtils sharedManager] unreadMessagesInThread:thread]; if (unreadCount > 0) { @@ -284,11 +291,11 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Date formatting -- (NSAttributedString *)attributedStringForDate:(nullable NSDate *)date +- (NSString *)stringForDate:(nullable NSDate *)date { if (date == nil) { OWSProdLogAndFail(@"%@ date was unexpectedly nil", self.logTag); - return [NSAttributedString new]; + return @""; } NSString *dateTimeString; @@ -302,11 +309,7 @@ NS_ASSUME_NONNULL_BEGIN dateTimeString = [[DateUtil timeFormatter] stringFromDate:date]; } - return [[NSAttributedString alloc] initWithString:dateTimeString.uppercaseString - attributes:@{ - NSForegroundColorAttributeName : [UIColor blackColor], - NSFontAttributeName : self.dateTimeFont, - }]; + return dateTimeString.uppercaseString; } #pragma mark - Constants From ee4d038d2dd2b07143343cb8b7c23a60ce8511f5 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 19 Apr 2018 19:08:16 -0400 Subject: [PATCH 4/9] Top row baseline and badge to spec - Single visual baseline for all top row text - Snippet should not flow under badge, it should stop at it's left edge - unread font to spec (same as date stamp font, but never bold) // FREEBIE --- .../ViewControllers/HomeView/HomeViewCell.m | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 5098087e9..9d5ede7f7 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -24,10 +24,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) UILabel *nameLabel; @property (nonatomic) UILabel *snippetLabel; @property (nonatomic) UILabel *dateTimeLabel; -// The unread badge has a larger v-height than the other elements in its -// row. We don't want it to distort the v-alignment of the cell's labels -// so we use a container to reserve the correct width. -@property (nonatomic) UIView *unreadBadgeContainer; @property (nonatomic) UIView *unreadBadge; @property (nonatomic) UILabel *unreadLabel; @@ -65,20 +61,19 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(!self.avatarView); [self setTranslatesAutoresizingMaskIntoConstraints:NO]; - self.layoutMargins = UIEdgeInsetsZero; - self.contentView.layoutMargins = UIEdgeInsetsZero; - self.preservesSuperviewLayoutMargins = NO; - self.contentView.preservesSuperviewLayoutMargins = NO; + self.layoutMargins = UIEdgeInsetsMake(0, self.cellHMargin, 0, self.cellHMargin); + self.contentView.preservesSuperviewLayoutMargins = YES; self.backgroundColor = [UIColor whiteColor]; _viewConstraints = [NSMutableArray new]; self.avatarView = [[AvatarImageView alloc] init]; + [self.contentView addSubview:self.avatarView]; [self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize]; [self.avatarView autoSetDimension:ALDimensionHeight toSize:self.avatarSize]; - [self.avatarView autoPinLeadingToSuperviewMarginWithInset:self.cellHMargin]; + [self.avatarView autoPinLeadingToSuperviewMargin]; [self.avatarView autoVCenterInSuperview]; [self.avatarView setContentHuggingHigh]; [self.avatarView setCompressionResistanceHigh]; @@ -87,11 +82,11 @@ NS_ASSUME_NONNULL_BEGIN self.payloadView.axis = UILayoutConstraintAxisVertical; [self.contentView addSubview:self.payloadView]; [self.payloadView autoPinLeadingToTrailingEdgeOfView:self.avatarView offset:self.avatarHSpacing]; - [self.payloadView autoPinTrailingToSuperviewMarginWithInset:self.cellHMargin]; [self.payloadView autoVCenterInSuperview]; // Ensure that the cell's contents never overflow the cell bounds. [self.payloadView autoPinEdgeToSuperviewMargin:ALEdgeTop relation:NSLayoutRelationGreaterThanOrEqual]; [self.payloadView autoPinEdgeToSuperviewMargin:ALEdgeBottom relation:NSLayoutRelationGreaterThanOrEqual]; + // We pin the payloadView traillingEdge later, as part of the "Unread Badge" logic. self.nameLabel = [UILabel new]; self.nameLabel.lineBreakMode = NSLineBreakByTruncatingTail; @@ -108,7 +103,7 @@ NS_ASSUME_NONNULL_BEGIN self.dateTimeLabel, ]]; self.topRowView.axis = UILayoutConstraintAxisHorizontal; - self.topRowView.alignment = UIStackViewAlignmentCenter; + self.topRowView.alignment = UIStackViewAlignmentBottom; [self.payloadView addArrangedSubview:self.topRowView]; self.snippetLabel = [UILabel new]; @@ -125,20 +120,12 @@ NS_ASSUME_NONNULL_BEGIN self.unreadLabel.lineBreakMode = NSLineBreakByTruncatingTail; self.unreadLabel.textAlignment = NSTextAlignmentCenter; - self.unreadBadgeContainer = [UIView containerView]; - [self.unreadBadgeContainer setContentHuggingHigh]; - [self.unreadBadgeContainer setCompressionResistanceHigh]; - self.unreadBadge = [NeverClearView new]; self.unreadBadge.backgroundColor = [UIColor ows_materialBlueColor]; - [self.unreadBadgeContainer addSubview:self.unreadBadge]; - [self.unreadBadge autoCenterInSuperview]; - [self.unreadBadge setContentHuggingHigh]; - [self.unreadBadge setCompressionResistanceHigh]; - [self.unreadBadge addSubview:self.unreadLabel]; - [self.unreadLabel autoVCenterInSuperview]; - [self.unreadLabel autoPinWidthToSuperview]; + [self.unreadLabel autoCenterInSuperview]; + [self.unreadLabel setContentHuggingHigh]; + [self.unreadLabel setCompressionResistanceHigh]; } + (NSString *)cellReuseIdentifier @@ -178,7 +165,7 @@ NS_ASSUME_NONNULL_BEGIN [self updateAvatarView]; self.payloadView.spacing = 0.f; - self.topRowView.spacing = ceil([HomeViewCell scaleValueWithDynamicType:5]); + self.topRowView.spacing = self.topRowHSpacing; // We update the fonts every time this cell is configured to ensure that // changes to the dynamic type settings are reflected. @@ -197,10 +184,11 @@ NS_ASSUME_NONNULL_BEGIN } NSUInteger unreadCount = [[OWSMessageUtils sharedManager] unreadMessagesInThread:thread]; - if (unreadCount > 0) { - [self.topRowView addArrangedSubview:self.unreadBadgeContainer]; + if (unreadCount == 0) { + [self.viewConstraints addObject:[self.payloadView autoPinTrailingToSuperviewMargin]]; + } else { + [self.contentView addSubview:self.unreadBadge]; - self.unreadLabel.font = [UIFont ows_dynamicTypeCaption1Font]; self.unreadLabel.text = [OWSFormat formatInt:MIN(99, (int)unreadCount)]; // TODO: Will this localize? It assumes that the worst case @@ -210,9 +198,22 @@ NS_ASSUME_NONNULL_BEGIN self.unreadBadge.layer.cornerRadius = unreadBadgeSize / 2; [self.viewConstraints addObjectsFromArray:@[ - [self.unreadBadgeContainer autoSetDimension:ALDimensionWidth toSize:unreadBadgeSize], + // badge sizing [self.unreadBadge autoSetDimension:ALDimensionWidth toSize:unreadBadgeSize], [self.unreadBadge autoSetDimension:ALDimensionHeight toSize:unreadBadgeSize], + + // Horizontally, badge is inserted after the tail of the payloadView, pushing back the date *and* snippet + // view + [self.payloadView autoPinEdge:ALEdgeTrailing + toEdge:ALEdgeLeading + ofView:self.unreadBadge + withOffset:-self.topRowHSpacing], + [self.unreadBadge autoPinTrailingToSuperviewMargin], + + // Vertically, badge is positioned vertically by aligning it's label *subview's* baseline. + // This allows us a single visual baseline of text across the top row across [name, dateTime, + // optional(unread count)] + [self.unreadLabel autoAlignAxis:ALAxisBaseline toSameAxisOfView:self.dateTimeLabel] ]]; } } @@ -381,6 +382,12 @@ NS_ASSUME_NONNULL_BEGIN return 12.f; } +// Using an NSUInteger precludes us from negating this value +- (CGFloat)topRowHSpacing +{ + return ceil([HomeViewCell scaleValueWithDynamicType:5]); +} + #pragma mark - Reuse - (void)prepareForReuse @@ -393,7 +400,7 @@ NS_ASSUME_NONNULL_BEGIN self.thread = nil; self.contactsManager = nil; - [self.unreadBadgeContainer removeFromSuperview]; + [self.unreadBadge removeFromSuperview]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } From 2bc072fe8e66892264a3fc27eeb3cacb6a59ba2c Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 19 Apr 2018 19:21:35 -0400 Subject: [PATCH 5/9] Now that snippet is a bit higher, increase max message cell height // FREEBIE --- Signal/src/ViewControllers/HomeView/HomeViewCell.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 9d5ede7f7..554bd6347 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -361,7 +361,7 @@ NS_ASSUME_NONNULL_BEGIN CGFloat alpha = CGFloatClamp01(CGFloatInverseLerp(referenceFontSize, kReferenceFontSizeMin, kReferenceFontSizeMax)); const CGFloat kCellHeightMin = 68.f; - const CGFloat kCellHeightMax = 76.f; + const CGFloat kCellHeightMax = 80.f; CGFloat result = ceil(CGFloatLerp(kCellHeightMin, kCellHeightMax, alpha)); return result; From bba2bcefe29a3f416e3446f193dea822e498baf6 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 19 Apr 2018 19:51:05 -0400 Subject: [PATCH 6/9] Grow unread badge into pill // FREEBIE --- .../ViewControllers/HomeView/HomeViewCell.m | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 554bd6347..ce872724b 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -189,18 +189,34 @@ NS_ASSUME_NONNULL_BEGIN } else { [self.contentView addSubview:self.unreadBadge]; - self.unreadLabel.text = [OWSFormat formatInt:MIN(99, (int)unreadCount)]; + self.unreadLabel.text = [OWSFormat formatInt:unreadCount]; // TODO: Will this localize? It assumes that the worst case // unread count (99) will fit horizontally into some multiple // N of the font's line height. - const int unreadBadgeSize = (int)ceil(self.unreadLabel.font.lineHeight * 1.5f); - self.unreadBadge.layer.cornerRadius = unreadBadgeSize / 2; + const int unreadBadgeHeight = (int)ceil(self.unreadLabel.font.lineHeight * 1.5f); + self.unreadBadge.layer.cornerRadius = unreadBadgeHeight / 2; + + [NSLayoutConstraint autoSetPriority:UILayoutPriorityDefaultHigh + forConstraints:^{ + // This is a bit arbitrary, but it should scale with the size of dynamic text + CGFloat minMargin = CeilEven(unreadBadgeHeight * .5); + + // Spec check. Should be 12pts (6pt on each side) when using default font size. + OWSAssert(UIFont.ows_dynamicTypeBodyFont.pointSize != 17 || minMargin == 12); + + [self.viewConstraints addObject:[self.unreadBadge autoMatchDimension:ALDimensionWidth + toDimension:ALDimensionWidth + ofView:self.unreadLabel + withOffset:minMargin]]; + }]; [self.viewConstraints addObjectsFromArray:@[ // badge sizing - [self.unreadBadge autoSetDimension:ALDimensionWidth toSize:unreadBadgeSize], - [self.unreadBadge autoSetDimension:ALDimensionHeight toSize:unreadBadgeSize], + [self.unreadBadge autoSetDimension:ALDimensionWidth + toSize:unreadBadgeHeight + relation:NSLayoutRelationGreaterThanOrEqual], + [self.unreadBadge autoSetDimension:ALDimensionHeight toSize:unreadBadgeHeight], // Horizontally, badge is inserted after the tail of the payloadView, pushing back the date *and* snippet // view From 743867859f9a3d26e0a853c87d96e79791940b6b Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 19 Apr 2018 19:51:18 -0400 Subject: [PATCH 7/9] smaller font for date/unread, per updated spec // FREEBIE --- Signal/src/ViewControllers/HomeView/HomeViewCell.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index ce872724b..d47bbde92 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -333,7 +333,7 @@ NS_ASSUME_NONNULL_BEGIN - (UIFont *)dateTimeFont { - return [UIFont ows_dynamicTypeFootnoteFont].ows_mediumWeight; + return [UIFont ows_dynamicTypeCaption1Font].ows_mediumWeight; } - (UIFont *)snippetFont From 8ca62a8d447130392fb0b32d3c3e778c3aa632a1 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 19 Apr 2018 21:01:45 -0400 Subject: [PATCH 8/9] Align top row by baseline // FREEBIE --- Signal/src/ViewControllers/HomeView/HomeViewCell.m | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index d47bbde92..a69c40451 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -103,7 +103,7 @@ NS_ASSUME_NONNULL_BEGIN self.dateTimeLabel, ]]; self.topRowView.axis = UILayoutConstraintAxisHorizontal; - self.topRowView.alignment = UIStackViewAlignmentBottom; + self.topRowView.alignment = UIStackViewAlignmentLastBaseline; [self.payloadView addArrangedSubview:self.topRowView]; self.snippetLabel = [UILabel new]; @@ -189,11 +189,8 @@ NS_ASSUME_NONNULL_BEGIN } else { [self.contentView addSubview:self.unreadBadge]; - self.unreadLabel.text = [OWSFormat formatInt:unreadCount]; + self.unreadLabel.text = [OWSFormat formatInt:(int)unreadCount]; - // TODO: Will this localize? It assumes that the worst case - // unread count (99) will fit horizontally into some multiple - // N of the font's line height. const int unreadBadgeHeight = (int)ceil(self.unreadLabel.font.lineHeight * 1.5f); self.unreadBadge.layer.cornerRadius = unreadBadgeHeight / 2; From 8e561750993fabfbb6962cb4c0de78d19883316c Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 20 Apr 2018 16:05:25 -0400 Subject: [PATCH 9/9] CR: unread badge updates w/ dynamic text change also renames for clarity // FREEBIE --- Signal/src/ViewControllers/HomeView/HomeViewCell.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index a69c40451..7281b65b2 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -62,6 +62,7 @@ NS_ASSUME_NONNULL_BEGIN [self setTranslatesAutoresizingMaskIntoConstraints:NO]; self.layoutMargins = UIEdgeInsetsMake(0, self.cellHMargin, 0, self.cellHMargin); + self.contentView.layoutMargins = UIEdgeInsetsZero; self.contentView.preservesSuperviewLayoutMargins = YES; self.backgroundColor = [UIColor whiteColor]; @@ -115,7 +116,6 @@ NS_ASSUME_NONNULL_BEGIN [self.snippetLabel setCompressionResistanceHorizontalLow]; self.unreadLabel = [UILabel new]; - self.unreadLabel.font = self.dateTimeFont; self.unreadLabel.textColor = [UIColor whiteColor]; self.unreadLabel.lineBreakMode = NSLineBreakByTruncatingTail; self.unreadLabel.textAlignment = NSTextAlignmentCenter; @@ -177,10 +177,10 @@ NS_ASSUME_NONNULL_BEGIN if (hasUnreadMessages) { self.dateTimeLabel.textColor = [UIColor ows_blackColor]; - self.dateTimeLabel.font = self.dateTimeFont.ows_mediumWeight; + self.dateTimeLabel.font = self.unreadFont.ows_mediumWeight; } else { self.dateTimeLabel.textColor = [UIColor lightGrayColor]; - self.dateTimeLabel.font = self.dateTimeFont; + self.dateTimeLabel.font = self.unreadFont; } NSUInteger unreadCount = [[OWSMessageUtils sharedManager] unreadMessagesInThread:thread]; @@ -190,7 +190,7 @@ NS_ASSUME_NONNULL_BEGIN [self.contentView addSubview:self.unreadBadge]; self.unreadLabel.text = [OWSFormat formatInt:(int)unreadCount]; - + self.unreadLabel.font = self.unreadFont; const int unreadBadgeHeight = (int)ceil(self.unreadLabel.font.lineHeight * 1.5f); self.unreadBadge.layer.cornerRadius = unreadBadgeHeight / 2; @@ -328,7 +328,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Constants -- (UIFont *)dateTimeFont +- (UIFont *)unreadFont { return [UIFont ows_dynamicTypeCaption1Font].ows_mediumWeight; }