| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- //
- // TUITextMessageCell.m
- // UIKit
- //
- // Created by annidyfeng on 2019/5/30.
- // Copyright © 2023 Tencent. All rights reserved.
- //
- #import "TUITextMessageCell.h"
- #import <TIMCommon/TIMCommonModel.h>
- #import <TIMCommon/TIMDefine.h>
- #import <TUICore/TUICore.h>
- #import <TUICore/TUIGlobalization.h>
- #import "TUIFaceView.h"
- #import <TUICore/UIColor+TUIHexColor.h>
- #ifndef CGFLOAT_CEIL
- #ifdef CGFLOAT_IS_DOUBLE
- #define CGFLOAT_CEIL(value) ceil(value)
- #else
- #define CGFLOAT_CEIL(value) ceilf(value)
- #endif
- #endif
- @interface TUITextMessageCell ()<TUITextViewDelegate>
- @end
- @implementation TUITextMessageCell
- - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
- self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
- if (self) {
- self.textView = [[TUITextView alloc] init];
- self.textView.backgroundColor = [UIColor clearColor];
- self.textView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
- self.textView.textContainer.lineFragmentPadding = 0;
- self.textView.scrollEnabled = NO;
- self.textView.editable = NO;
- self.textView.delegate = self;
- self.textView.tuiTextViewDelegate = self;
- self.bubbleView.userInteractionEnabled = YES;
- [self.bubbleView addSubview:self.textView];
-
- self.bottomContainer = [[UIView alloc] init];
- [self.contentView addSubview:self.bottomContainer];
- self.voiceReadPoint = [[UIImageView alloc] init];
- self.voiceReadPoint.backgroundColor = [UIColor redColor];
- self.voiceReadPoint.frame = CGRectMake(0, 0, 5, 5);
- self.voiceReadPoint.hidden = YES;
- [self.voiceReadPoint.layer setCornerRadius:self.voiceReadPoint.frame.size.width / 2];
- [self.voiceReadPoint.layer setMasksToBounds:YES];
- [self.bubbleView addSubview:self.voiceReadPoint];
- }
- return self;
- }
- - (void)prepareForReuse {
- [super prepareForReuse];
- for (UIView *view in self.bottomContainer.subviews) {
- [view removeFromSuperview];
- }
- }
- - (void)onLongPressTextViewMessage:(UITextView *)textView {
- if (self.delegate && [self.delegate respondsToSelector:@selector(onLongPressMessage:)]) {
- [self.delegate onLongPressMessage:self];
- }
- }
- // Override
- - (void)notifyBottomContainerReadyOfData:(TUIMessageCellData *)cellData {
- NSDictionary *param = @{TUICore_TUIChatExtension_BottomContainer_CellData : self.textData};
- [TUICore raiseExtension:TUICore_TUIChatExtension_BottomContainer_ClassicExtensionID parentView:self.bottomContainer param:param];
- }
- - (void)fillWithData:(TUITextMessageCellData *)data {
- // set data
- [super fillWithData:data];
- self.textData = data;
-
- self.selectContent = data.content;
- self.voiceReadPoint.hidden = !data.showUnreadPoint;
- self.bottomContainer.hidden = CGSizeEqualToSize(data.bottomContainerSize, CGSizeZero);
-
- UIColor *textColor = self.class.incommingTextColor;
- UIFont *textFont = self.class.incommingTextFont;
- if (data.direction == MsgDirectionIncoming) {
- textColor = self.class.incommingTextColor;
- textFont = self.class.incommingTextFont;
- } else {
- textColor = self.class.outgoingTextColor;
- textFont = self.class.outgoingTextFont;
- }
- self.textView.attributedText = [data getContentAttributedString:textFont];
- self.textView.textColor = textColor;
- self.textView.font = textFont;
- // tell constraints they need updating
- [self setNeedsUpdateConstraints];
- // update constraints now so we can animate the change
- [self updateConstraintsIfNeeded];
- [self layoutIfNeeded];
-
- }
- + (BOOL)requiresConstraintBasedLayout {
- return YES;
- }
- // this is Apple's recommended place for adding/updating constraints
- - (void)updateConstraints {
-
- [super updateConstraints];
-
- [self.textView mas_remakeConstraints:^(MASConstraintMaker *make) {
- make.leading.mas_equalTo(self.bubbleView.mas_leading).mas_offset(self.textData.textOrigin.x);
- make.top.mas_equalTo(self.bubbleView.mas_top).mas_offset(self.textData.textOrigin.y);
- make.size.mas_equalTo(self.textData.textSize);
- }];
- if (self.voiceReadPoint.hidden == NO) {
- [self.voiceReadPoint mas_remakeConstraints:^(MASConstraintMaker *make) {
- make.top.mas_equalTo(self.bubbleView);
- make.leading.mas_equalTo(self.bubbleView.mas_trailing).mas_offset(1);
- make.size.mas_equalTo(CGSizeMake(5, 5));
- }];
- }
-
- BOOL hasRiskContent = self.messageData.innerMessage.hasRiskContent;
- if (hasRiskContent ) {
- [self.securityStrikeView mas_remakeConstraints:^(MASConstraintMaker *make) {
- make.top.mas_equalTo(self.textView.mas_bottom);
- make.width.mas_equalTo(self.bubbleView);
- make.bottom.mas_equalTo(self.container).mas_offset(- self.messageData.messageContainerAppendSize.height);
- }];
- }
- [self layoutBottomContainer];
-
- }
- - (void)layoutBottomContainer {
- if (CGSizeEqualToSize(self.textData.bottomContainerSize, CGSizeZero)) {
- return;
- }
- CGSize size = self.textData.bottomContainerSize;
- [self.bottomContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
- if (self.textData.direction == MsgDirectionIncoming) {
- make.leading.mas_equalTo(self.container.mas_leading);
- }
- else {
- make.trailing.mas_equalTo(self.container.mas_trailing);
- }
- make.top.mas_equalTo(self.container.mas_bottom).offset(6);
- make.size.mas_equalTo(size);
- }];
- CGFloat repliesBtnTextWidth = self.messageModifyRepliesButton.frame.size.width;
- if (!self.messageModifyRepliesButton.hidden) {
- [self.messageModifyRepliesButton mas_remakeConstraints:^(MASConstraintMaker *make) {
- if (self.textData.direction == MsgDirectionIncoming) {
- make.leading.mas_equalTo(self.container.mas_leading);
- } else {
- make.trailing.mas_equalTo(self.container.mas_trailing);
- }
- make.top.mas_equalTo(self.bottomContainer.mas_bottom);
- make.size.mas_equalTo(CGSizeMake(repliesBtnTextWidth + 10, 30));
- }];
- }
- }
- - (void)textViewDidChangeSelection:(UITextView *)textView {
- NSAttributedString *selectedString = [textView.attributedText attributedSubstringFromRange:textView.selectedRange];
- if (self.selectAllContentContent && selectedString.length > 0) {
- if (selectedString.length == textView.attributedText.length) {
- self.selectAllContentContent(YES);
- } else {
- self.selectAllContentContent(NO);
- }
- }
- if (selectedString.length > 0) {
- NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
- [attributedString appendAttributedString:selectedString];
- NSUInteger offsetLocation = 0;
- for (NSDictionary *emojiLocation in self.textData.emojiLocations) {
- NSValue *key = emojiLocation.allKeys.firstObject;
- NSAttributedString *originStr = emojiLocation[key];
- NSRange currentRange = [key rangeValue];
- /**
- * After each emoji is replaced, the length of the string will change, and the actual location of the emoji will also change accordingly.
- */
- currentRange.location += offsetLocation;
- if (currentRange.location >= textView.selectedRange.location) {
- currentRange.location -= textView.selectedRange.location;
- if (currentRange.location + currentRange.length <= attributedString.length) {
- [attributedString replaceCharactersInRange:currentRange withAttributedString:originStr];
- offsetLocation += originStr.length - currentRange.length;
- }
- }
- }
- self.selectContent = attributedString.string;
- } else {
- self.selectContent = nil;
- [[NSNotificationCenter defaultCenter] postNotificationName:@"kTUIChatPopMenuWillHideNotification" object:nil];
- }
- }
- #pragma mark - TUIMessageCellProtocol
- + (CGFloat)getEstimatedHeight:(TUIMessageCellData *)data {
- return 60.f;
- }
- + (CGFloat)getHeight:(TUIMessageCellData *)data withWidth:(CGFloat)width {
- CGFloat height = [super getHeight:data withWidth:width];
- if (data.bottomContainerSize.height > 0) {
- height += data.bottomContainerSize.height + kScale375(6);
- }
- return height;
- }
- static CGSize gMaxTextSize;
- + (void)setMaxTextSize:(CGSize)maxTextSz {
- gMaxTextSize = maxTextSz;
- }
- + (CGSize)getContentSize:(TUIMessageCellData *)data {
- NSAssert([data isKindOfClass:TUITextMessageCellData.class], @"data must be kind of TUITextMessageCellData");
- TUITextMessageCellData *textCellData = (TUITextMessageCellData *)data;
- NSAttributedString *attributeString = [textCellData getContentAttributedString:
- data.direction == MsgDirectionIncoming ? self.incommingTextFont : self.outgoingTextFont];
-
- if (CGSizeEqualToSize(gMaxTextSize, CGSizeZero)) {
- gMaxTextSize = CGSizeMake(TTextMessageCell_Text_Width_Max, MAXFLOAT);
- }
- CGSize contentSize = [textCellData getContentAttributedStringSize:attributeString maxTextSize:gMaxTextSize];
- textCellData.textSize = contentSize;
- CGPoint textOrigin = CGPointMake(textCellData.cellLayout.bubbleInsets.left,
- textCellData.cellLayout.bubbleInsets.top);
- textCellData.textOrigin = textOrigin;
-
- CGFloat height = contentSize.height;
- CGFloat width = contentSize.width;
- height += textCellData.cellLayout.bubbleInsets.top;
- height += textCellData.cellLayout.bubbleInsets.bottom;
-
- width += textCellData.cellLayout.bubbleInsets.left;
- width += textCellData.cellLayout.bubbleInsets.right;
- if (textCellData.direction == MsgDirectionIncoming) {
- height = MAX(height, TUIBubbleMessageCell.incommingBubble.size.height);
- } else {
- height = MAX(height, TUIBubbleMessageCell.outgoingBubble.size.height);
- }
-
- BOOL hasRiskContent = textCellData.innerMessage.hasRiskContent;
- if (hasRiskContent) {
- width = MAX(width, 200);// width must more than TIMCommonLocalizableString(TUIKitMessageTypeSecurityStrike)
- height += kTUISecurityStrikeViewTopLineMargin;
- height += kTUISecurityStrikeViewTopLineToBottom;
- }
- CGSize size = CGSizeMake(width, height);
- return size;
- }
- @end
- @implementation TUITextMessageCell (TUILayoutConfiguration)
- + (void)initialize {
- [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(onThemeChanged) name:TUIDidApplyingThemeChangedNotfication object:nil];
- }
- static UIColor *gOutgoingTextColor;
- + (UIColor *)outgoingTextColor {
- if (!gOutgoingTextColor) {
- gOutgoingTextColor = TUIChatDynamicColor(@"chat_text_message_send_text_color", @"#000000");
- }
- return gOutgoingTextColor;
- }
- + (void)setOutgoingTextColor:(UIColor *)outgoingTextColor {
- gOutgoingTextColor = outgoingTextColor;
- }
- static UIFont *gOutgoingTextFont;
- + (UIFont *)outgoingTextFont {
- if (!gOutgoingTextFont) {
- gOutgoingTextFont = [UIFont systemFontOfSize:16];
- }
- return gOutgoingTextFont;
- }
- + (void)setOutgoingTextFont:(UIFont *)outgoingTextFont {
- gOutgoingTextFont = outgoingTextFont;
- }
- static UIColor *gIncommingTextColor;
- + (UIColor *)incommingTextColor {
- if (!gIncommingTextColor) {
- gIncommingTextColor = TUIChatDynamicColor(@"chat_text_message_receive_text_color", @"#000000");
- }
- return gIncommingTextColor;
- }
- + (void)setIncommingTextColor:(UIColor *)incommingTextColor {
- gIncommingTextColor = incommingTextColor;
- }
- static UIFont *gIncommingTextFont;
- + (UIFont *)incommingTextFont {
- if (!gIncommingTextFont) {
- gIncommingTextFont = [UIFont systemFontOfSize:16];
- }
- return gIncommingTextFont;
- }
- + (void)setIncommingTextFont:(UIFont *)incommingTextFont {
- gIncommingTextFont = incommingTextFont;
- }
- + (void)onThemeChanged {
- gOutgoingTextColor = nil;
- gIncommingTextColor = nil;
- }
- @end
|