Skip to content

Commit 94a2302

Browse files
committed
Size error messages correctly.
* calculate size of info message using the info message font. * offset by the info message header There were instances of lines getting cropped, or an extra line being added. The previous, more conservative, solution was to just make every bubble too big, but it looked terrible. // FREEBIE
1 parent b951123 commit 94a2302

File tree

8 files changed

+130
-42
lines changed

8 files changed

+130
-42
lines changed

Signal/src/Models/OWSMessagesBubblesSizeCalculator.m

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#import "OWSMessagesBubblesSizeCalculator.h"
44
#import "OWSDisplayedMessageCollectionViewCell.h"
55
#import "TSMessageAdapter.h"
6+
#import "UIFont+OWS.h"
67
#import "tgmath.h" // generic math allows fmax to handle CGFLoat correctly on 32 & 64bit.
78
#import <JSQMessagesViewController/JSQMessagesCollectionViewFlowLayout.h>
89

@@ -41,8 +42,14 @@ - (CGSize)messageBubbleSizeForMessageData:(id<JSQMessageData>)messageData
4142
atIndexPath:(NSIndexPath *)indexPath
4243
withLayout:(JSQMessagesCollectionViewFlowLayout *)layout
4344
{
44-
CGSize size;
45+
if ([messageData isKindOfClass:[TSMessageAdapter class]]) {
46+
TSMessageAdapter *message = (TSMessageAdapter *)messageData;
47+
if (message.messageType == TSInfoMessageAdapter || message.messageType == TSErrorMessageAdapter) {
48+
return [self messageBubbleSizeForInfoMessageData:messageData atIndexPath:indexPath withLayout:layout];
49+
}
50+
}
4551

52+
CGSize size;
4653
// BEGIN HACK iOS10EmojiBug see: https://github.com/WhisperSystems/Signal-iOS/issues/1368
4754
BOOL isIOS10OrGreater =
4855
[[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){.majorVersion = 10 }];
@@ -55,22 +62,8 @@ - (CGSize)messageBubbleSizeForMessageData:(id<JSQMessageData>)messageData
5562
}
5663
// END HACK iOS10EmojiBug see: https://github.com/WhisperSystems/Signal-iOS/issues/1368
5764

58-
if ([messageData isKindOfClass:[TSMessageAdapter class]]) {
59-
TSMessageAdapter *message = (TSMessageAdapter *)messageData;
6065

6166

62-
if (message.messageType == TSInfoMessageAdapter || message.messageType == TSErrorMessageAdapter) {
63-
// DDLogVerbose(@"[OWSMessagesBubblesSizeCalculator] superSize.height:%f, superSize.width:%f",
64-
// superSize.height,
65-
// superSize.width);
66-
67-
// header icon hangs ouside of the frame a bit.
68-
CGFloat headerIconProtrusion = 30.0f; // too much padding with normal font.
69-
// CGFloat headerIconProtrusion = 18.0f; // clips
70-
size.height += headerIconProtrusion;
71-
}
72-
}
73-
7467
return size;
7568
}
7669

@@ -173,6 +166,83 @@ - (CGSize)withiOS10EmojiFixSuperMessageBubbleSizeForMessageData:(id<JSQMessageDa
173166
return finalSize;
174167
}
175168

