TUIReferenceMessageCell.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. //
  2. // TUIReferenceMessageCell.m
  3. // TUIChat
  4. //
  5. // Created by wyl on 2022/5/24.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIReferenceMessageCell.h"
  9. #import <TIMCommon/NSString+TUIEmoji.h>
  10. #import <TUICore/TUICore.h>
  11. #import <TUICore/TUIDarkModel.h>
  12. #import <TUICore/TUIThemeManager.h>
  13. #import <TUICore/UIView+TUILayout.h>
  14. #import "TUIFileMessageCellData.h"
  15. #import "TUIImageMessageCellData.h"
  16. #import "TUILinkCellData.h"
  17. #import "TUIMergeMessageCellData.h"
  18. #import "TUIReplyMessageCell.h"
  19. #import "TUIReplyMessageCellData.h"
  20. #import "TUITextMessageCellData.h"
  21. #import "TUIVideoMessageCellData.h"
  22. #import "TUIVoiceMessageCellData.h"
  23. #import "TUIFileReplyQuoteView.h"
  24. #import "TUIImageReplyQuoteView.h"
  25. #import "TUIMergeReplyQuoteView.h"
  26. #import "TUIReplyQuoteView.h"
  27. #import "TUITextMessageCell.h"
  28. #import "TUITextReplyQuoteView.h"
  29. #import "TUIVideoReplyQuoteView.h"
  30. #import "TUIVoiceReplyQuoteView.h"
  31. #ifndef CGFLOAT_CEIL
  32. #ifdef CGFLOAT_IS_DOUBLE
  33. #define CGFLOAT_CEIL(value) ceil(value)
  34. #else
  35. #define CGFLOAT_CEIL(value) ceilf(value)
  36. #endif
  37. #endif
  38. @interface TUIReferenceMessageCell () <UITextViewDelegate,TUITextViewDelegate>
  39. @property(nonatomic, strong) TUIReplyQuoteView *currentOriginView;
  40. @property(nonatomic, strong) NSMutableDictionary<NSString *, TUIReplyQuoteView *> *customOriginViewsCache;
  41. @end
  42. @implementation TUIReferenceMessageCell
  43. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  44. if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
  45. [self setupViews];
  46. }
  47. return self;
  48. }
  49. - (void)setupViews {
  50. [self setupContentTextView];
  51. [self.quoteView addSubview:self.senderLabel];
  52. [self.contentView addSubview:self.quoteView];
  53. self.bottomContainer = [[UIView alloc] init];
  54. [self.contentView addSubview:self.bottomContainer];
  55. }
  56. - (void)setupContentTextView {
  57. self.textView = [[TUITextView alloc] init];
  58. self.textView.backgroundColor = [UIColor clearColor];
  59. self.textView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
  60. self.textView.textContainer.lineFragmentPadding = 0;
  61. self.textView.scrollEnabled = NO;
  62. self.textView.editable = NO;
  63. self.textView.delegate = self;
  64. self.textView.tuiTextViewDelegate = self;
  65. self.textView.font = [UIFont systemFontOfSize:16.0];
  66. self.textView.textColor = TUIChatDynamicColor(@"chat_reference_message_content_text_color", @"#000000");
  67. [self.bubbleView addSubview:self.textView];
  68. }
  69. - (void)onLongPressTextViewMessage:(UITextView *)textView {
  70. if (self.delegate && [self.delegate respondsToSelector:@selector(onLongPressMessage:)]) {
  71. [self.delegate onLongPressMessage:self];
  72. }
  73. }
  74. - (void)fillWithData:(TUIReferenceMessageCellData *)data {
  75. [super fillWithData:data];
  76. self.referenceData = data;
  77. self.senderLabel.text = [NSString stringWithFormat:@"%@:", data.sender];
  78. self.selectContent = data.content;
  79. self.textView.attributedText = [data.content getFormatEmojiStringWithFont:self.textView.font emojiLocations:self.referenceData.emojiLocations];
  80. self.bottomContainer.hidden = CGSizeEqualToSize(data.bottomContainerSize, CGSizeZero);
  81. @weakify(self);
  82. [[RACObserve(data, originMessage) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(V2TIMMessage *originMessage) {
  83. @strongify(self);
  84. // tell constraints they need updating
  85. [self setNeedsUpdateConstraints];
  86. // update constraints now so we can animate the change
  87. [self updateConstraintsIfNeeded];
  88. [self layoutIfNeeded];
  89. }];
  90. // tell constraints they need updating
  91. [self setNeedsUpdateConstraints];
  92. // update constraints now so we can animate the change
  93. [self updateConstraintsIfNeeded];
  94. [self layoutIfNeeded];
  95. }
  96. + (BOOL)requiresConstraintBasedLayout {
  97. return YES;
  98. }
  99. // this is Apple's recommended place for adding/updating constraints
  100. - (void)updateConstraints {
  101. [super updateConstraints];
  102. [self updateUI:self.referenceData];
  103. [self layoutBottomContainer];
  104. }
  105. // Override
  106. - (void)notifyBottomContainerReadyOfData:(TUIMessageCellData *)cellData {
  107. NSDictionary *param = @{TUICore_TUIChatExtension_BottomContainer_CellData : self.referenceData};
  108. [TUICore raiseExtension:TUICore_TUIChatExtension_BottomContainer_ClassicExtensionID parentView:self.bottomContainer param:param];
  109. }
  110. - (void)updateUI:(TUIReferenceMessageCellData *)referenceData {
  111. self.currentOriginView = [self getCustomOriginView:referenceData.originCellData];
  112. [self hiddenAllCustomOriginViews:YES];
  113. self.currentOriginView.hidden = NO;
  114. referenceData.quoteData.supportForReply = NO;
  115. referenceData.quoteData.showRevokedOriginMessage = referenceData.showRevokedOriginMessage;
  116. [self.currentOriginView fillWithData:referenceData.quoteData];
  117. [self.textView mas_remakeConstraints:^(MASConstraintMaker *make) {
  118. make.leading.mas_equalTo(self.bubbleView.mas_leading).mas_offset(self.referenceData.textOrigin.x);
  119. make.top.mas_equalTo(self.bubbleView.mas_top).mas_offset(self.referenceData.textOrigin.y);
  120. make.size.mas_equalTo(self.referenceData.textSize);
  121. }];
  122. if (referenceData.direction == MsgDirectionIncoming) {
  123. self.textView.textColor = TUIChatDynamicColor(@"chat_reference_message_content_recv_text_color", @"#000000");
  124. self.senderLabel.textColor = TUIChatDynamicColor(@"chat_reference_message_quoteView_recv_text_color", @"#888888");
  125. self.quoteView.backgroundColor = TUIChatDynamicColor(@"chat_reference_message_quoteView_bg_color", @"#4444440c");
  126. } else {
  127. self.textView.textColor = TUIChatDynamicColor(@"chat_reference_message_content_text_color", @"#000000");
  128. self.senderLabel.textColor = TUIChatDynamicColor(@"chat_reference_message_quoteView_text_color", @"#888888");
  129. self.quoteView.backgroundColor = TUIChatDynamicColor(@"chat_reference_message_quoteView_bg_color", @"#4444440c");
  130. }
  131. if (referenceData.textColor) {
  132. self.textView.textColor = referenceData.textColor;
  133. }
  134. BOOL hasRiskContent = self.messageData.innerMessage.hasRiskContent;
  135. if (hasRiskContent ) {
  136. [self.securityStrikeView mas_remakeConstraints:^(MASConstraintMaker *make) {
  137. make.top.mas_equalTo(self.textView.mas_bottom);
  138. make.width.mas_equalTo(self.bubbleView);
  139. make.bottom.mas_equalTo(self.container);
  140. }];
  141. }
  142. [self.senderLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  143. make.leading.mas_equalTo(self.quoteView).mas_offset(6);
  144. make.top.mas_equalTo(self.quoteView).mas_offset(8);
  145. make.width.mas_equalTo(referenceData.senderSize.width);
  146. make.height.mas_equalTo(referenceData.senderSize.height);
  147. }];
  148. BOOL hideSenderLabel = (referenceData.originCellData.innerMessage.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) &&
  149. !referenceData.showRevokedOriginMessage;
  150. if (hideSenderLabel) {
  151. self.senderLabel.hidden = YES;
  152. } else {
  153. self.senderLabel.hidden = NO;
  154. }
  155. [self.quoteView mas_remakeConstraints:^(MASConstraintMaker *make) {
  156. if (self.referenceData.direction == MsgDirectionIncoming) {
  157. make.leading.mas_equalTo(self.bubbleView);
  158. }
  159. else {
  160. make.trailing.mas_equalTo(self.bubbleView);
  161. }
  162. make.top.mas_equalTo(self.container.mas_bottom).mas_offset(6);
  163. make.size.mas_equalTo(self.referenceData.quoteSize);
  164. }];
  165. if (self.referenceData.showMessageModifyReplies) {
  166. [self.messageModifyRepliesButton mas_remakeConstraints:^(MASConstraintMaker *make) {
  167. if (self.referenceData.direction == MsgDirectionIncoming) {
  168. make.leading.mas_equalTo(self.quoteView.mas_leading);
  169. }
  170. else {
  171. make.trailing.mas_equalTo(self.quoteView.mas_trailing);
  172. }
  173. make.top.mas_equalTo(self.quoteView.mas_bottom).mas_offset(3);
  174. make.size.mas_equalTo(self.messageModifyRepliesButton.frame.size);
  175. }];
  176. }
  177. [self.currentOriginView mas_remakeConstraints:^(MASConstraintMaker *make) {
  178. if (hideSenderLabel) {
  179. make.leading.mas_equalTo(self.quoteView).mas_offset(6);
  180. make.top.mas_equalTo(self.quoteView).mas_offset(8);
  181. make.trailing.mas_lessThanOrEqualTo(self.quoteView.mas_trailing);
  182. make.height.mas_equalTo(self.referenceData.quotePlaceholderSize);
  183. }
  184. else {
  185. make.leading.mas_equalTo(self.senderLabel.mas_trailing).mas_offset(3);
  186. make.top.mas_equalTo(self.senderLabel.mas_top).mas_offset(1);
  187. make.trailing.mas_lessThanOrEqualTo(self.quoteView.mas_trailing);
  188. make.height.mas_equalTo(self.referenceData.quotePlaceholderSize);
  189. }
  190. }];
  191. }
  192. - (TUIReplyQuoteView *)getCustomOriginView:(TUIMessageCellData *)originCellData {
  193. NSString *reuseId = originCellData ? NSStringFromClass(originCellData.class) : NSStringFromClass(TUITextMessageCellData.class);
  194. TUIReplyQuoteView *view = nil;
  195. BOOL reuse = NO;
  196. BOOL hasRiskContent = originCellData.innerMessage.hasRiskContent;
  197. if (hasRiskContent) {
  198. reuseId = @"hasRiskContent";
  199. }
  200. if ([self.customOriginViewsCache.allKeys containsObject:reuseId]) {
  201. view = [self.customOriginViewsCache objectForKey:reuseId];
  202. reuse = YES;
  203. }
  204. if (hasRiskContent && view == nil){
  205. TUITextReplyQuoteView *quoteView = [[TUITextReplyQuoteView alloc] init];
  206. view = quoteView;
  207. }
  208. if (view == nil) {
  209. Class class = [originCellData getReplyQuoteViewClass];
  210. if (class) {
  211. view = [[class alloc] init];
  212. }
  213. }
  214. if (view == nil) {
  215. TUITextReplyQuoteView *quoteView = [[TUITextReplyQuoteView alloc] init];
  216. view = quoteView;
  217. }
  218. if ([view isKindOfClass:[TUITextReplyQuoteView class]]) {
  219. TUITextReplyQuoteView *quoteView = (TUITextReplyQuoteView *)view;
  220. if (self.referenceData.direction == MsgDirectionIncoming) {
  221. quoteView.textLabel.textColor = TUIChatDynamicColor(@"chat_reference_message_quoteView_recv_text_color", @"#888888");
  222. } else {
  223. quoteView.textLabel.textColor = TUIChatDynamicColor(@"chat_reference_message_quoteView_text_color", @"#888888");
  224. }
  225. } else if ([view isKindOfClass:[TUIMergeReplyQuoteView class]]) {
  226. TUIMergeReplyQuoteView *quoteView = (TUIMergeReplyQuoteView *)view;
  227. if (self.referenceData.direction == MsgDirectionIncoming) {
  228. quoteView.titleLabel.textColor = TUIChatDynamicColor(@"chat_reference_message_quoteView_recv_text_color", @"#888888");
  229. } else {
  230. quoteView.titleLabel.textColor = TUIChatDynamicColor(@"chat_reference_message_quoteView_text_color", @"#888888");
  231. }
  232. }
  233. if (!reuse) {
  234. [self.customOriginViewsCache setObject:view forKey:reuseId];
  235. [self.quoteView addSubview:view];
  236. }
  237. view.hidden = YES;
  238. return view;
  239. }
  240. - (void)hiddenAllCustomOriginViews:(BOOL)hidden {
  241. [self.customOriginViewsCache enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, TUIReplyQuoteView *_Nonnull obj, BOOL *_Nonnull stop) {
  242. obj.hidden = hidden;
  243. [obj reset];
  244. }];
  245. }
  246. - (void)layoutSubviews {
  247. [super layoutSubviews];
  248. }
  249. - (void)layoutBottomContainer {
  250. if (CGSizeEqualToSize(self.referenceData.bottomContainerSize, CGSizeZero)) {
  251. return;
  252. }
  253. CGSize size = self.referenceData.bottomContainerSize;
  254. CGFloat topMargin = self.bubbleView.mm_maxY + self.nameLabel.mm_h + 6;
  255. [self.bottomContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
  256. if (!self.messageModifyRepliesButton.isHidden){
  257. make.top.mas_equalTo(self.messageModifyRepliesButton.mas_bottom).mas_offset(8);
  258. }
  259. else {
  260. make.top.mas_equalTo(self.quoteView.mas_bottom).mas_offset(8);
  261. }
  262. make.size.mas_equalTo(size);
  263. if (self.referenceData.direction == MsgDirectionOutgoing) {
  264. make.trailing.mas_equalTo(self.container);
  265. }
  266. else {
  267. make.leading.mas_equalTo(self.container);
  268. }
  269. }];
  270. if (!self.quoteView.hidden) {
  271. CGRect oldRect = self.quoteView.frame;
  272. CGRect newRect = CGRectMake(oldRect.origin.x, CGRectGetMaxY(self.bottomContainer.frame) + 5, oldRect.size.width, oldRect.size.height);
  273. self.quoteView.frame = newRect;
  274. }
  275. if (!self.messageModifyRepliesButton.hidden) {
  276. CGRect oldRect = self.messageModifyRepliesButton.frame;
  277. CGRect newRect = CGRectMake(oldRect.origin.x, CGRectGetMaxY(self.quoteView.frame), oldRect.size.width, oldRect.size.height);
  278. self.messageModifyRepliesButton.frame = newRect;
  279. }
  280. }
  281. - (UILabel *)senderLabel {
  282. if (_senderLabel == nil) {
  283. _senderLabel = [[UILabel alloc] init];
  284. _senderLabel.text = @"harvy:";
  285. _senderLabel.font = [UIFont systemFontOfSize:12.0];
  286. _senderLabel.textColor = TUIChatDynamicColor(@"chat_reference_message_sender_text_color", @"#888888");
  287. }
  288. return _senderLabel;
  289. }
  290. - (UIView *)quoteView {
  291. if (_quoteView == nil) {
  292. _quoteView = [[UIView alloc] init];
  293. _quoteView.backgroundColor = TUIChatDynamicColor(@"chat_reference_message_quoteView_bg_color", @"#4444440c");
  294. _quoteView.layer.cornerRadius = 4.0;
  295. _quoteView.layer.masksToBounds = YES;
  296. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(quoteViewOnTap)];
  297. [_quoteView addGestureRecognizer:tap];
  298. }
  299. return _quoteView;
  300. }
  301. - (void)quoteViewOnTap {
  302. if (self.delegate && [self.delegate respondsToSelector:@selector(onSelectMessage:)]) {
  303. [self.delegate onSelectMessage:self];
  304. }
  305. }
  306. - (NSMutableDictionary *)customOriginViewsCache {
  307. if (_customOriginViewsCache == nil) {
  308. _customOriginViewsCache = [[NSMutableDictionary alloc] init];
  309. }
  310. return _customOriginViewsCache;
  311. }
  312. - (void)textViewDidChangeSelection:(UITextView *)textView {
  313. NSAttributedString *selectedString = [textView.attributedText attributedSubstringFromRange:textView.selectedRange];
  314. if (self.selectAllContentContent && selectedString) {
  315. if (selectedString.length == textView.attributedText.length) {
  316. self.selectAllContentContent(YES);
  317. } else {
  318. self.selectAllContentContent(NO);
  319. }
  320. }
  321. if (selectedString.length > 0) {
  322. NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
  323. [attributedString appendAttributedString:selectedString];
  324. NSUInteger offsetLocation = 0;
  325. for (NSDictionary *emojiLocation in self.referenceData.emojiLocations) {
  326. NSValue *key = emojiLocation.allKeys.firstObject;
  327. NSAttributedString *originStr = emojiLocation[key];
  328. NSRange currentRange = [key rangeValue];
  329. currentRange.location += offsetLocation;
  330. if (currentRange.location >= textView.selectedRange.location) {
  331. currentRange.location -= textView.selectedRange.location;
  332. if (currentRange.location + currentRange.length <= attributedString.length) {
  333. [attributedString replaceCharactersInRange:currentRange withAttributedString:originStr];
  334. offsetLocation += originStr.length - currentRange.length;
  335. }
  336. }
  337. }
  338. self.selectContent = attributedString.string;
  339. } else {
  340. self.selectContent = nil;
  341. }
  342. }
  343. #pragma mark - TUIMessageCellProtocol
  344. + (CGFloat)getHeight:(TUIMessageCellData *)data withWidth:(CGFloat)width {
  345. NSAssert([data isKindOfClass:TUIReferenceMessageCellData.class], @"data must be kind of TUIReferenceMessageCellData");
  346. TUIReferenceMessageCellData *referenceCellData = (TUIReferenceMessageCellData *)data;
  347. CGFloat cellHeight = [super getHeight:data withWidth:width];
  348. cellHeight += referenceCellData.quoteSize.height + referenceCellData.bottomContainerSize.height;
  349. cellHeight += kScale375(6);
  350. return cellHeight;
  351. }
  352. + (CGSize)getContentSize:(TUIMessageCellData *)data {
  353. NSAssert([data isKindOfClass:TUIReferenceMessageCellData.class], @"data must be kind of TUIReferenceMessageCellData");
  354. TUIReferenceMessageCellData *referenceCellData = (TUIReferenceMessageCellData *)data;
  355. CGFloat quoteHeight = 0;
  356. CGFloat quoteWidth = 0;
  357. CGFloat quoteMaxWidth = TReplyQuoteView_Max_Width;
  358. CGFloat quotePlaceHolderMarginWidth = 12;
  359. // Calculate the size of label which displays the sender's displayname
  360. CGSize senderSize = [@"0" sizeWithAttributes:@{NSFontAttributeName : [UIFont boldSystemFontOfSize:12.0]}];
  361. CGRect senderRect = [[NSString stringWithFormat:@"%@:",referenceCellData.sender] boundingRectWithSize:CGSizeMake(quoteMaxWidth, senderSize.height)
  362. options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
  363. attributes:@{NSFontAttributeName : [UIFont boldSystemFontOfSize:12.0]}
  364. context:nil];
  365. // Calculate the size of customize quote placeholder view
  366. CGSize placeholderSize = [referenceCellData quotePlaceholderSizeWithType:referenceCellData.originMsgType data:referenceCellData.quoteData];
  367. // Calculate the size of revoke string
  368. CGRect messageRevokeRect = CGRectZero;
  369. bool showRevokeStr = (referenceCellData.originCellData.innerMessage.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) &&
  370. !referenceCellData.showRevokedOriginMessage;
  371. if (showRevokeStr) {
  372. NSString *msgRevokeStr = TIMCommonLocalizableString(TUIKitReferenceOriginMessageRevoke);
  373. messageRevokeRect = [msgRevokeStr boundingRectWithSize:CGSizeMake(quoteMaxWidth, senderSize.height)
  374. options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
  375. attributes:@{NSFontAttributeName : [UIFont boldSystemFontOfSize:12.0]}
  376. context:nil];
  377. }
  378. // Calculate the size of label which displays the content of replying the original message
  379. NSAttributedString *attributeString = [referenceCellData.content getFormatEmojiStringWithFont:[UIFont systemFontOfSize:16.0] emojiLocations:nil];
  380. CGRect replyContentRect = [attributeString boundingRectWithSize:CGSizeMake(TTextMessageCell_Text_Width_Max, MAXFLOAT)
  381. options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
  382. context:nil];
  383. CGSize size = CGSizeMake(CGFLOAT_CEIL(replyContentRect.size.width), CGFLOAT_CEIL(replyContentRect.size.height));
  384. referenceCellData.textSize = size;
  385. referenceCellData.textOrigin = CGPointMake(referenceCellData.cellLayout.bubbleInsets.left,
  386. referenceCellData.cellLayout.bubbleInsets.top + [TUIBubbleMessageCell getBubbleTop:referenceCellData]);
  387. size.height += referenceCellData.cellLayout.bubbleInsets.top + referenceCellData.cellLayout.bubbleInsets.bottom;
  388. size.width += referenceCellData.cellLayout.bubbleInsets.left + referenceCellData.cellLayout.bubbleInsets.right;
  389. if (referenceCellData.direction == MsgDirectionIncoming) {
  390. size.height = MAX(size.height, TUIBubbleMessageCell.incommingBubble.size.height);
  391. } else {
  392. size.height = MAX(size.height, TUIBubbleMessageCell.outgoingBubble.size.height);
  393. }
  394. BOOL hasRiskContent = referenceCellData.innerMessage.hasRiskContent;
  395. if (hasRiskContent) {
  396. size.width = MAX(size.width, 200);// width must more than TIMCommonLocalizableString(TUIKitMessageTypeSecurityStrike)
  397. size.height += kTUISecurityStrikeViewTopLineMargin;
  398. size.height += kTUISecurityStrikeViewTopLineToBottom;
  399. }
  400. quoteWidth = senderRect.size.width;
  401. quoteWidth += placeholderSize.width;
  402. quoteWidth += (quotePlaceHolderMarginWidth * 2);
  403. if (showRevokeStr) {
  404. quoteWidth = messageRevokeRect.size.width;
  405. }
  406. quoteHeight = MAX(senderRect.size.height, placeholderSize.height);
  407. quoteHeight += (8 + 8);
  408. referenceCellData.senderSize = CGSizeMake(senderRect.size.width, senderRect.size.height);
  409. referenceCellData.quotePlaceholderSize = placeholderSize;
  410. // self.replyContentSize = CGSizeMake(replyContentRect.size.width, replyContentRect.size.height);
  411. referenceCellData.quoteSize = CGSizeMake(quoteWidth, quoteHeight);
  412. return size;
  413. }
  414. @end