Files
TIMSDK/iOS/TUIKit/TUIChat/Cell/CellUI/Base/TUIMessageCell.m
T
2022-01-25 21:03:58 +08:00

329 lines
13 KiB
Objective-C

//
// TUIMessageCell.m
// UIKit
//
// Created by kennethmiao on 2018/9/17.
// Copyright © 2018年 Tencent. All rights reserved.
//
#import "TUIMessageCell.h"
#import "TUIDefine.h"
#import "TUITool.h"
#import "TUIThemeManager.h"
@interface TUIMessageCell() <CAAnimationDelegate>
@property (nonatomic, strong) TUIMessageCellData *messageData;
@end
@implementation TUIMessageCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if(self) {
self.backgroundColor = [UIColor clearColor];
//head
_avatarView = [[UIImageView alloc] init];
_avatarView.contentMode = UIViewContentModeScaleAspectFit;
[self.contentView addSubview:_avatarView];
UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSelectMessageAvatar:)];
[_avatarView addGestureRecognizer:tap1];
UILongPressGestureRecognizer *tap2 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongSelectMessageAvatar:)];
[_avatarView addGestureRecognizer:tap2];
[_avatarView setUserInteractionEnabled:YES];
//nameLabel
_nameLabel = [[UILabel alloc] init];
_nameLabel.font = [UIFont systemFontOfSize:13];
_nameLabel.textColor = [UIColor d_systemGrayColor];
[self.contentView addSubview:_nameLabel];
//container
_container = [[UIView alloc] init];
_container.backgroundColor = [UIColor clearColor];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSelectMessage:)];
[_container addGestureRecognizer:tap];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPress:)];
[_container addGestureRecognizer:longPress];
[self.contentView addSubview:_container];
//indicator
_indicator = [[UIActivityIndicatorView alloc] init];
_indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.contentView addSubview:_indicator];
//error
_retryView = [[UIImageView alloc] init];
_retryView.userInteractionEnabled = YES;
UITapGestureRecognizer *resendTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onRetryMessage:)];
[_retryView addGestureRecognizer:resendTap];
[self.contentView addSubview:_retryView];
//已读label,由于 indicator 和 error,所以默认隐藏,消息发送成功后进行显示
_readReceiptLabel = [[UILabel alloc] init];
_readReceiptLabel.hidden = YES;
_readReceiptLabel.font = [UIFont systemFontOfSize:12];
_readReceiptLabel.textColor = [UIColor d_systemGrayColor];
_readReceiptLabel.lineBreakMode = NSLineBreakByCharWrapping;
[self.contentView addSubview:_readReceiptLabel];
// selectedIcon
_selectedIcon = [[UIImageView alloc] init];
[self.contentView addSubview:_selectedIcon];
// selectedView
_selectedView = [UIButton buttonWithType:UIButtonTypeCustom];
_selectedView.backgroundColor = [UIColor clearColor];
[_selectedView addTarget:self action:@selector(onSelectMessage:) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:_selectedView];
// timeLabel
_timeLabel = [[UILabel alloc] init];
_timeLabel.textColor = [UIColor darkGrayColor];
_timeLabel.font = [UIFont systemFontOfSize:11.0];
[self.contentView addSubview:_timeLabel];
self.selectionStyle = UITableViewCellSelectionStyleNone;
self.backgroundColor = TUIChatDynamicColor(@"chat_controller_bg_color", @"#FFFFFF");
self.contentView.backgroundColor = TUIChatDynamicColor(@"chat_controller_bg_color", @"#FFFFFF");
}
return self;
}
- (void)fillWithData:(TUIMessageCellData *)data
{
[super fillWithData:data];
self.messageData = data;
[self.avatarView setImage:data.avatarImage];
@weakify(self)
[[[RACObserve(data, avatarUrl) takeUntil:self.rac_prepareForReuseSignal] ignore:nil] subscribeNext:^(NSURL *url) {
@strongify(self)
[self.avatarView sd_setImageWithURL:url placeholderImage:self.messageData.avatarImage];
}];
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = data.cellLayout.avatarSize.height / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
//set data
self.nameLabel.text = data.name;
self.nameLabel.textColor = data.nameColor;
self.nameLabel.font = data.nameFont;
//由于tableView的刷新策略,导致部分情况下可能会出现未读label未显示的bug。原因是因为在label显示时,内容为空。
//label内容的变化不会引起tableView的刷新,但是hiddend状态的变化会引起tableView刷新。
//所以未读标签选择直接赋值,而不是在发送成功时赋值。显示时机由hidden属性控制。
self.readReceiptLabel.text = self.messageData.innerMessage.isPeerRead ? TUIKitLocalizableString(Read): TUIKitLocalizableString(Unread);
if(data.status == Msg_Status_Fail){
[_indicator stopAnimating];
self.retryView.image = [UIImage imageNamed:TUIChatImagePath(@"msg_error")];
_readReceiptLabel.hidden = YES;
} else {
if (data.status == Msg_Status_Sending_2) {
[_indicator startAnimating];
_readReceiptLabel.hidden = YES;
}
else if(data.status == Msg_Status_Succ){
[_indicator stopAnimating];
//发送成功,说明 indicator 和 error 已不会显示在 label 中,可以开始显示已读回执label
if(self.messageData.direction == MsgDirectionOutgoing
&& self.messageData.showReadReceipt
&& self.messageData.innerMessage.userID.length > 0){//只对发送的消息进行label显示。
_readReceiptLabel.hidden = NO;
}
}
else if(data.status == Msg_Status_Sending){
[_indicator stopAnimating];
_readReceiptLabel.hidden = YES;
}
self.retryView.image = nil;
}
NSString *imageName = (data.showCheckBox && data.selected) ? TUICoreImagePath(@"icon_select_selected") : TUICoreImagePath(@"icon_select_normal");
self.selectedIcon.image = [UIImage imageNamed:imageName];
_timeLabel.text = [TUITool convertDateToStr:data.innerMessage.timestamp];
// 高亮文本 - 此处异步是为了让其执行顺序与子类一致
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf highlightWhenMatchKeyword:data.highlightKeyword];
});
}
- (void)highlightWhenMatchKeyword:(NSString *)keyword
{
static NSString * const key = @"highlightAnimation";
if (keyword && keyword.length) {
if (self.highlightAnimating) {
return;
}
self.highlightAnimating = YES;
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"backgroundColor"];
animation.repeatCount = 3;
animation.values = @[
(id)[[UIColor orangeColor] colorWithAlphaComponent:0.2].CGColor,
(id)[[UIColor orangeColor] colorWithAlphaComponent:0.5].CGColor,
(id)[[UIColor orangeColor] colorWithAlphaComponent:0.2].CGColor,
];
animation.duration = 0.5;
animation.removedOnCompletion = YES;
animation.delegate = self;
[self.highlightAnimateView.layer addAnimation:animation forKey:key];
} else {
[self.highlightAnimateView.layer removeAnimationForKey:key];
}
}
- (UIView *)highlightAnimateView
{
return self.container;
}
- (void)animationDidStart:(CAAnimation *)anim
{
self.highlightAnimating = YES;
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
self.highlightAnimating = NO;
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.messageData.showName) {
_nameLabel.mm_sizeToFitThan(1, 20);
_nameLabel.hidden = NO;
} else {
_nameLabel.hidden = YES;
_nameLabel.mm_height(0);
}
_selectedView.mm_x = 0;
_selectedView.mm_y = 0;
_selectedView.mm_w = self.contentView.mm_w;
_selectedView.mm_h = self.contentView.mm_h;
if (self.messageData.showCheckBox) {
_selectedIcon.mm_width(20).mm_height(20);
_selectedIcon.mm_x = 10;
_selectedIcon.mm_centerY = _avatarView.mm_centerY;
_selectedIcon.hidden = NO;
_selectedView.hidden = NO;
} else {
_selectedIcon.mm_width(0).mm_height(0);
_selectedIcon.mm_x = 0;
_selectedIcon.mm_y = 0;
_selectedIcon.hidden = YES;
_selectedView.hidden = YES;
}
if (self.messageData.showMessageTime) {
_timeLabel.mm_sizeToFit();
_timeLabel.mm_y = self.avatarView.mm_y;
_timeLabel.mm_x = self.contentView.bounds.size.width - 10 - _timeLabel.mm_w;
_timeLabel.hidden = NO;
} else {
_timeLabel.mm_y = 0;
_timeLabel.mm_r = 0;
_timeLabel.mm_sizeToFit();
_timeLabel.hidden = YES;
}
TUIMessageCellLayout *cellLayout = self.messageData.cellLayout;
if (self.messageData.direction == MsgDirectionIncoming) {
self.avatarView.mm_x = _selectedIcon.mm_maxX + cellLayout.avatarInsets.left;
self.avatarView.mm_y = cellLayout.avatarInsets.top;
self.avatarView.mm_w = cellLayout.avatarSize.width;
self.avatarView.mm_h = cellLayout.avatarSize.height;
self.nameLabel.mm_top(self.avatarView.mm_y);
CGSize csize = [self.messageData contentSize];
CGFloat ctop = cellLayout.messageInsets.top + _nameLabel.mm_h;
self.container.mm_left(cellLayout.messageInsets.left+self.avatarView.mm_maxX)
.mm_top(ctop).mm_width(csize.width).mm_height(csize.height);
self.nameLabel.mm_left(_container.mm_x + 7) ;//与气泡对齐
self.indicator.mm_sizeToFit().mm__centerY(_container.mm_centerY).mm_left(_container.mm_maxX + 8);
self.retryView.frame = self.indicator.frame;
self.readReceiptLabel.hidden = YES;
} else {
self.avatarView.mm_w = cellLayout.avatarSize.width;
self.avatarView.mm_h = cellLayout.avatarSize.height;
self.avatarView.mm_top(cellLayout.avatarInsets.top).mm_right(cellLayout.avatarInsets.right);
self.nameLabel.mm_top(self.avatarView.mm_y);
CGSize csize = [self.messageData contentSize];
CGFloat ctop = cellLayout.messageInsets.top + _nameLabel.mm_h;
self.container.mm_width(csize.width).mm_height(csize.height)
.mm_right(cellLayout.messageInsets.right+self.contentView.mm_w-self.avatarView.mm_x).mm_top(ctop);
self.nameLabel.mm_right(_container.mm_r);
self.indicator.mm_sizeToFit().mm__centerY(_container.mm_centerY).mm_left(_container.mm_x - 8 - _indicator.mm_w);
self.retryView.frame = self.indicator.frame;
//这里不能像 retryView 一样直接使用 indicator 的设定,否则内容会显示不全。
self.readReceiptLabel.mm_sizeToFit().mm_bottom(self.container.mm_b + cellLayout.bubbleInsets.bottom).mm_left(_container.mm_x - 8 - _readReceiptLabel.mm_w);
}
}
- (void)onLongPress:(UIGestureRecognizer *)recognizer
{
if([recognizer isKindOfClass:[UILongPressGestureRecognizer class]] &&
recognizer.state == UIGestureRecognizerStateBegan){
if(_delegate && [_delegate respondsToSelector:@selector(onLongPressMessage:)]){
[_delegate onLongPressMessage:self];
}
}
}
- (void)onRetryMessage:(UIGestureRecognizer *)recognizer
{
if (_messageData.status == Msg_Status_Fail)
if (_delegate && [_delegate respondsToSelector:@selector(onRetryMessage:)]) {
[_delegate onRetryMessage:self];
}
}
- (void)onSelectMessage:(UIGestureRecognizer *)recognizer
{
if(_delegate && [_delegate respondsToSelector:@selector(onSelectMessage:)]){
[_delegate onSelectMessage:self];
}
}
- (void)onSelectMessageAvatar:(UIGestureRecognizer *)recognizer
{
if(_delegate && [_delegate respondsToSelector:@selector(onSelectMessageAvatar:)]){
[_delegate onSelectMessageAvatar:self];
}
}
- (void)onLongSelectMessageAvatar:(UIGestureRecognizer *)recognizer
{
if(_delegate && [_delegate respondsToSelector:@selector(onLongSelectMessageAvatar:)]){
[_delegate onLongSelectMessageAvatar:self];
}
}
- (void)prepareForReuse{
[super prepareForReuse];
//今后任何关于复用产生的 UI 问题,都可以在此尝试编码解决。
_readReceiptLabel.text = TUIKitLocalizableString(Unread); // @"未读";//一但消息复用,说明即将新消息出现,label内容改为未读。
}
@end