169+
170+
- (CGSize)messageBubbleSizeForInfoMessageData:(id<JSQMessageData>)messageData
171+
atIndexPath:(NSIndexPath *)indexPath
172+
withLayout:(JSQMessagesCollectionViewFlowLayout *)layout
173+
{
174+
NSValue *cachedSize = [self.cache objectForKey:@([messageData messageHash])];
175+
if (cachedSize != nil) {
176+
return [cachedSize CGSizeValue];
177+
}
178+
179+
CGSize finalSize = CGSizeZero;
180+
181+
if ([messageData isMediaMessage]) {
182+
finalSize = [[messageData media] mediaViewDisplaySize];
183+
} else {
184+
///////////////////
185+
// BEGIN InfoMessage sizing HACK
186+
// Braindead, and painstakingly produced.
187+
// If you want to change, check for clipping / excess space on 1, 2, and 3 line messages with short and long
188+
// words very near the edge.
189+
190+
// CGSize avatarSize = [self jsq_avatarSizeForMessageData:messageData withLayout:layout];
191+
// // from the cell xibs, there is a 2 point space between avatar and bubble
192+
// CGFloat spacingBetweenAvatarAndBubble = 2.0f;
193+
// CGFloat horizontalContainerInsets = layout.messageBubbleTextViewTextContainerInsets.left + layout.messageBubbleTextViewTextContainerInsets.right;
194+
// CGFloat horizontalFrameInsets = layout.messageBubbleTextViewFrameInsets.left + layout.messageBubbleTextViewFrameInsets.right;
195+
// CGFloat horizontalInsetsTotal = horizontalContainerInsets + horizontalFrameInsets + spacingBetweenAvatarAndBubble;
196+
// CGFloat maximumTextWidth = [self textBubbleWidthForLayout:layout] - avatarSize.width - layout.messageBubbleLeftRightMargin - horizontalInsetsTotal;
197+
198+
// The full layout width, less the textView margins from xib.
199+
// CGFloat horizontalInsetsTotal = 12.0; cropped 3rd line
200+
CGFloat horizontalInsetsTotal = 50.0;
201+
CGFloat maximumTextWidth = [self textBubbleWidthForLayout:layout] - horizontalInsetsTotal;
202+
203+
CGRect stringRect = [[messageData text]
204+
boundingRectWithSize:CGSizeMake(maximumTextWidth, CGFLOAT_MAX)
205+
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
206+
attributes:@{
207+
NSFontAttributeName : [UIFont ows_dynamicTypeBodyFont]
208+
} // Hack to use a slightly larger than actual font, because I'm seeing messages with higher line count get clipped.
209+
context:nil];
210+
// END InfoMessage sizing HACK
211+
////////////////////
212+
213+
CGSize stringSize = CGRectIntegral(stringRect).size;
214+
215+
CGFloat verticalContainerInsets = layout.messageBubbleTextViewTextContainerInsets.top
216+
+ layout.messageBubbleTextViewTextContainerInsets.bottom;
217+
218+
CGFloat verticalFrameInsets
219+
= layout.messageBubbleTextViewFrameInsets.top + layout.messageBubbleTextViewFrameInsets.bottom;
220+
///////////////////
221+
// BEGIN InfoMessage sizing HACK
222+
223+
CGFloat topIconPortrusion = 28;
224+
225+
verticalFrameInsets += topIconPortrusion;
226+
227+
// END InfoMessage sizing HACK
228+
///////////////////
229+
230+
// add extra 2 points of space (`self.additionalInset`), because `boundingRectWithSize:` is slightly off
231+
// not sure why. magix. (shrug) if you know, submit a PR
232+
CGFloat verticalInsets = verticalContainerInsets + verticalFrameInsets + self.additionalInset;
233+
234+
// same as above, an extra 2 points of magix
235+
CGFloat finalWidth
236+
= MAX(stringSize.width + horizontalInsetsTotal, self.minimumBubbleWidth) + self.additionalInset;
237+
238+
finalSize = CGSizeMake(finalWidth, stringSize.height + verticalInsets);
239+
}
240+
241+
[self.cache setObject:[NSValue valueWithCGSize:finalSize] forKey:@([messageData messageHash])];
242+
243+
return finalSize;
244+
}
245+
176246
@end
177247

178248
NS_ASSUME_NONNULL_END

Signal/src/util/UIDevice+TSHardwareVersion.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ - (UIDeviceFamily)deviceFamily {
158158
return UIDeviceFamilyUnknown;
159159
}
160160

