diff --git a/Signal/Images.xcassets/generic-attachment.imageset/Contents.json b/Signal/Images.xcassets/generic-attachment.imageset/Contents.json new file mode 100644 index 000000000..53e5e34c0 --- /dev/null +++ b/Signal/Images.xcassets/generic-attachment.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "File@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "File@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "File@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/generic-attachment.imageset/File@1x.png b/Signal/Images.xcassets/generic-attachment.imageset/File@1x.png new file mode 100644 index 000000000..cd817594a Binary files /dev/null and b/Signal/Images.xcassets/generic-attachment.imageset/File@1x.png differ diff --git a/Signal/Images.xcassets/generic-attachment.imageset/File@2x.png b/Signal/Images.xcassets/generic-attachment.imageset/File@2x.png new file mode 100644 index 000000000..24027f545 Binary files /dev/null and b/Signal/Images.xcassets/generic-attachment.imageset/File@2x.png differ diff --git a/Signal/Images.xcassets/generic-attachment.imageset/File@3x.png b/Signal/Images.xcassets/generic-attachment.imageset/File@3x.png new file mode 100644 index 000000000..26c2172db Binary files /dev/null and b/Signal/Images.xcassets/generic-attachment.imageset/File@3x.png differ diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.h index 73b9bcaba..fac8da3f6 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.h @@ -1,12 +1,12 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN @class TSAttachmentStream; -@interface OWSGenericAttachmentView : UIView +@interface OWSGenericAttachmentView : UIStackView - (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream isIncoming:(BOOL)isIncoming; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m index 8778028ee..78599afb4 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m @@ -41,9 +41,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -- (CGFloat)iconHMargin +- (CGFloat)hMargin { - return 12.f; + return 0.f; } - (CGFloat)iconHSpacing @@ -51,19 +51,19 @@ NS_ASSUME_NONNULL_BEGIN return 8.f; } -+ (CGFloat)iconVMargin ++ (CGFloat)vMargin { - return 12.f; + return 0.f; } -- (CGFloat)iconVMargin +- (CGFloat)vMargin { - return [OWSGenericAttachmentView iconVMargin]; + return [OWSGenericAttachmentView vMargin]; } + (CGFloat)bubbleHeight { - return self.iconSize + self.iconVMargin * 2; + return self.iconHeight + self.vMargin * 2; } - (CGFloat)bubbleHeight @@ -71,19 +71,14 @@ NS_ASSUME_NONNULL_BEGIN return [OWSGenericAttachmentView bubbleHeight]; } -+ (CGFloat)iconSize ++ (CGFloat)iconHeight { - return 44.f; + return 48.f; } -- (CGFloat)iconSize -{ - return [OWSGenericAttachmentView iconSize]; -} - -- (CGFloat)vMargin +- (CGFloat)iconHeight { - return 10.f; + return [OWSGenericAttachmentView iconHeight]; } - (UIColor *)bubbleBackgroundColor @@ -105,45 +100,27 @@ NS_ASSUME_NONNULL_BEGIN { UIColor *textColor = (self.isIncoming ? [UIColor colorWithWhite:0.2 alpha:1.f] : [UIColor whiteColor]); - self.backgroundColor = self.bubbleBackgroundColor; self.layoutMargins = UIEdgeInsetsZero; + self.axis = UILayoutConstraintAxisHorizontal; + self.alignment = UIStackViewAlignmentCenter; + self.spacing = self.iconHSpacing; - // TODO: Verify that this layout works in RTL. - const CGFloat kBubbleTailWidth = 6.f; - - UIView *contentView = [UIView containerView]; - [self addSubview:contentView]; - [contentView autoPinLeadingToSuperviewMarginWithInset:self.isIncoming ? kBubbleTailWidth : 0.f]; - [contentView autoPinTrailingToSuperviewMarginWithInset:self.isIncoming ? 0.f : kBubbleTailWidth]; - [contentView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.vMargin]; - [contentView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.vMargin]; - - UIView *iconCircleView = [UIView containerView]; - iconCircleView.backgroundColor - = (self.isIncoming ? [UIColor colorWithRGBHex:0x9e9e9e] : [self foregroundColorWithOpacity:0.15f]); - iconCircleView.layer.cornerRadius = self.iconSize * 0.5f; - [contentView addSubview:iconCircleView]; - [iconCircleView autoPinLeadingToSuperviewMarginWithInset:self.iconHMargin]; - [iconCircleView autoVCenterInSuperview]; - [iconCircleView autoSetDimension:ALDimensionWidth toSize:self.iconSize]; - [iconCircleView autoSetDimension:ALDimensionHeight toSize:self.iconSize]; - - UIImage *image = [UIImage imageNamed:@"attachment_file"]; + // attachment_file + UIImage *image = [UIImage imageNamed:@"generic-attachment"]; OWSAssert(image); + OWSAssert(image.size.height == self.iconHeight); UIImageView *imageView = [UIImageView new]; - imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + imageView.image = image; imageView.tintColor = self.bubbleBackgroundColor; - [iconCircleView addSubview:imageView]; - [imageView autoCenterInSuperview]; + [self addArrangedSubview:imageView]; + [imageView setContentHuggingHigh]; - const CGFloat kLabelHSpacing = self.iconHSpacing; - UIView *labelsView = [UIView containerView]; - [contentView addSubview:labelsView]; - [labelsView autoPinLeadingToTrailingEdgeOfView:iconCircleView offset:kLabelHSpacing]; - [labelsView autoPinTrailingToSuperviewMarginWithInset:self.iconHMargin]; - [labelsView autoVCenterInSuperview]; + UIStackView *labelsView = [UIStackView new]; + labelsView.axis = UILayoutConstraintAxisVertical; + labelsView.spacing = 2; + labelsView.alignment = UIStackViewAlignmentLeading; + [self addArrangedSubview:labelsView]; - const CGFloat kLabelVSpacing = 2; NSString *topText = [self.attachmentStream.sourceFilename ows_stripped]; if (topText.length < 1) { topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachmentStream.contentType].uppercaseString; @@ -156,9 +133,7 @@ NS_ASSUME_NONNULL_BEGIN topLabel.textColor = textColor; topLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; topLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(13.f, 15.f)]; - [labelsView addSubview:topLabel]; - [topLabel autoPinEdgeToSuperviewEdge:ALEdgeTop]; - [topLabel autoPinWidthToSuperview]; + [labelsView addArrangedSubview:topLabel]; NSError *error; unsigned long long fileSize = @@ -170,10 +145,7 @@ NS_ASSUME_NONNULL_BEGIN bottomLabel.textColor = [textColor colorWithAlphaComponent:0.85f]; bottomLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; bottomLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(11.f, 13.f)]; - [labelsView addSubview:bottomLabel]; - [bottomLabel autoPinWidthToSuperview]; - [bottomLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:topLabel withOffset:kLabelVSpacing]; - [bottomLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom]; + [labelsView addArrangedSubview:bottomLabel]; } @end diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index accc79bcd..31c0d62d9 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -246,10 +246,10 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(self.viewItem.interaction); OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - CGSize quotedMessageContentSize = [self quotedMessageSize]; + NSValue *_Nullable quotedMessageSize = [self quotedMessageSize]; // TODO: - CGSize bodyMediaContentSize = [self bodyMediaSize]; - CGSize bodyTextContentSize = [self bodyTextSizeWithIncludeMargins:NO]; + // CGSize bodyMediaContentSize = [self bodyMediaSize]; + NSValue *_Nullable bodyTextSize = [self bodyTextSize]; [self.bubbleView addSubview:self.stackView]; [self.viewConstraints addObjectsFromArray:[self.stackView autoPinEdgesToSuperviewEdges]]; @@ -260,9 +260,11 @@ NS_ASSUME_NONNULL_BEGIN [textViews addObject:self.senderNameLabel]; } + UIView *_Nullable topTextStackView = nil; if (self.isQuotedReply) { // Flush any pending "text" subviews. - [self insertAnyTextViewsIntoStackView:textViews]; + OWSAssert(!topTextStackView); + topTextStackView = [self insertAnyTextViewsIntoStackView:textViews]; [textViews removeAllObjects]; BOOL isOutgoing = [self.viewItem.interaction isKindOfClass:TSOutgoingMessage.class]; @@ -278,14 +280,14 @@ NS_ASSUME_NONNULL_BEGIN self.quotedMessageView = quotedMessageView; [quotedMessageView createContents]; [self.stackView addArrangedSubview:quotedMessageView]; - [self.viewConstraints - addObject:[quotedMessageView autoSetDimension:ALDimensionHeight toSize:quotedMessageContentSize.height]]; + OWSAssert(quotedMessageSize); + [self.viewConstraints addObject:[quotedMessageView autoSetDimension:ALDimensionHeight + toSize:quotedMessageSize.CGSizeValue.height]]; [self.bubbleView addPartnerView:quotedMessageView.boundsStrokeView]; } UIView *_Nullable bodyMediaView = nil; - BOOL hasThumbnailForBodyMedia = NO; switch (self.cellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextMessage: @@ -294,17 +296,14 @@ NS_ASSUME_NONNULL_BEGIN case OWSMessageCellType_StillImage: OWSAssert(self.viewItem.attachmentStream); bodyMediaView = [self loadViewForStillImage]; - hasThumbnailForBodyMedia = YES; break; case OWSMessageCellType_AnimatedImage: OWSAssert(self.viewItem.attachmentStream); bodyMediaView = [self loadViewForAnimatedImage]; - hasThumbnailForBodyMedia = YES; break; case OWSMessageCellType_Video: OWSAssert(self.viewItem.attachmentStream); bodyMediaView = [self loadViewForVideo]; - hasThumbnailForBodyMedia = YES; break; case OWSMessageCellType_Audio: OWSAssert(self.viewItem.attachmentStream); @@ -325,19 +324,21 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(self.loadCellContentBlock); OWSAssert(self.unloadCellContentBlock); - // Flush any pending "text" subviews. - [self insertAnyTextViewsIntoStackView:textViews]; - [textViews removeAllObjects]; - bodyMediaView.clipsToBounds = YES; self.bodyMediaView = bodyMediaView; bodyMediaView.userInteractionEnabled = NO; if (self.isMediaBeingSent) { + // TODO: bodyMediaView.layer.opacity = 0.75f; } - if (hasThumbnailForBodyMedia) { + if (self.hasBodyMediaWithThumbnail) { + // Flush any pending "text" subviews. + OWSAssert(!topTextStackView); + topTextStackView = [self insertAnyTextViewsIntoStackView:textViews]; + [textViews removeAllObjects]; + // The "body media" view casts a shadow "downward" onto adjacent views, // so we use a "proxy" view to take its place within the v-stack // view and then insert the body media view above its proxy so that @@ -369,7 +370,8 @@ NS_ASSUME_NONNULL_BEGIN self.mediaShadowView.layer.shadowOffset = CGSizeMake(0.f, 0.f); self.mediaShadowView.layer.shadowRadius = 0.5f; } else { - [self.stackView addArrangedSubview:bodyMediaView]; + // [self.stackView addArrangedSubview:bodyMediaView]; + [textViews addObject:bodyMediaView]; } } @@ -379,8 +381,10 @@ NS_ASSUME_NONNULL_BEGIN [self configureBodyTextView]; [textViews addObject:self.bodyTextView]; + // TODO: Media? + OWSAssert(bodyTextSize); [self.viewConstraints addObjectsFromArray:@[ - [self.bodyTextView autoSetDimension:ALDimensionHeight toSize:bodyTextContentSize.height], + [self.bodyTextView autoSetDimension:ALDimensionHeight toSize:bodyTextSize.CGSizeValue.height], ]]; UIView *_Nullable tapForMoreLabel = [self createTapForMoreLabelIfNecessary]; @@ -422,6 +426,8 @@ NS_ASSUME_NONNULL_BEGIN }]; [self updateBubbleColorWithBodyMediaView:bodyMediaView]; + + [self logFrameLaterWithLabel:@"----- message bubble"]; } - (void)updateBubbleColorWithBodyMediaView:(nullable UIView *)bodyMediaView @@ -461,7 +467,7 @@ NS_ASSUME_NONNULL_BEGIN return [self.bubbleFactory bubbleColorWithMessage:message]; } -- (BOOL)canFooterOverlayMedia +- (BOOL)hasBodyMediaWithThumbnail { switch (self.cellType) { case OWSMessageCellType_Unknown: @@ -480,10 +486,15 @@ NS_ASSUME_NONNULL_BEGIN } } -- (void)insertAnyTextViewsIntoStackView:(NSArray *)textViews +- (BOOL)canFooterOverlayMedia +{ + return self.hasBodyMediaWithThumbnail; +} + +- (nullable UIView *)insertAnyTextViewsIntoStackView:(NSArray *)textViews { if (textViews.count < 1) { - return; + return nil; } UIStackView *textStackView = [[UIStackView alloc] initWithArrangedSubviews:textViews]; @@ -497,6 +508,7 @@ NS_ASSUME_NONNULL_BEGIN self.conversationStyle.textInsetBottom, self.conversationStyle.textInsetHorizontal); [self.stackView addArrangedSubview:textStackView]; + return textStackView; } // We now eagerly create our view hierarchy (to do this exactly once per cell usage) @@ -930,13 +942,13 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Measurement // Size of "message body" text, not quoted reply text. -- (CGSize)bodyTextSizeWithIncludeMargins:(BOOL)includeMargins +- (nullable NSValue *)bodyTextSize { OWSAssert(self.conversationStyle); OWSAssert(self.conversationStyle.maxMessageWidth > 0); if (!self.hasBodyText) { - return CGSizeZero; + return nil; } CGFloat hMargins = self.conversationStyle.textInsetHorizontal * 2; @@ -947,15 +959,10 @@ NS_ASSUME_NONNULL_BEGIN const int kMaxIterations = 5; CGSize result = [self.bodyTextView compactSizeThatFitsMaxWidth:maxTextWidth maxIterations:kMaxIterations]; - if (includeMargins) { - result.width += hMargins; - result.height += (self.conversationStyle.textInsetTop + self.conversationStyle.textInsetBottom); - } - - return CGSizeCeil(result); + return [NSValue valueWithCGSize:CGSizeCeil(result)]; } -- (CGSize)bodyMediaSize +- (nullable NSValue *)bodyMediaSize { OWSAssert(self.conversationStyle); OWSAssert(self.conversationStyle.maxMessageWidth > 0); @@ -967,8 +974,7 @@ NS_ASSUME_NONNULL_BEGIN case OWSMessageCellType_Unknown: case OWSMessageCellType_TextMessage: case OWSMessageCellType_OversizeTextMessage: { - result = CGSizeZero; - break; + return nil; } case OWSMessageCellType_StillImage: case OWSMessageCellType_AnimatedImage: @@ -1024,10 +1030,10 @@ NS_ASSUME_NONNULL_BEGIN break; } - return CGSizeCeil(result); + return [NSValue valueWithCGSize:CGSizeCeil(result)]; } -- (CGSize)quotedMessageSize +- (nullable NSValue *)quotedMessageSize { OWSAssert(self.conversationStyle); OWSAssert(self.conversationStyle.maxMessageWidth > 0); @@ -1035,7 +1041,7 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); if (!self.isQuotedReply) { - return CGSizeZero; + return nil; } BOOL isOutgoing = [self.viewItem.interaction isKindOfClass:TSOutgoingMessage.class]; @@ -1047,16 +1053,16 @@ NS_ASSUME_NONNULL_BEGIN displayableQuotedText:displayableQuotedText isOutgoing:isOutgoing]; CGSize result = [quotedMessageView sizeForMaxWidth:self.conversationStyle.maxMessageWidth]; - return CGSizeCeil(result); + return [NSValue valueWithCGSize:CGSizeCeil(result)]; } -- (CGSize)senderNameSizeWithBodyMediaSize:(CGSize)bodyMediaSize includeMargins:(BOOL)includeMargins +- (nullable NSValue *)senderNameSize { OWSAssert(self.conversationStyle); OWSAssert(self.conversationStyle.maxMessageWidth > 0); if (!self.shouldShowSenderName) { - return CGSizeZero; + return nil; } CGFloat hMargins = self.conversationStyle.textInsetHorizontal * 2; @@ -1064,18 +1070,7 @@ NS_ASSUME_NONNULL_BEGIN [self configureSenderNameLabel]; CGSize result = CGSizeCeil([self.senderNameLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); - BOOL hasSeparateTextStackView = (self.isQuotedReply || bodyMediaSize.width > 0 || bodyMediaSize.height > 0); - - if (includeMargins) { - result.width += hMargins; - - if (hasSeparateTextStackView) { - result.height += (self.conversationStyle.textInsetTop + self.conversationStyle.textInsetBottom); - } else { - result.height += self.textViewVSpacing; - } - } - return result; + return [NSValue valueWithCGSize:result]; } - (CGSize)measureSize @@ -1087,24 +1082,56 @@ NS_ASSUME_NONNULL_BEGIN CGSize cellSize = CGSizeZero; - // TODO: Reflect "sender name" and "footer" layout. - // shouldFooterOverlayMedia = self.canFooterOverlayMedia; + NSMutableArray *textViewSizes = [NSMutableArray new]; - CGSize quotedMessageSize = [self quotedMessageSize]; - cellSize.width = MAX(cellSize.width, quotedMessageSize.width); - cellSize.height += quotedMessageSize.height; + NSValue *_Nullable senderNameSize = [self senderNameSize]; + if (senderNameSize) { + [textViewSizes addObject:senderNameSize]; + } - CGSize bodyMediaSize = [self bodyMediaSize]; - cellSize.width = MAX(cellSize.width, bodyMediaSize.width); - cellSize.height += bodyMediaSize.height; + NSValue *_Nullable quotedMessageSize = [self quotedMessageSize]; + if (quotedMessageSize) { + cellSize.width = MAX(cellSize.width, quotedMessageSize.CGSizeValue.width); + cellSize.height += quotedMessageSize.CGSizeValue.height; + } - CGSize senderNameSize = [self senderNameSizeWithBodyMediaSize:bodyMediaSize includeMargins:YES]; - cellSize.width = MAX(cellSize.width, senderNameSize.width); - cellSize.height += senderNameSize.height; + NSValue *_Nullable bodyMediaSize = [self bodyMediaSize]; + if (bodyMediaSize) { + if (self.hasBodyMediaWithThumbnail) { + cellSize.width = MAX(cellSize.width, bodyMediaSize.CGSizeValue.width); + cellSize.height += bodyMediaSize.CGSizeValue.height; + } else { + DDLogVerbose(@"%@ ---- bodyMediaSize: %@", self.logTag, bodyMediaSize); + [textViewSizes addObject:bodyMediaSize]; + bodyMediaSize = nil; + } + } - CGSize textContentSize = [self bodyTextSizeWithIncludeMargins:YES]; - cellSize.width = MAX(cellSize.width, textContentSize.width); - cellSize.height += textContentSize.height; + if (bodyMediaSize || quotedMessageSize) { + if (textViewSizes.count > 0) { + CGSize groupSize = [self sizeForTextViewGroup:textViewSizes]; + cellSize.width = MAX(cellSize.width, groupSize.width); + cellSize.height += groupSize.height; + [textViewSizes removeAllObjects]; + } + } + + NSValue *_Nullable bodyTextSize = [self bodyTextSize]; + if (bodyTextSize) { + [textViewSizes addObject:bodyTextSize]; + } + + BOOL shouldFooterOverlayMedia = (self.canFooterOverlayMedia && !self.hasBodyText); + if (!self.viewItem.shouldHideFooter && !shouldFooterOverlayMedia) { + CGSize footerSize = [self.footerView measureWithConversationViewItem:self.viewItem]; + [textViewSizes addObject:[NSValue valueWithCGSize:footerSize]]; + } + + if (textViewSizes.count > 0) { + CGSize groupSize = [self sizeForTextViewGroup:textViewSizes]; + cellSize.width = MAX(cellSize.width, groupSize.width); + cellSize.height += groupSize.height; + } // Make sure the bubble is always wide enough to complete it's bubble shape. cellSize.width = MAX(cellSize.width, OWSBubbleView.minWidth); @@ -1115,23 +1142,32 @@ NS_ASSUME_NONNULL_BEGIN cellSize.height += self.tapForMoreHeight + self.textViewVSpacing; } - // TODO: Update this to reflect generic attachment, downloading attachments and - // contact shares. - BOOL shouldFooterOverlayMedia = (self.canFooterOverlayMedia && !self.hasBodyText); - if (!self.viewItem.shouldHideFooter && !shouldFooterOverlayMedia) { - CGSize footerSize = [self.footerView measureWithConversationViewItem:self.viewItem]; - cellSize.width = MAX(cellSize.width, footerSize.width + self.conversationStyle.textInsetHorizontal * 2); - cellSize.height += self.textViewVSpacing + footerSize.height; - if (!self.hasBodyText) { - cellSize.height += (self.conversationStyle.textInsetTop + self.conversationStyle.textInsetBottom); - } - } - cellSize = CGSizeCeil(cellSize); + DDLogVerbose(@"%@ ---- cellSize: %@", self.logTag, NSStringFromCGSize(cellSize)); + return cellSize; } +- (CGSize)sizeForTextViewGroup:(NSArray *)textViewSizes +{ + OWSAssert(textViewSizes); + OWSAssert(textViewSizes.count > 0); + OWSAssert(self.conversationStyle); + OWSAssert(self.conversationStyle.maxMessageWidth > 0); + + CGSize result = CGSizeZero; + for (NSValue *size in textViewSizes) { + result.width = MAX(result.width, size.CGSizeValue.width); + result.height += size.CGSizeValue.height; + } + result.height += self.textViewVSpacing * (textViewSizes.count - 1); + result.height += (self.conversationStyle.textInsetTop + self.conversationStyle.textInsetBottom); + result.width += self.conversationStyle.textInsetHorizontal * 2; + + return result; +} + - (UIFont *)tapForMoreFont { return UIFont.ows_dynamicTypeCaption1Font;