// // NIMMessageCell.m // NIMKit // // Created by chris. // Copyright (c) 2015年 NetEase. All rights reserved. // #import "NIMMessageCell.h" #import "NIMMessageModel.h" #import "NIMAvatarImageView.h" #import "NIMBadgeView.h" #import "NIMSessionMessageContentView.h" #import "NIMKitUtil.h" #import "NIMSessionAudioContentView.h" #import "UIView+NIM.h" #import "NIMKitDependency.h" #import "M80AttributedLabel.h" #import "UIImage+NIMKit.h" #import "NIMSessionUnknowContentView.h" #import "NIMKitConfig.h" #import "NIMKit.h" @interface NIMMessageCell() { UILongPressGestureRecognizer *_longPressGesture; UIMenuController *_menuController; } @property (nonatomic,strong) NIMMessageModel *model; @property (nonatomic,copy) NSArray *customViews; @end @implementation NIMMessageCell - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]) { self.selectionStyle = UITableViewCellSelectionStyleNone; [self makeComponents]; [self makeGesture]; } return self; } - (void)dealloc { [self removeGestureRecognizer:_longPressGesture]; } - (void)makeComponents { // static UIImage *NIMRetryButtonImage; // static dispatch_once_t onceToken; // dispatch_once(&onceToken, ^{ // NIMRetryButtonImage = [UIImage nim_imageInKit:@"icon_message_cell_error"]; // }); //retyrBtn // _retryButton = [UIButton buttonWithType:UIButtonTypeCustom]; // [_retryButton setImage:NIMRetryButtonImage forState:UIControlStateNormal]; // [_retryButton setImage:NIMRetryButtonImage forState:UIControlStateHighlighted]; // [_retryButton setFrame:CGRectMake(0, 0, 20, 20)]; // [_retryButton addTarget:self action:@selector(onRetryMessage:) forControlEvents:UIControlEventTouchUpInside]; // [self.contentView addSubview:_retryButton]; _retryButton = [UIButton buttonWithType:UIButtonTypeCustom]; _retryButton.opaque = YES; _retryButton.titleLabel.font = [NIMKit sharedKit].config.sendFailFont; [_retryButton setTitleColor:[NIMKit sharedKit].config.sendFailColor forState:UIControlStateNormal]; [_retryButton setTitleColor:[NIMKit sharedKit].config.sendFailColor forState:UIControlStateHighlighted]; [_retryButton setTitle:@"重试" forState:UIControlStateNormal]; [_retryButton setHidden:YES]; [_retryButton addTarget:self action:@selector(onRetryMessage:) forControlEvents:UIControlEventTouchUpInside]; [self.contentView addSubview:_retryButton]; [_retryButton sizeToFit]; _priBtnButton = [UIButton buttonWithType:UIButtonTypeCustom]; _priBtnButton.adjustsImageWhenHighlighted = NO; // [_priBtnButton setFrame:CGRectMake(0, 0, 20, 20)]; [_priBtnButton setImage:[UIImage imageNamed:@"vqu_images_im_coin"] forState:UIControlStateNormal]; _priBtnButton.titleLabel.font = LCFont12; [_priBtnButton setTitleColor:LZA3AABEColor forState:UIControlStateNormal]; [_priBtnButton addTarget:self action:@selector(onPriBtnButtonMessage:) forControlEvents:UIControlEventTouchUpInside]; // _priBtnButton.backgroundColor = LCOrangeColor; // _priBtnButton.layer.cornerRadius = 10; [_priBtnButton setEnlargeEdge:30]; // _priBtnButton.layer.masksToBounds = YES; [self.contentView addSubview:_priBtnButton]; //audioPlayedIcon _audioPlayedIcon = [NIMBadgeView viewWithBadgeTip:@""]; [self.contentView addSubview:_audioPlayedIcon]; //traningActivityIndicator _traningActivityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0,0,20,20)]; [self.contentView addSubview:_traningActivityIndicator]; //headerView _headImageView = [[NIMAvatarImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; [_headImageView addTarget:self action:@selector(onTapAvatar:) forControlEvents:UIControlEventTouchUpInside]; UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPressAvatar:)]; [_headImageView addGestureRecognizer:gesture]; [self.contentView addSubview:_headImageView]; //nicknamel _nameLabel = [[UILabel alloc] init]; _nameLabel.backgroundColor = [UIColor clearColor]; _nameLabel.opaque = YES; _nameLabel.font = [NIMKit sharedKit].config.nickFont; _nameLabel.textColor = [NIMKit sharedKit].config.nickColor; [_nameLabel setHidden:YES]; [self.contentView addSubview:_nameLabel]; //readlabel _readButton = [UIButton buttonWithType:UIButtonTypeCustom]; _readButton.opaque = YES; _readButton.titleLabel.font = [NIMKit sharedKit].config.receiptFont; [_readButton setTitleColor:[NIMKit sharedKit].config.receiptColor forState:UIControlStateNormal]; [_readButton setTitleColor:[NIMKit sharedKit].config.receiptColor forState:UIControlStateHighlighted]; [_readButton setHidden:YES]; [_readButton addTarget:self action:@selector(onPressReadButton:) forControlEvents:UIControlEventTouchUpInside]; [self.contentView addSubview:_readButton]; _sendSuccessBtn = [UIButton buttonWithType:UIButtonTypeCustom]; _sendSuccessBtn.opaque = YES; _sendSuccessBtn.titleLabel.font = [NIMKit sharedKit].config.sendSuccessFont; [_sendSuccessBtn setTitleColor:[NIMKit sharedKit].config.sendSuccessColor forState:UIControlStateNormal]; [_sendSuccessBtn setTitleColor:[NIMKit sharedKit].config.sendSuccessColor forState:UIControlStateHighlighted]; [_sendSuccessBtn setHidden:YES]; // [_sendSuccessBtn addTarget:self action:@selector(onPressReadButton:) forControlEvents:UIControlEventTouchUpInside]; [self.contentView addSubview:_sendSuccessBtn]; } - (void)makeGesture{ _longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longGesturePress:)]; [self addGestureRecognizer:_longPressGesture]; } - (void)refreshData:(NIMMessageModel *)data { self.model = data; if ([self checkData]) { [self refresh]; } } - (BOOL)checkData{ return [self.model isKindOfClass:[NIMMessageModel class]]; } - (void)refresh { [self addContentViewIfNotExist]; [self addUserCustomViews]; self.backgroundColor = [NIMKit sharedKit].config.cellBackgroundColor; if ([self needShowAvatar]) { [_headImageView setAvatarByMessage:self.model.message]; } if([self needShowNickName]) { NSString *nick = [NIMKitUtil showNick:self.model.message.from inMessage:self.model.message]; [self.nameLabel setText:nick]; } [_nameLabel setHidden:![self needShowNickName]]; [_bubbleView refresh:self.model]; [_bubbleView setNeedsLayout]; BOOL isActivityIndicatorHidden = [self activityIndicatorHidden]; if (isActivityIndicatorHidden) { [_traningActivityIndicator stopAnimating]; } else { [_traningActivityIndicator startAnimating]; } [_traningActivityIndicator setHidden:isActivityIndicatorHidden]; [_retryButton setHidden:[self retryButtonHidden]]; [_priBtnButton setHidden:[self priBtnButtonHidden]]; [_audioPlayedIcon setHidden:[self unreadHidden]]; [self refreshReadButton]; [self refreshSendSuccessBtn]; [self setNeedsLayout]; } - (void)refreshReadButton { BOOL hidden = [self readLabelHidden]; [_readButton setHidden:hidden]; if (!hidden) { if (self.model.message.session.sessionType == NIMSessionTypeP2P) { [_readButton setTitle:@"已读" forState:UIControlStateNormal]; [_readButton sizeToFit]; } else if(self.model.message.session.sessionType == NIMSessionTypeTeam) { [_readButton setTitle:[NSString stringWithFormat:@"%zd人未读",self.model.message.teamReceiptInfo.unreadCount] forState:UIControlStateNormal]; [_readButton sizeToFit]; } } } - (void)refreshSendSuccessBtn { BOOL hidden = [self sendSuccessLabelHidden]; [_sendSuccessBtn setHidden:hidden]; if (!hidden) { [_sendSuccessBtn setTitle:@"送达" forState:UIControlStateNormal]; [_sendSuccessBtn sizeToFit]; } } - (void)addContentViewIfNotExist { if (_bubbleView == nil) { id layoutConfig = [[NIMKit sharedKit] layoutConfig]; NSString *contentStr = [layoutConfig cellContent:self.model]; NSAssert([contentStr length] > 0, @"should offer cell content class name"); Class clazz = NSClassFromString(contentStr); NIMSessionMessageContentView *contentView = [[clazz alloc] initSessionMessageContentView]; NSAssert(contentView, @"can not init content view"); _bubbleView = contentView; _bubbleView.delegate = self; NIMMessageType messageType = self.model.message.messageType; if (messageType == NIMMessageTypeAudio) { ((NIMSessionAudioContentView *)_bubbleView).audioUIDelegate = self; } [self.contentView addSubview:_bubbleView]; } } - (void)addUserCustomViews { for (UIView *view in self.customViews) { [view removeFromSuperview]; } id layoutConfig = [[NIMKit sharedKit] layoutConfig]; self.customViews = [layoutConfig customViews:self.model]; for (UIView *view in self.customViews) { [self.contentView addSubview:view]; } } - (void)layoutSubviews { [super layoutSubviews]; [self layoutAvatar]; [self layoutNameLabel]; [self layoutBubbleView]; [self layoutRetryButton]; [self layoutPriBtnButton]; [self layoutAudioPlayedIcon]; [self layoutActivityIndicator]; [self layoutReadButton]; [self layoutSendSuccessButton]; } - (void)layoutAvatar { BOOL needShow = [self needShowAvatar]; _headImageView.hidden = !needShow; if (needShow) { _headImageView.frame = [self avatarViewRect]; } } - (void)layoutNameLabel { if ([self needShowNickName]) { CGFloat otherBubbleOriginX = self.cellPaddingToNick.x; CGFloat otherBubbleOriginy = self.cellPaddingToNick.y; CGFloat otherNickNameWidth = 200.f; CGFloat otherNickNameHeight = 20.f; CGFloat cellPaddingToProtrait = self.cellPaddingToAvatar.x; CGFloat avatarWidth = self.headImageView.nim_width; CGFloat myBubbleOriginX = self.nim_width - cellPaddingToProtrait - avatarWidth - self.cellPaddingToNick.x; _nameLabel.frame = self.model.shouldShowLeft ? CGRectMake(otherBubbleOriginX,otherBubbleOriginy, otherNickNameWidth, otherNickNameHeight) : CGRectMake(myBubbleOriginX,otherBubbleOriginy, otherNickNameWidth,otherNickNameHeight) ; } } - (void)layoutBubbleView { CGSize size = [self.model contentSize:self.nim_width]; UIEdgeInsets insets = self.model.contentViewInsets; size.width = size.width + insets.left + insets.right; size.height = size.height + insets.top + insets.bottom; _bubbleView.nim_size = size; UIEdgeInsets contentInsets = self.model.bubbleViewInsets; if (!self.model.shouldShowLeft) { CGFloat protraitRightToBubble = 5.f; CGFloat right = self.model.shouldShowAvatar? CGRectGetMinX(self.headImageView.frame) - protraitRightToBubble : self.nim_width; contentInsets.left = right - CGRectGetWidth(self.bubbleView.bounds); } _bubbleView.nim_left = contentInsets.left; _bubbleView.nim_top = contentInsets.top; } - (void)layoutActivityIndicator { if (_traningActivityIndicator.isAnimating) { CGFloat centerX = 0; if (!self.model.shouldShowLeft) { centerX = CGRectGetMinX(_bubbleView.frame) - [self retryButtonBubblePadding] - CGRectGetWidth(_traningActivityIndicator.bounds)/2;; } else { centerX = CGRectGetMaxX(_bubbleView.frame) + [self retryButtonBubblePadding] + CGRectGetWidth(_traningActivityIndicator.bounds)/2; } self.traningActivityIndicator.center = CGPointMake(centerX, _bubbleView.center.y); } } - (void)layoutRetryButton { if (!_retryButton.isHidden) { CGFloat centerX = 0; if (self.model.shouldShowLeft) { centerX = CGRectGetMaxX(_bubbleView.frame) + [self retryButtonBubblePadding] +CGRectGetWidth(_retryButton.bounds)/2; } else { centerX = CGRectGetMinX(_bubbleView.frame) - [self retryButtonBubblePadding] - CGRectGetWidth(_retryButton.bounds)/2; } _retryButton.center = CGPointMake(centerX, _bubbleView.center.y); } } - (void)layoutPriBtnButton{ if (!_priBtnButton.isHidden) { // CGFloat centerX = 0; // if (!self.model.message.isOutgoingMsg) // { // centerX = CGRectGetMaxX(_bubbleView.frame) + 5 +CGRectGetWidth(_retryButton.bounds)/2; // } // else // { // centerX = CGRectGetMinX(_bubbleView.frame) - 5 - CGRectGetWidth(_retryButton.bounds)/2; // } _priBtnButton.mj_y = CGRectGetMaxY(_bubbleView.frame) + 6.0f; _priBtnButton.mj_x = _bubbleView.mj_x; // _priBtnButton.center = CGPointMake(CGRectGetMaxX(_bubbleView.frame) + 6, CGRectGetMaxY(_bubbleView.frame)-15); } } - (void)layoutAudioPlayedIcon{ if (!_audioPlayedIcon.hidden) { CGFloat padding = [self audioPlayedIconBubblePadding]; if (self.model.shouldShowLeft) { _audioPlayedIcon.nim_left = _bubbleView.nim_right + padding; } else { _audioPlayedIcon.nim_right = _bubbleView.nim_left - padding; } _audioPlayedIcon.nim_top = _bubbleView.nim_top; } } - (void)layoutReadButton{ if (!_readButton.isHidden) { CGFloat left = _bubbleView.nim_left; // CGFloat bottom = _bubbleView.nim_bottom; _readButton.nim_left = left - CGRectGetWidth(_readButton.bounds) - [self readButtonBubblePadding]; _readButton.nim_centerY = _bubbleView.nim_centerY; } } - (void)layoutSendSuccessButton{ if (!_sendSuccessBtn.isHidden) { CGFloat left = _bubbleView.nim_left; _sendSuccessBtn.nim_left = left - CGRectGetWidth(_sendSuccessBtn.bounds) - [self sendSuccessButtonBubblePadding]; _sendSuccessBtn.nim_centerY = _bubbleView.nim_centerY; } } #pragma mark - NIMMessageContentViewDelegate - (void)onCatchEvent:(NIMKitEvent *)event{ if ([self.delegate respondsToSelector:@selector(onTapCell:)]) { [self.delegate onTapCell:event]; } } #pragma mark - Action - (void)onPriBtnButtonMessage:(id)sender{ if (self.delegate && [self.delegate respondsToSelector:@selector(onPriBtnButtonMessage:)]) { [self.delegate onPriBtnButtonMessage:self.model.message]; } } - (void)onRetryMessage:(id)sender { if (self.delegate && [self.delegate respondsToSelector:@selector(onRetryMessage:)]) { [self.delegate onRetryMessage:self.model.message]; } } - (void)longGesturePress:(UIGestureRecognizer *)gestureRecognizer { if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] && gestureRecognizer.state == UIGestureRecognizerStateBegan) { if (self.delegate && [self.delegate respondsToSelector:@selector(onLongPressCell:inView:)]) { [self.delegate onLongPressCell:self.model.message inView:_bubbleView]; } } } #pragma mark - NIMPlayAudioUIDelegate - (void)startPlayingAudioUI { [self refreshData:self.model]; } - (void)retryDownloadMsg { [self onRetryMessage:nil]; } #pragma mark - Private - (CGRect)avatarViewRect { CGFloat cellWidth = self.bounds.size.width; CGFloat cellHeight = self.bounds.size.height; CGFloat protraitImageWidth = [self avatarSize].width; CGFloat protraitImageHeight = [self avatarSize].height; CGFloat selfProtraitOriginX = (cellWidth - self.cellPaddingToAvatar.x - protraitImageWidth); CGFloat avatarY = self.cellPaddingToAvatar.y; if ([self.bubbleView isKindOfClass:NSClassFromString(@"YOUPAILCGiftContentView")]) { avatarY = (cellHeight - protraitImageHeight) / 2; } return self.model.shouldShowLeft ? CGRectMake(self.cellPaddingToAvatar.x, avatarY,protraitImageWidth, protraitImageHeight) : CGRectMake(selfProtraitOriginX, avatarY,protraitImageWidth,protraitImageHeight); } - (BOOL)needShowAvatar { return self.model.shouldShowAvatar; } - (BOOL)needShowNickName { return self.model.shouldShowNickName; } - (BOOL)retryButtonHidden { id layoutConfig = [[NIMKit sharedKit] layoutConfig]; BOOL disable = NO; if ([layoutConfig respondsToSelector:@selector(disableRetryButton:)]) { disable = [layoutConfig disableRetryButton:self.model]; } return disable; } - (BOOL)priBtnButtonHidden{ BOOL messageType = (self.model.message.messageType != NIMMessageTypeText && self.model.message.messageType != NIMMessageTypeImage && self.model.message.messageType != NIMMessageTypeAudio && self.model.message.messageType != NIMMessageTypeVideo); BOOL isspecial = [LCSaveData getIsspecial]; BOOL is_cut = [[self.model.message.remoteExt objectForKey:@"is_cut"]integerValue] != 1; BOOL isWhite= [LCSaveData getWhiteVersion]; if (messageType || isspecial || is_cut || isWhite) { return YES; } if (!self.model.message.isOutgoingMsg) { NSInteger cut = [[self.model.message.remoteExt objectForKey:@"is_cut"]integerValue]; NSString *money = [self.model.message.remoteExt objectForKey:@"money"]; if (cut==1) { [_priBtnButton setTitle:[NSString stringWithFormat:@"+%@元",money] forState:(UIControlStateNormal)]; [_priBtnButton sizeToFit]; _priBtnButton.mj_h = 16.0f; [_priBtnButton setTitleColor:LZA3AABEColor forState:(UIControlStateNormal)]; return NO; }else{ return YES; } }else{ return YES; } } - (CGFloat)retryButtonBubblePadding { BOOL isFromMe = !self.model.shouldShowLeft; if (self.model.message.messageType == NIMMessageTypeAudio) { return isFromMe ? 11 : 9; } return isFromMe ? 4 : 6; } - (BOOL)activityIndicatorHidden { if (!self.model.message.isReceivedMsg) { return self.model.message.deliveryState != NIMMessageDeliveryStateDelivering; } else { return self.model.message.attachmentDownloadState != NIMMessageAttachmentDownloadStateDownloading; } } - (BOOL)unreadHidden { if (self.model.message.messageType == NIMMessageTypeAudio) { //音频 BOOL disable = NO; if ([self.delegate respondsToSelector:@selector(disableAudioPlayedStatusIcon:)]) { disable = [self.delegate disableAudioPlayedStatusIcon:self.model.message]; } BOOL hideIcon = self.model.message.attachmentDownloadState != NIMMessageAttachmentDownloadStateDownloaded || disable; return (hideIcon || self.model.message.isOutgoingMsg || [self.model.message isPlayed]); } return YES; } - (BOOL)readLabelHidden { if (self.model.shouldShowReadLabel && [self activityIndicatorHidden] && [self retryButtonHidden] && [self unreadHidden]) { return NO; } return YES; } - (BOOL)sendSuccessLabelHidden { if ([self readLabelHidden] && [self activityIndicatorHidden] && [self retryButtonHidden] && self.model.message.deliveryState == NIMMessageDeliveryStateDeliveried && !self.model.shouldShowLeft) { return NO; } return YES; } - (CGFloat)audioPlayedIconBubblePadding{ return 10.0; } - (CGFloat)readButtonBubblePadding{ return 2.0; } - (CGFloat)sendSuccessButtonBubblePadding{ return 2.0; } - (CGPoint)cellPaddingToAvatar { return self.model.avatarMargin; } - (CGPoint)cellPaddingToNick { return self.model.nickNameMargin; } - (CGSize)avatarSize { return self.model.avatarSize; } - (void)onTapAvatar:(id)sender{ if ([self.delegate respondsToSelector:@selector(onTapAvatar:)]) { [self.delegate onTapAvatar:self.model.message]; } } - (void)onLongPressAvatar:(UIGestureRecognizer *)gestureRecognizer { if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] && gestureRecognizer.state == UIGestureRecognizerStateBegan) { if ([self.delegate respondsToSelector:@selector(onLongPressAvatar:)]) { [self.delegate onLongPressAvatar:self.model.message]; } } } - (void)onPressReadButton:(id)sender { if ([self.delegate respondsToSelector:@selector(onPressReadLabel:)]) { [self.delegate onPressReadLabel:self.model.message]; } } @end