161+
// FIXME this is probably broken for new iPhones =(
162+
// Who could have guessed that there would ever be a new iPhone model?
161163
- (BOOL)isiPhoneVersionSixOrMore {
162164
return
163165
[[self modelIdentifier] isEqualToString:@"iPhone7,1"] || [[self modelIdentifier] isEqualToString:@"iPhone7,2"];

Signal/src/util/UIFont+OWS.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@
2525

2626
+ (UIFont *)ows_dynamicTypeBodyFont;
2727
+ (UIFont *)ows_dynamicTypeTitle2Font;
28+
+ (UIFont *)ows_infoMessageFont;
2829

2930
@end

Signal/src/util/UIFont+OWS.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ + (UIFont *)ows_dynamicTypeBodyFont {
3737
return [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
3838
}
3939

40+
+ (UIFont *)ows_infoMessageFont
41+
{
42+
return [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
43+
}
44+
4045
+ (UIFont *)ows_dynamicTypeTitle2Font {
4146
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(_iOS_9)) {
4247
return [UIFont preferredFontForTextStyle:UIFontTextStyleTitle2];

Signal/src/view controllers/MessagesViewController.m

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ @interface MessagesViewController () {
110110
@property (nonatomic, strong) UILabel *navbarTitleLabel;
111111
@property (nonatomic, retain) UIButton *attachButton;
112112

113+
@property (nonatomic) CGFloat previousCollectionViewFrameWidth;
114+
113115
@property NSUInteger page;
114116
@property (nonatomic) BOOL composeOnOpen;
115117
@property (nonatomic) BOOL peek;
@@ -215,12 +217,6 @@ - (void)viewDidLoad
215217
{
216218
[super viewDidLoad];
217219

218-
// JSQMVC width is 375px at this point (as specified by the xib), but this causes
219-
// our initial bubble calculations to be off since they happen before the containing
220-
// view is layed out. https://github.com/jessesquires/JSQMessagesViewController/issues/1257
221-
// Resetting here makes sure we've got a good initial width.
222-
[self resetFrame];
223-
224220
[self.navigationController.navigationBar setTranslucent:NO];
225221

226222
self.messageAdapterCache = [[NSCache alloc] init];
@@ -251,6 +247,23 @@ - (void)viewDidLoad
251247
[self initializeToolbars];
252248
}
253249

250+
- (void)viewDidLayoutSubviews
251+
{
252+
[super viewDidLayoutSubviews];
253+
254+
// JSQMVC width is initially 375px on iphone6/ios9 (as specified by the xib), which causes
255+
// our initial bubble calculations to be off since they happen before the containing
256+
// view is layed out. https://github.com/jessesquires/JSQMessagesViewController/issues/1257
257+
if (CGRectGetWidth(self.collectionView.frame) != self.previousCollectionViewFrameWidth) {
258+
// save frame value from next comparison
259+
self.previousCollectionViewFrameWidth = CGRectGetWidth(self.collectionView.frame);
260+
261+
// invalidate layout
262+
[self.collectionView.collectionViewLayout
263+
invalidateLayoutWithContext:[JSQMessagesCollectionViewFlowLayoutInvalidationContext context]];
264+
}
265+
}
266+
254267
- (void)didMoveToParentViewController:(UIViewController *)parent
255268
{
256269
[self setupTitleLabelGestureRecognizer];
@@ -619,7 +632,7 @@ - (void)didPressSendButton:(UIButton *)button
619632
message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
620633
inThread:self.thread
621634
messageBody:text
622-
attachmentIds:@[]
635+
attachmentIds:[NSMutableArray new]
623636
expiresInSeconds:configuration.durationSeconds];
624637
} else {
625638
message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
@@ -831,6 +844,7 @@ - (OWSDisplayedMessageCollectionViewCell *)loadDisplayedMessageCollectionViewCel
831844
forIndexPath:indexPath];
832845
messageCell.layer.shouldRasterize = YES;
833846
messageCell.layer.rasterizationScale = [UIScreen mainScreen].scale;
847+
messageCell.textView.textColor = [UIColor darkGrayColor];
834848
messageCell.cellTopLabel.attributedText = [self.collectionView.dataSource collectionView:self.collectionView attributedTextForCellTopLabelAtIndexPath:indexPath];
835849

836850
return messageCell;
@@ -846,7 +860,7 @@ - (OWSDisplayedMessageCollectionViewCell *)loadInfoMessageCellForMessage:(OWSInf
846860
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:self.thread.uniqueId];
847861
[self setBarButtonItemsForDisappearingMessagesConfiguration:configuration];
848862

849-
infoCell.cellLabel.text = [infoMessage text];
863+
infoCell.textView.text = [infoMessage text];
850864
infoCell.messageBubbleContainerView.layer.borderColor = [[UIColor ows_infoMessageBorderColor] CGColor];
851865
infoCell.headerImageView.image = [UIImage imageNamed:@"warning_white"];
852866

@@ -857,7 +871,7 @@ - (OWSDisplayedMessageCollectionViewCell *)loadErrorMessageCellForMessage:(OWSEr
857871
atIndexPath:(NSIndexPath *)indexPath
858872
{
859873
OWSDisplayedMessageCollectionViewCell *errorCell = [self loadDisplayedMessageCollectionViewCellForIndexPath:indexPath];
860-
errorCell.cellLabel.text = [errorMessage text];
874+
errorCell.textView.text = [errorMessage text];
861875
errorCell.messageBubbleContainerView.layer.borderColor = [[UIColor ows_errorMessageBorderColor] CGColor];
862876
errorCell.headerImageView.image = [UIImage imageNamed:@"error_white"];
863877

Signal/src/views/OWSDisplayedMessageCollectionViewCell.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ extern const CGFloat OWSDisplayedMessageCellMinimumHeight;
99

1010
@interface OWSDisplayedMessageCollectionViewCell : JSQMessagesCollectionViewCell
1111

12-
@property (weak, nonatomic, readonly) JSQMessagesLabel *cellLabel;
1312
@property (weak, nonatomic, readonly) UIImageView *headerImageView;
1413

1514
@end

Signal/src/views/OWSDisplayedMessageCollectionViewCell.m

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved.
44

55
#import "OWSDisplayedMessageCollectionViewCell.h"
6-
6+
#import "UIFont+OWS.h"
77
#import <JSQMessagesViewController/UIView+JSQMessages.h>
88

99
const CGFloat OWSDisplayedMessageCellMinimumHeight = 70.0;
1010

1111
@interface OWSDisplayedMessageCollectionViewCell ()
1212

13-
@property (weak, nonatomic) IBOutlet JSQMessagesLabel *cellLabel;
1413
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cellTopLabelHeightConstraint;
1514
@property (weak, nonatomic) IBOutlet UIImageView *headerImageView;
1615

@@ -43,7 +42,8 @@ - (void)awakeFromNib
4342
self.messageBubbleContainerView.layer.borderColor = [[UIColor lightGrayColor] CGColor];
4443
self.messageBubbleContainerView.layer.borderWidth = 0.75f;
4544
self.messageBubbleContainerView.layer.cornerRadius = 5.0f;
46-
self.cellLabel.textColor = [UIColor darkGrayColor];
45+
self.textView.font = [UIFont ows_infoMessageFont];
46+
self.textView.textColor = [UIColor darkGrayColor];
4747
}
4848

4949
#pragma mark - Collection view cell
@@ -52,7 +52,7 @@ - (void)prepareForReuse
5252
{
5353
[super prepareForReuse];
5454

55-
self.cellLabel.text = nil;
55+
self.textView.text = nil;
5656
}
5757

5858
@end

Signal/src/views/OWSDisplayedMessageCollectionViewCell.xib

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,18 @@
2727
</label>
2828
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qCf-bs-dBd" userLabel="Bubble container">
2929
<subviews>
30-
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Info Message" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="OVa-Xw-5vl" customClass="JSQMessagesLabel">
31-
<constraints>
32-
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="14" id="fed-2c-dqd"/>
33-
</constraints>
34-
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
35-
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
36-
<nil key="highlightedColor"/>
37-
</label>
30+
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" text="Lorem ipsum dolor sit er elit lamet" textAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="gbd-vF-h3e" customClass="JSQMessagesCellTextView">
31+
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
32+
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
33+
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
34+
</textView>
3835
</subviews>
3936
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
4037
<constraints>
41-
<constraint firstItem="OVa-Xw-5vl" firstAttribute="leading" secondItem="qCf-bs-dBd" secondAttribute="leading" constant="8" id="2IE-8k-czI"/>
42-
<constraint firstAttribute="bottom" secondItem="OVa-Xw-5vl" secondAttribute="bottom" constant="8" id="MtI-jW-t1x"/>
43-
<constraint firstAttribute="trailing" secondItem="OVa-Xw-5vl" secondAttribute="trailing" constant="8" id="Y8z-8G-PLt"/>
44-
<constraint firstItem="OVa-Xw-5vl" firstAttribute="top" secondItem="qCf-bs-dBd" secondAttribute="top" constant="12" id="v5B-tB-pOB"/>
38+
<constraint firstAttribute="trailing" secondItem="gbd-vF-h3e" secondAttribute="trailing" constant="6" id="WME-2Q-rFT"/>
39+
<constraint firstItem="gbd-vF-h3e" firstAttribute="top" secondItem="qCf-bs-dBd" secondAttribute="top" constant="6" id="Zee-be-E6m"/>
40+
<constraint firstAttribute="bottom" secondItem="gbd-vF-h3e" secondAttribute="bottom" id="aCy-5N-gI4"/>
41+
<constraint firstItem="gbd-vF-h3e" firstAttribute="leading" secondItem="qCf-bs-dBd" secondAttribute="leading" constant="6" id="wL5-SN-GFl"/>
4542
</constraints>
4643
</view>
4744
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="warning_white.png" translatesAutoresizingMaskIntoConstraints="NO" id="ePO-Cy-jUE">
@@ -65,11 +62,11 @@
6562
</constraints>
6663
<size key="customSize" width="320" height="55"/>
6764
<connections>
68-
<outlet property="cellLabel" destination="OVa-Xw-5vl" id="7PC-oj-dQZ"/>
6965
<outlet property="cellTopLabel" destination="gcR-Rk-KDC" id="Ogk-hD-ge8"/>
7066
<outlet property="cellTopLabelHeightConstraint" destination="ckj-xD-FJI" id="wBH-pQ-Wc7"/>
7167
<outlet property="headerImageView" destination="ePO-Cy-jUE" id="4uq-2C-V7U"/>
7268
<outlet property="messageBubbleContainerView" destination="qCf-bs-dBd" id="WMx-Di-LZG"/>
69+
<outlet property="textView" destination="gbd-vF-h3e" id="Ynt-WC-VrK"/>
7370
</connections>
7471
<point key="canvasLocation" x="219" y="433"/>
7572
</collectionViewCell>

0 commit comments

Comments
 (0)