Skip to content

Commit

Permalink
Add markdown support for chat messages
Browse files Browse the repository at this point in the history
Signed-off-by: Marcel Müller <[email protected]>
  • Loading branch information
SystemKeeper committed Jul 19, 2023
1 parent bad9da7 commit 0a7b4f1
Show file tree
Hide file tree
Showing 15 changed files with 156 additions and 30 deletions.
41 changes: 41 additions & 0 deletions NextcloudTalk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
1F0A1D442A5F1FA800A25433 /* SwiftMarkdownObjCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0A1D432A5F1FA800A25433 /* SwiftMarkdownObjCBridge.swift */; };
1F0ECBF52A68274400921E90 /* CDMarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = 1F0ECBF42A68274400921E90 /* CDMarkdownKit */; };
1F0ECBF72A68277000921E90 /* CDMarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = 1F0ECBF62A68277000921E90 /* CDMarkdownKit */; };
1F0ECBF92A68277C00921E90 /* CDMarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = 1F0ECBF82A68277C00921E90 /* CDMarkdownKit */; };
1F11FB7229C07B04001E21E7 /* NCZoomableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F11FB7129C07B04001E21E7 /* NCZoomableView.swift */; };
1F1C0D7F29A7F33600D17C6D /* NCNotificationAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA38C8F29A4B3C6008871B8 /* NCNotificationAction.swift */; };
1F1C0D8729AFB88800D17C6D /* VLCKitVideoViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1F1C0D8629AFB88800D17C6D /* VLCKitVideoViewController.xib */; };
Expand Down Expand Up @@ -73,6 +77,8 @@
1FA732FC2966CBB7003D2103 /* CallFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA732FB2966CBB7003D2103 /* CallFlowLayout.swift */; };
1FB52E762842C75E00AC741B /* QRCodeLoginController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB52E752842C75E00AC741B /* QRCodeLoginController.swift */; };
1FB6678F28CE381300D29F8D /* SubtitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB6678E28CE381300D29F8D /* SubtitleTableViewCell.swift */; };
1FC940B92A5F21FC00FFFADE /* SwiftMarkdownObjCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0A1D432A5F1FA800A25433 /* SwiftMarkdownObjCBridge.swift */; };
1FC940BA2A5F21FD00FFFADE /* SwiftMarkdownObjCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0A1D432A5F1FA800A25433 /* SwiftMarkdownObjCBridge.swift */; };
1FD8AE6B2A3A216300787C16 /* NextcloudTalkUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FD8AD8C2A3A162100787C16 /* NextcloudTalkUITests.swift */; };
1FD9182928C55A73009092AB /* BGTaskHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FD9182828C55A73009092AB /* BGTaskHelper.swift */; };
1FDCC3D429EBF6E700DEB39B /* AvatarImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDCC3D329EBF6E700DEB39B /* AvatarImageView.swift */; };
Expand Down Expand Up @@ -419,6 +425,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
1F0A1D432A5F1FA800A25433 /* SwiftMarkdownObjCBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftMarkdownObjCBridge.swift; sourceTree = "<group>"; };
1F11FB7129C07B04001E21E7 /* NCZoomableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCZoomableView.swift; sourceTree = "<group>"; };
1F1C0D8629AFB88800D17C6D /* VLCKitVideoViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = VLCKitVideoViewController.xib; sourceTree = "<group>"; };
1F1C0D8829AFB89900D17C6D /* VLCKitVideoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCKitVideoViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -891,6 +898,7 @@
1F45A1162A01D6EC005FE87D /* SDWebImage in Frameworks */,
2C90E5641EDDE0FB0093D85A /* Foundation.framework in Frameworks */,
1F468E7628DCC6C60099597B /* Dynamic in Frameworks */,
1F0ECBF52A68274400921E90 /* CDMarkdownKit in Frameworks */,
2C38D4AC27BBAFCC00BAE015 /* WebRTC.xcframework in Frameworks */,
1F66B72F29FABD01003FB168 /* SwiftyAttributes in Frameworks */,
1F7AE07829142CA1009F72AD /* NextcloudKit in Frameworks */,
Expand All @@ -903,6 +911,7 @@
buildActionMask = 2147483647;
files = (
1F7AE07C29142E6A009F72AD /* NextcloudKit in Frameworks */,
1F0ECBF92A68277C00921E90 /* CDMarkdownKit in Frameworks */,
8789AE73BFCAA413B43319C0 /* libPods-ShareExtension.a in Frameworks */,
1F45A11A2A01D70E005FE87D /* SDWebImage in Frameworks */,
1F45A1252A01D8F7005FE87D /* SDWebImageSVGKitPlugin in Frameworks */,
Expand All @@ -914,6 +923,7 @@
buildActionMask = 2147483647;
files = (
1F45A1232A01D8F1005FE87D /* SDWebImageSVGKitPlugin in Frameworks */,
1F0ECBF72A68277000921E90 /* CDMarkdownKit in Frameworks */,
1F7AE07A29142E62009F72AD /* NextcloudKit in Frameworks */,
1F7AE07D29158878009F72AD /* IntentsUI.framework in Frameworks */,
3FCA62550CD1442D28E8A7C6 /* libPods-NotificationServiceExtension.a in Frameworks */,
Expand Down Expand Up @@ -1371,6 +1381,7 @@
2CA52ACF267613CA00619610 /* VoiceMessageTableViewCell.m */,
2C5BFBEB28895E6A00E75118 /* ObjectShareMessageTableViewCell.h */,
2C5BFBEC28895E6B00E75118 /* ObjectShareMessageTableViewCell.m */,
1F0A1D432A5F1FA800A25433 /* SwiftMarkdownObjCBridge.swift */,
);
name = "Chat cells";
sourceTree = "<group>";
Expand Down Expand Up @@ -1658,6 +1669,7 @@
1F66B72E29FABD01003FB168 /* SwiftyAttributes */,
1F45A1152A01D6EC005FE87D /* SDWebImage */,
1F45A1202A01D8BA005FE87D /* SDWebImageSVGKitPlugin */,
1F0ECBF42A68274400921E90 /* CDMarkdownKit */,
);
productName = NextcloudTalk;
productReference = 2C05747D1EDD9E8E00D9E7F2 /* NextcloudTalk.app */;
Expand All @@ -1682,6 +1694,7 @@
1F7AE07B29142E6A009F72AD /* NextcloudKit */,
1F45A1192A01D70E005FE87D /* SDWebImage */,
1F45A1242A01D8F7005FE87D /* SDWebImageSVGKitPlugin */,
1F0ECBF82A68277C00921E90 /* CDMarkdownKit */,
);
productName = ShareExtension;
productReference = 2C62AFA324C08845007E460A /* ShareExtension.appex */;
Expand All @@ -1705,6 +1718,7 @@
1F7AE07929142E62009F72AD /* NextcloudKit */,
1F45A11D2A01D719005FE87D /* SDWebImage */,
1F45A1222A01D8F1005FE87D /* SDWebImageSVGKitPlugin */,
1F0ECBF62A68277000921E90 /* CDMarkdownKit */,
);
productName = NotificationServiceExtension;
productReference = 2CC0014F24A1F0E900A20167 /* NotificationServiceExtension.appex */;
Expand Down Expand Up @@ -1792,6 +1806,7 @@
1F66B72D29FABD01003FB168 /* XCRemoteSwiftPackageReference "SwiftyAttributes" */,
1F45A1142A01D6EC005FE87D /* XCRemoteSwiftPackageReference "SDWebImage" */,
1F45A11F2A01D8BA005FE87D /* XCRemoteSwiftPackageReference "SDWebImageSVGKitPlugin" */,
1F0ECBF32A68274400921E90 /* XCRemoteSwiftPackageReference "CDMarkdownKit" */,
);
productRefGroup = 2C05747E1EDD9E8E00D9E7F2 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -2203,6 +2218,7 @@
2C5BFBED28895E6B00E75118 /* ObjectShareMessageTableViewCell.m in Sources */,
2C6E74462386D33200AE396C /* ReplyMessageView.m in Sources */,
DA8801A227A2DA00009EF248 /* UserProfileTableViewController.swift in Sources */,
1F0A1D442A5F1FA800A25433 /* SwiftMarkdownObjCBridge.swift in Sources */,
2C16A82C28E7284D00EDE523 /* NCButton.swift in Sources */,
DA75580F278EEA1000A48A1B /* SettingsTableViewController.swift in Sources */,
2CB6ACD22640814100D3D641 /* LocationMessageTableViewCell.m in Sources */,
Expand Down Expand Up @@ -2293,6 +2309,7 @@
2C444705265D641300DF1DBC /* NCUserDefaults.m in Sources */,
2C4446F5265D583200DF1DBC /* NCKeyChainController.m in Sources */,
2C62AFFA24C1BDA5007E460A /* NCChatMessage.m in Sources */,
1FC940BA2A5F21FD00FFFADE /* SwiftMarkdownObjCBridge.swift in Sources */,
2C4446D52658147900DF1DBC /* TalkAccount.m in Sources */,
2C1EF36D25505DCE007C9768 /* NCNavigationController.m in Sources */,
1FEDE3D0257D43AB00853F79 /* NCMessageFileParameter.m in Sources */,
Expand Down Expand Up @@ -2330,6 +2347,7 @@
2CC001C124A37AC500A20167 /* NCNotification.m in Sources */,
2C4446FD265D5DFA00DF1DBC /* ABContact.m in Sources */,
2C4446F8265D5A0700DF1DBC /* NotificationCenterNotifications.m in Sources */,
1FC940B92A5F21FC00FFFADE /* SwiftMarkdownObjCBridge.swift in Sources */,
2CB6ACDB2641483800D3D641 /* NCMessageLocationParameter.m in Sources */,
2C4446FB265D5C5700DF1DBC /* NCRoomParticipants.m in Sources */,
2CC1FF492818395E009F7288 /* NCDeckCardParameter.m in Sources */,
Expand Down Expand Up @@ -3098,6 +3116,14 @@
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
1F0ECBF32A68274400921E90 /* XCRemoteSwiftPackageReference "CDMarkdownKit" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/nextcloud-deps/CDMarkdownKit.git";
requirement = {
kind = revision;
revision = 5581a065b865a88accea1a305923d83e14d1beba;
};
};
1F45A1142A01D6EC005FE87D /* XCRemoteSwiftPackageReference "SDWebImage" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/SDWebImage/SDWebImage.git";
Expand Down Expand Up @@ -3157,6 +3183,21 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
1F0ECBF42A68274400921E90 /* CDMarkdownKit */ = {
isa = XCSwiftPackageProductDependency;
package = 1F0ECBF32A68274400921E90 /* XCRemoteSwiftPackageReference "CDMarkdownKit" */;
productName = CDMarkdownKit;
};
1F0ECBF62A68277000921E90 /* CDMarkdownKit */ = {
isa = XCSwiftPackageProductDependency;
package = 1F0ECBF32A68274400921E90 /* XCRemoteSwiftPackageReference "CDMarkdownKit" */;
productName = CDMarkdownKit;
};
1F0ECBF82A68277C00921E90 /* CDMarkdownKit */ = {
isa = XCSwiftPackageProductDependency;
package = 1F0ECBF32A68274400921E90 /* XCRemoteSwiftPackageReference "CDMarkdownKit" */;
productName = CDMarkdownKit;
};
1F45A1152A01D6EC005FE87D /* SDWebImage */ = {
isa = XCSwiftPackageProductDependency;
package = 1F45A1142A01D6EC005FE87D /* XCRemoteSwiftPackageReference "SDWebImage" */;
Expand Down
4 changes: 2 additions & 2 deletions NextcloudTalk/ChatMessageTableViewCell.m
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ - (MessageBodyTextView *)bodyTextView
- (void)setupForMessage:(NCChatMessage *)message withLastCommonReadMessage:(NSInteger)lastCommonRead
{
self.titleLabel.text = message.actorDisplayName;
self.bodyTextView.attributedText = message.parsedMessageForChat;
self.bodyTextView.attributedText = message.parsedMarkdownForChat;
self.messageId = message.messageId;
self.message = message;
NSDate *date = [[NSDate alloc] initWithTimeIntervalSince1970:message.timestamp];
Expand Down Expand Up @@ -318,7 +318,7 @@ - (void)setupForMessage:(NCChatMessage *)message withLastCommonReadMessage:(NSIn
NCChatMessage *parent = message.parent;
if (parent.message) {
self.quotedMessageView.actorLabel.text = ([parent.actorDisplayName isEqualToString:@""]) ? NSLocalizedString(@"Guest", nil) : parent.actorDisplayName;
self.quotedMessageView.messageLabel.text = parent.parsedMessageForChat.string;
self.quotedMessageView.messageLabel.text = parent.parsedMarkdownForChat.string;
self.quotedMessageView.highlighted = [parent isMessageFromUser:activeAccount.userId];
}

Expand Down
2 changes: 1 addition & 1 deletion NextcloudTalk/FileMessageTableViewCell.m
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ - (void)prepareForReuse
- (void)setupForMessage:(NCChatMessage *)message withLastCommonReadMessage:(NSInteger)lastCommonRead
{
self.titleLabel.text = message.actorDisplayName;
self.bodyTextView.attributedText = message.parsedMessageForChat;
self.bodyTextView.attributedText = message.parsedMessage;
self.messageId = message.messageId;
self.message = message;

Expand Down
2 changes: 1 addition & 1 deletion NextcloudTalk/GroupedChatMessageTableViewCell.m
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ - (void)prepareForReuse

- (void)setupForMessage:(NCChatMessage *)message withLastCommonReadMessage:(NSInteger)lastCommonRead
{
self.bodyTextView.attributedText = message.parsedMessageForChat;
self.bodyTextView.attributedText = message.parsedMarkdownForChat;
self.messageId = message.messageId;
self.message = message;

Expand Down
2 changes: 1 addition & 1 deletion NextcloudTalk/LocationMessageTableViewCell.m
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ - (void)prepareForReuse
- (void)setupForMessage:(NCChatMessage *)message withLastCommonReadMessage:(NSInteger)lastCommonRead
{
self.titleLabel.text = message.actorDisplayName;
self.bodyTextView.attributedText = message.parsedMessageForChat;
self.bodyTextView.attributedText = message.parsedMessage;
self.messageId = message.messageId;
self.message = message;

Expand Down
9 changes: 7 additions & 2 deletions NextcloudTalk/MessageBodyTextView.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,17 @@ @implementation MessageBodyTextView

- (instancetype)init
{
self = [super init];
// Use TextKit1 to have the background of code/syntax blocks span the whole line
if (@available(iOS 16.0, *)) {
self = [MessageBodyTextView textViewUsingTextLayoutManager:false];
} else {
self = [super init];
}

if (!self) {
return nil;
}

self.dataDetectorTypes = UIDataDetectorTypeAll;
self.textContainer.lineFragmentPadding = 0;
self.textContainerInset = UIEdgeInsetsZero;
Expand Down
3 changes: 2 additions & 1 deletion NextcloudTalk/NCChatMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ typedef void (^GetReferenceDataCompletionBlock)(NCChatMessage *message, NSDictio
- (NSString *)objectShareLink;
- (NSDictionary *)messageParameters;
- (NSMutableAttributedString *)parsedMessage;
- (NSMutableAttributedString *)parsedMessageForChat;
- (NSMutableAttributedString *)parsedMarkdown;
- (NSMutableAttributedString *)parsedMarkdownForChat;
- (NSMutableAttributedString *)systemMessageFormat;
- (NSString *)sendingMessage;
- (NCChatMessage *)parent;
Expand Down
45 changes: 31 additions & 14 deletions NextcloudTalk/NCChatMessage.m
Original file line number Diff line number Diff line change
Expand Up @@ -422,22 +422,22 @@ - (NSMutableAttributedString *)parsedMessage
if (!self.message) {
return nil;
}

NSString *originalMessage = self.file.contactName ? self.file.contactName : self.message;
NSString *parsedMessage = originalMessage;
NSError *error = nil;

NSRegularExpression *parameterRegex = [NSRegularExpression regularExpressionWithPattern:@"\\{([^}]+)\\}" options:NSRegularExpressionCaseInsensitive error:&error];
NSArray *matches = [parameterRegex matchesInString:originalMessage
options:0
range:NSMakeRange(0, [originalMessage length])];

// Find message parameters
NSMutableArray *parameters = [NSMutableArray new];
for (NSTextCheckingResult *match in matches) {
NSString* parameter = [originalMessage substringWithRange:match.range];
NSString *parameterKey = [[parameter stringByReplacingOccurrencesOfString:@"{" withString:@""]
stringByReplacingOccurrencesOfString:@"}" withString:@""];
stringByReplacingOccurrencesOfString:@"}" withString:@""];
NSDictionary *parameterDict = [[self messageParameters] objectForKey:parameterKey];
if (parameterDict) {
NCMessageParameter *messageParameter = [[NCMessageParameter alloc] initWithDictionary:parameterDict] ;
Expand All @@ -460,19 +460,19 @@ - (NSMutableAttributedString *)parsedMessage
[parameters addObject:messageParameter];
}
}

UIColor *defaultColor = [NCAppBranding chatForegroundColor];
UIColor *highlightedColor = [NCAppBranding elementColor];

NSMutableAttributedString *attributedMessage = [[NSMutableAttributedString alloc] initWithString:parsedMessage];
[attributedMessage addAttribute:NSForegroundColorAttributeName value:defaultColor range:NSMakeRange(0,parsedMessage.length)];
[attributedMessage addAttribute:NSForegroundColorAttributeName value:defaultColor range:NSMakeRange(0, parsedMessage.length)];

if (self.isEmojiMessage) {
[attributedMessage addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:36.0f] range:NSMakeRange(0,parsedMessage.length)];
[attributedMessage addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:36.0f] range:NSMakeRange(0, parsedMessage.length)];
} else {
[attributedMessage addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16.0f] range:NSMakeRange(0,parsedMessage.length)];
[attributedMessage addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16.0f] range:NSMakeRange(0, parsedMessage.length)];
}

for (NCMessageParameter *param in parameters) {
//Set color for mentions
if ([param.type isEqualToString:@"user"] || [param.type isEqualToString:@"guest"] ||
Expand All @@ -490,18 +490,35 @@ - (NSMutableAttributedString *)parsedMessage
}
}
}

return attributedMessage;
}

- (NSMutableAttributedString *)parsedMessageForChat
- (NSMutableAttributedString *)parsedMarkdown
{
NSMutableAttributedString *parsedMessage = self.parsedMessage;

if (!parsedMessage) {
return nil;
}

return [SwiftMarkdownObjCBridge parseMarkdownWithMarkdownString:parsedMessage];
}

- (NSMutableAttributedString *)parsedMarkdownForChat
{
// In some circumstances we want/need to hide the message in the chat, but still want to show it in other parts like the conversation list
if ([self getDeckCardUrlForReferenceProvider]) {
return nil;
}

return self.parsedMessage;
NSMutableAttributedString *parsedMessage = self.parsedMessage;

if (!parsedMessage) {
return nil;
}

return [SwiftMarkdownObjCBridge parseMarkdownWithMarkdownString:parsedMessage];
}

- (NSMutableAttributedString *)systemMessageFormat
Expand Down
20 changes: 16 additions & 4 deletions NextcloudTalk/NCChatViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -1623,7 +1623,7 @@ - (void)didPressForward:(NCChatMessage *)message {
if (message.isObjectShare) {
shareViewController = [[ShareViewController alloc] initToForwardObjectShareMessage:message fromChatViewController:self];
} else {
shareViewController = [[ShareViewController alloc] initToForwardMessage:message.parsedMessageForChat.string fromChatViewController:self];
shareViewController = [[ShareViewController alloc] initToForwardMessage:message.parsedMessage.string fromChatViewController:self];
}
shareViewController.delegate = self;
NCNavigationController *forwardMessageNC = [[NCNavigationController alloc] initWithRootViewController:shareViewController];
Expand All @@ -1641,7 +1641,7 @@ - (void)didPressResend:(NCChatMessage *)message {

- (void)didPressCopy:(NCChatMessage *)message {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
pasteboard.string = message.parsedMessageForChat.string;
pasteboard.string = message.parsedMessage.string;
[self.view makeToast:NSLocalizedString(@"Message copied", nil) duration:1.5 position:CSToastPositionCenter];
}

Expand Down Expand Up @@ -4049,14 +4049,26 @@ - (CGFloat)getCellHeightForMessage:(NCChatMessage *)message withWidth:(CGFloat)w
}

// Chat messages
NSMutableAttributedString *messageString = message.parsedMessageForChat;
NSMutableAttributedString *messageString = message.parsedMarkdownForChat;
width -= (message.isSystemMessage)? 80.0 : 30.0; // 4*right(10) + dateLabel(40) : 3*right(10)
if (message.poll) {
messageString = [[NSMutableAttributedString alloc] initWithString:message.poll.name];
[messageString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:[ObjectShareMessageTableViewCell defaultFontSize]] range:NSMakeRange(0,messageString.length)];
width -= kObjectShareMessageCellObjectTypeImageSize + 25; // 2*right(10) + left(5)
}
CGRect bodyBounds = [messageString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:NULL];

// Calculate the height of the message. "boundingRectWithSize" does not work correctly with markdown, so we use this
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:message.parsedMarkdownForChat];
CGRect targetBounding = CGRectMake(0, 0, width, CGFLOAT_MAX);
NSTextContainer *container = [[NSTextContainer alloc] initWithSize:targetBounding.size];
container.lineFragmentPadding = 0;

NSLayoutManager *manager = [[NSLayoutManager alloc] init];
[manager addTextContainer:container];
[textStorage addLayoutManager:manager];

[manager glyphRangeForBoundingRect:targetBounding inTextContainer:container];
CGRect bodyBounds = [manager usedRectForTextContainer:container];

CGFloat height = kChatCellAvatarHeight;
height += ceil(CGRectGetHeight(bodyBounds));
Expand Down
Loading

0 comments on commit 0a7b4f1

Please sign in to comment.