chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:35:01 +08:00
commit a2e318a963
2974 changed files with 158180 additions and 0 deletions
@@ -0,0 +1,14 @@
//
// TLExpressionMessageCell.h
// TLChat
//
// Created by 李伯坤 on 16/3/19.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMessageBaseCell.h"
#import "TLExpressionMessage.h"
@interface TLExpressionMessageCell : TLMessageBaseCell
@end
@@ -0,0 +1,124 @@
//
// TLExpressionMessageCell.m
// TLChat
//
// Created by 李伯坤 on 16/3/19.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLExpressionMessageCell.h"
#import <SDWebImage/UIImage+GIF.h>
#import "NSFileManager+TLChat.h"
@interface TLExpressionMessageCell ()
@property (nonatomic, strong) UIImageView *msgImageView;
@end
@implementation TLExpressionMessageCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self.contentView addSubview:self.msgImageView];
}
return self;
}
- (void)setMessage:(TLExpressionMessage *)message
{
[self.msgImageView setAlpha:1.0]; // 取消长按效果
// if (self.message && [self.message.messageID isEqualToString:message.messageID]) {
// return;
// }
TLMessageOwnerType lastOwnType = self.message ? self.message.ownerTyper : -1;
[super setMessage:message];
NSData *data = [NSData dataWithContentsOfFile:message.path];
if (data) {
[self.msgImageView setImage:[UIImage imageNamed:message.path]];
[self.msgImageView setImage:[UIImage sd_animatedGIFWithData:data]];
}
else { // 表情组被删掉,先从缓存目录中查找,没有的话在下载并存入缓存目录
NSString *cachePath = [NSFileManager cacheForFile:[NSString stringWithFormat:@"%@_%@.gif", message.emoji.gid, message.emoji.eId]];
NSData *data = [NSData dataWithContentsOfFile:cachePath];
if (data) {
[self.msgImageView setImage:[UIImage imageNamed:cachePath]];
[self.msgImageView setImage:[UIImage sd_animatedGIFWithData:data]];
}
else {
__weak typeof(self) weakSelf = self;
[self.msgImageView tt_setImageWithURL:TLURL(message.url) completed:^(UIImage *image, NSError *error, TLImageCacheType cacheType, NSURL *imageURL) {
if ([[imageURL description] isEqualToString:[(TLExpressionMessage *)weakSelf.message url]]) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:imageURL];
[data writeToFile:cachePath atomically:NO]; // 再写入到缓存中
if ([[imageURL description] isEqualToString:[(TLExpressionMessage *)weakSelf.message url]]) {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.msgImageView setImage:[UIImage sd_animatedGIFWithData:data]];
});
}
});
}
}];
}
}
if (lastOwnType != message.ownerTyper) {
if (message.ownerTyper == TLMessageOwnerTypeSelf) {
[self.msgImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.usernameLabel.mas_bottom).mas_offset(5);
make.right.mas_equalTo(self.messageBackgroundView).mas_offset(-10);
}];
}
else if (message.ownerTyper == TLMessageOwnerTypeFriend){
[self.msgImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.usernameLabel.mas_bottom).mas_offset(5);
make.left.mas_equalTo(self.messageBackgroundView).mas_offset(10);
}];
}
}
[self.msgImageView mas_updateConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(message.messageFrame.contentSize);
}];
}
#pragma mark - Event Response -
- (void)longPressMsgBGView:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateBegan) {
return;
}
[self.msgImageView setAlpha:0.7]; // 比较low的选中效果
if (self.delegate && [self.delegate respondsToSelector:@selector(messageCellLongPress:rect:)]) {
CGRect rect = self.msgImageView.frame;
[self.delegate messageCellLongPress:self.message rect:rect];
}
}
- (void)doubleTabpMsgBGView
{
if (self.delegate && [self.delegate respondsToSelector:@selector(messageCellDoubleClick:)]) {
[self.delegate messageCellDoubleClick:self.message];
}
}
#pragma mark - Getter -
- (UIImageView *)msgImageView
{
if (_msgImageView == nil) {
_msgImageView = [[UIImageView alloc] init];
[_msgImageView setUserInteractionEnabled:YES];
UILongPressGestureRecognizer *longPressGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMsgBGView:)];
[_msgImageView addGestureRecognizer:longPressGR];
UITapGestureRecognizer *doubleTapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTabpMsgBGView)];
[doubleTapGR setNumberOfTapsRequired:2];
[_msgImageView addGestureRecognizer:doubleTapGR];
}
return _msgImageView;
}
@end
@@ -0,0 +1,14 @@
//
// TLImageMessageCell.h
// TLChat
//
// Created by 李伯坤 on 16/3/14.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMessageBaseCell.h"
#import "TLImageMessage.h"
@interface TLImageMessageCell : TLMessageBaseCell
@end
@@ -0,0 +1,118 @@
//
// TLImageMessageCell.m
// TLChat
//
// Created by 李伯坤 on 16/3/14.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLImageMessageCell.h"
#import "TLMessageImageView.h"
#import "NSFileManager+TLChat.h"
#define MSG_SPACE_TOP 2
@interface TLImageMessageCell ()
@property (nonatomic, strong) TLMessageImageView *msgImageView;
@end
@implementation TLImageMessageCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self.contentView addSubview:self.msgImageView];
}
return self;
}
- (void)setMessage:(TLImageMessage *)message
{
[self.msgImageView setAlpha:1.0]; // 取消长按效果
if (self.message && [self.message.messageID isEqualToString:message.messageID]) {
return;
}
TLMessageOwnerType lastOwnType = self.message ? self.message.ownerTyper : -1;
[super setMessage:message];
if ([message imagePath]) {
NSString *imagePath = [NSFileManager pathUserChatImage:[message imagePath]];
[self.msgImageView setThumbnailPath:imagePath highDefinitionImageURL:[message imagePath]];
}
else {
[self.msgImageView setThumbnailPath:nil highDefinitionImageURL:[message imagePath]];
[self.msgImageView tt_setImageWithURL:[NSURL URLWithString:[message imageURL]]];
}
if (lastOwnType != message.ownerTyper) {
if (message.ownerTyper == TLMessageOwnerTypeSelf) {
[self.msgImageView setBackgroundImage:[UIImage imageNamed:@"message_sender_bg"]];
[self.msgImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.messageBackgroundView);
make.right.mas_equalTo(self.messageBackgroundView);
}];
}
else if (message.ownerTyper == TLMessageOwnerTypeFriend){
[self.msgImageView setBackgroundImage:[UIImage imageNamed:@"message_receiver_bg"]];
[self.msgImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.messageBackgroundView);
make.left.mas_equalTo(self.messageBackgroundView);
}];
}
}
[self.msgImageView mas_updateConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(message.messageFrame.contentSize);
}];
}
#pragma mark - Event Response -
- (void)tapMessageView
{
if (self.delegate && [self.delegate respondsToSelector:@selector(messageCellTap:)]) {
[self.delegate messageCellTap:self.message];
}
}
- (void)longPressMsgBGView:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateBegan) {
return;
}
[self.msgImageView setAlpha:0.7]; // 比较low的选中效果
if (self.delegate && [self.delegate respondsToSelector:@selector(messageCellLongPress:rect:)]) {
CGRect rect = self.msgImageView.frame;
rect.size.height -= 10; // 北京图片底部空白区域
[self.delegate messageCellLongPress:self.message rect:rect];
}
}
- (void)doubleTabpMsgBGView
{
if (self.delegate && [self.delegate respondsToSelector:@selector(messageCellDoubleClick:)]) {
[self.delegate messageCellDoubleClick:self.message];
}
}
#pragma mark - Getter -
- (TLMessageImageView *)msgImageView
{
if (_msgImageView == nil) {
_msgImageView = [[TLMessageImageView alloc] init];
[_msgImageView setUserInteractionEnabled:YES];
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapMessageView)];
[_msgImageView addGestureRecognizer:tapGR];
UILongPressGestureRecognizer *longPressGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMsgBGView:)];
[_msgImageView addGestureRecognizer:longPressGR];
UITapGestureRecognizer *doubleTapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTabpMsgBGView)];
[doubleTapGR setNumberOfTapsRequired:2];
[_msgImageView addGestureRecognizer:doubleTapGR];
}
return _msgImageView;
}
@end
@@ -0,0 +1,32 @@
//
// TLMessageBaseCell.h
// TLChat
//
// Created by 李伯坤 on 16/3/1.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLMessageCellDelegate.h"
#import "TLMessage.h"
@interface TLMessageBaseCell : UITableViewCell
@property (nonatomic, assign) id<TLMessageCellDelegate>delegate;
@property (nonatomic, strong) UILabel *timeLabel;
@property (nonatomic, strong) UIButton *avatarButton;
@property (nonatomic, strong) UILabel *usernameLabel;
@property (nonatomic, strong) UIImageView *messageBackgroundView;
@property (nonatomic, strong) TLMessage *message;
/**
* 更新消息,如果子类不重写,默认调用setMessage方法
*/
- (void)updateMessage:(TLMessage *)message;
@end
@@ -0,0 +1,221 @@
//
// TLMessageBaseCell.m
// TLChat
//
// Created by 李伯坤 on 16/3/1.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMessageBaseCell.h"
#import "NSDate+TLChat.h"
#import "NSFileManager+TLChat.h"
#define TIMELABEL_HEIGHT 20.0f
#define TIMELABEL_SPACE_Y 10.0f
#define NAMELABEL_HEIGHT 14.0f
#define NAMELABEL_SPACE_X 12.0f
#define NAMELABEL_SPACE_Y 1.0f
#define AVATAR_WIDTH 40.0f
#define AVATAR_SPACE_X 8.0f
#define AVATAR_SPACE_Y 12.0f
#define MSGBG_SPACE_X 5.0f
#define MSGBG_SPACE_Y 1.0f
@interface TLMessageBaseCell ()
@end
@implementation TLMessageBaseCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self setBackgroundColor:[UIColor clearColor]];
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
[self.contentView addSubview:self.timeLabel];
[self.contentView addSubview:self.avatarButton];
[self.contentView addSubview:self.usernameLabel];
[self.contentView addSubview:self.messageBackgroundView];
[self p_addMasonry];
}
return self;
}
- (void)setMessage:(TLMessage *)message
{
if (_message && [_message.messageID isEqualToString:message.messageID]) {
return;
}
[self.timeLabel setText:[NSString stringWithFormat:@" %@ ", message.date.chatTimeInfo]];
[self.usernameLabel setText:[message.fromUser chat_username]];
if ([message.fromUser chat_avatarPath].length > 0) {
NSString *path = [NSFileManager pathUserAvatar:[message.fromUser chat_avatarPath]];
[self.avatarButton setImage:[UIImage imageNamed:path] forState:UIControlStateNormal];
}
else {
[self.avatarButton tt_setImageWithURL:TLURL([message.fromUser chat_avatarURL]) forState:UIControlStateNormal];
}
// 时间
if (!_message || _message.showTime != message.showTime) {
[self.timeLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(message.showTime ? TIMELABEL_HEIGHT : 0);
make.top.mas_equalTo(self.contentView).mas_offset(message.showTime ? TIMELABEL_SPACE_Y : 0);
}];
}
if (!message || _message.ownerTyper != message.ownerTyper) {
// 头像
[self.avatarButton mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.and.height.mas_equalTo(AVATAR_WIDTH);
make.top.mas_equalTo(self.timeLabel.mas_bottom).mas_offset(AVATAR_SPACE_Y);
if(message.ownerTyper == TLMessageOwnerTypeSelf) {
make.right.mas_equalTo(self.contentView).mas_offset(-AVATAR_SPACE_X);
}
else {
make.left.mas_equalTo(self.contentView).mas_offset(AVATAR_SPACE_X);
}
}];
// 用户名
[self.usernameLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.avatarButton).mas_equalTo(-NAMELABEL_SPACE_Y);
if (message.ownerTyper == TLMessageOwnerTypeSelf) {
make.right.mas_equalTo(self.avatarButton.mas_left).mas_offset(- NAMELABEL_SPACE_X);
}
else {
make.left.mas_equalTo(self.avatarButton.mas_right).mas_equalTo(NAMELABEL_SPACE_X);
}
}];
// 背景
[self.messageBackgroundView mas_remakeConstraints:^(MASConstraintMaker *make) {
message.ownerTyper == TLMessageOwnerTypeSelf ? make.right.mas_equalTo(self.avatarButton.mas_left).mas_offset(-MSGBG_SPACE_X) : make.left.mas_equalTo(self.avatarButton.mas_right).mas_offset(MSGBG_SPACE_X);
make.top.mas_equalTo(self.usernameLabel.mas_bottom).mas_offset(message.showName ? 0 : -MSGBG_SPACE_Y);
}];
}
[self.usernameLabel setHidden:!message.showName];
[self.usernameLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(message.showName ? NAMELABEL_HEIGHT : 0);
}];
_message = message;
}
- (void)updateMessage:(TLMessage *)message
{
[self setMessage:message];
}
#pragma mark - Private Methods -
- (void)p_addMasonry
{
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.contentView).mas_offset(TIMELABEL_SPACE_Y);
make.centerX.mas_equalTo(self.contentView);
}];
[self.usernameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.avatarButton).mas_equalTo(-NAMELABEL_SPACE_Y);
make.right.mas_equalTo(self.avatarButton.mas_left).mas_offset(- NAMELABEL_SPACE_X);
}];
// Default - self
[self.avatarButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self.contentView).mas_offset(-AVATAR_SPACE_X);
make.width.and.height.mas_equalTo(AVATAR_WIDTH);
make.top.mas_equalTo(self.timeLabel.mas_bottom).mas_offset(AVATAR_SPACE_Y);
}];
[self.messageBackgroundView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self.avatarButton.mas_left).mas_offset(-MSGBG_SPACE_X);
make.top.mas_equalTo(self.usernameLabel.mas_bottom).mas_offset(-MSGBG_SPACE_Y);
}];
}
#pragma mark - Event Response -
- (void)avatarButtonDown:(UIButton *)sender
{
if (_delegate && [_delegate respondsToSelector:@selector(messageCellDidClickAvatarForUser:)]) {
[_delegate messageCellDidClickAvatarForUser:self.message.fromUser];
}
}
- (void)longPressMsgBGView:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateBegan) {
return;
}
[self.messageBackgroundView setHighlighted:YES];
if (_delegate && [_delegate respondsToSelector:@selector(messageCellLongPress:rect:)]) {
CGRect rect = self.messageBackgroundView.frame;
rect.size.height -= 10; // 北京图片底部空白区域
[_delegate messageCellLongPress:self.message rect:rect];
}
}
- (void)doubleTabpMsgBGView
{
if (_delegate && [_delegate respondsToSelector:@selector(messageCellDoubleClick:)]) {
[_delegate messageCellDoubleClick:self.message];
}
}
#pragma mark - Getter -
- (UILabel *)timeLabel
{
if (_timeLabel == nil) {
_timeLabel = [[UILabel alloc] init];
[_timeLabel setFont:[UIFont systemFontOfSize:12.0f]];
[_timeLabel setTextColor:[UIColor whiteColor]];
[_timeLabel setBackgroundColor:[UIColor grayColor]];
[_timeLabel setAlpha:0.7f];
[_timeLabel.layer setMasksToBounds:YES];
[_timeLabel.layer setCornerRadius:5.0f];
}
return _timeLabel;
}
- (UIButton *)avatarButton
{
if (_avatarButton == nil) {
_avatarButton = [[UIButton alloc] init];
[_avatarButton.layer setMasksToBounds:YES];
[_avatarButton.layer setBorderWidth:BORDER_WIDTH_1PX];
[_avatarButton.layer setBorderColor:[UIColor colorWithWhite:0.7 alpha:1.0].CGColor];
[_avatarButton addTarget:self action:@selector(avatarButtonDown:) forControlEvents:UIControlEventTouchUpInside];
}
return _avatarButton;
}
- (UILabel *)usernameLabel
{
if (_usernameLabel == nil) {
_usernameLabel = [[UILabel alloc] init];
[_usernameLabel setTextColor:[UIColor grayColor]];
[_usernameLabel setFont:[UIFont systemFontOfSize:12.0f]];
}
return _usernameLabel;
}
- (UIImageView *)messageBackgroundView
{
if (_messageBackgroundView == nil) {
_messageBackgroundView = [[UIImageView alloc] init];
[_messageBackgroundView setUserInteractionEnabled:YES];
UILongPressGestureRecognizer *longPressGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMsgBGView:)];
[_messageBackgroundView addGestureRecognizer:longPressGR];
UITapGestureRecognizer *doubleTapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTabpMsgBGView)];
[doubleTapGR setNumberOfTapsRequired:2];
[_messageBackgroundView addGestureRecognizer:doubleTapGR];
}
return _messageBackgroundView;
}
@end
@@ -0,0 +1,24 @@
//
// TLMessageCellDelegate.h
// TLChat
//
// Created by 李伯坤 on 16/3/2.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol TLChatUserProtocol;
@class TLMessage;
@protocol TLMessageCellDelegate <NSObject>
- (void)messageCellDidClickAvatarForUser:(id<TLChatUserProtocol>)user;
- (void)messageCellTap:(TLMessage *)message;
- (void)messageCellLongPress:(TLMessage *)message rect:(CGRect)rect;
- (void)messageCellDoubleClick:(TLMessage *)message;
@end
@@ -0,0 +1,23 @@
//
// TLMessageImageView.h
// TLChat
//
// Created by 李伯坤 on 16/3/15.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TLMessageImageView : UIImageView
@property (nonatomic, strong) UIImage *backgroundImage;
/**
* 设置消息图片(规则:收到消息时,先下载缩略图到本地,再添加到列表显示,并自动下载大图)
*
* @param imagePath 缩略图Path
* @param imageURL 高清图URL
*/
- (void)setThumbnailPath:(NSString *)imagePath highDefinitionImageURL:(NSString *)imageURL;
@end
@@ -0,0 +1,66 @@
//
// TLMessageImageView.m
// TLChat
//
// Created by 李伯坤 on 16/3/15.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMessageImageView.h"
@interface TLMessageImageView ()
@property (nonatomic, weak) CAShapeLayer *maskLayer;
@property (nonatomic, weak) CALayer *contentLayer;
@end
@implementation TLMessageImageView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.contentsCenter = CGRectMake(0.5, 0.6, 0.1, 0.1);
maskLayer.contentsScale = [UIScreen mainScreen].scale; //非常关键设置自动拉伸的效果且不变形
CALayer *contentLayer = [[CALayer alloc] init];
[contentLayer setMask:maskLayer];
[self.layer addSublayer:contentLayer];
self.maskLayer = maskLayer;
self.contentLayer = contentLayer;
}
return self;
}
- (void)dealloc
{
NSLog(@"delloc TLMessageImageView");
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self.maskLayer setFrame:CGRectMake(0, 0, self.width, self.height)];
[self.contentLayer setFrame:CGRectMake(0, 0, self.width, self.height)];
}
- (void)setThumbnailPath:(NSString *)imagePath highDefinitionImageURL:(NSString *)imageURL
{
if (imagePath == nil) {
[self.contentLayer setContents:nil];
}
else {
UIImage *image = [[UIImage imageNamed:imagePath] copy];
[self.contentLayer setContents:(id)(image.CGImage)];
}
}
- (void)setBackgroundImage:(UIImage *)backgroundImage
{
UIImage *image = [backgroundImage copy];
[self.maskLayer setContents:(id)image.CGImage];
}
@end
@@ -0,0 +1,15 @@
//
// TLTextMessageCell.h
// TLChat
//
// Created by 李伯坤 on 16/3/1.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMessageBaseCell.h"
#import "TLTextMessage.h"
@interface TLTextMessageCell : TLMessageBaseCell
@end
@@ -0,0 +1,88 @@
//
// TLTextMessageCell.m
// TLChat
//
// Created by 李伯坤 on 16/3/1.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLTextMessageCell.h"
#define MSG_SPACE_TOP 14
#define MSG_SPACE_BTM 20
#define MSG_SPACE_LEFT 19
#define MSG_SPACE_RIGHT 22
@interface TLTextMessageCell ()
@property (nonatomic, strong) UILabel *messageLabel;
@end
@implementation TLTextMessageCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self.contentView addSubview:self.messageLabel];
}
return self;
}
- (void)setMessage:(TLTextMessage *)message
{
if (self.message && [self.message.messageID isEqualToString:message.messageID]) {
return;
}
TLMessageOwnerType lastOwnType = self.message ? self.message.ownerTyper : -1;
[super setMessage:message];
[self.messageLabel setAttributedText:[message attrText]];
[self.messageLabel setContentCompressionResistancePriority:500 forAxis:UILayoutConstraintAxisHorizontal];
[self.messageBackgroundView setContentCompressionResistancePriority:100 forAxis:UILayoutConstraintAxisHorizontal];
if (lastOwnType != message.ownerTyper) {
if (message.ownerTyper == TLMessageOwnerTypeSelf) {
[self.messageBackgroundView setImage:[UIImage imageNamed:@"message_sender_bg"]];
[self.messageBackgroundView setHighlightedImage:[UIImage imageNamed:@"message_sender_bgHL"]];
[self.messageLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self.messageBackgroundView).mas_offset(-MSG_SPACE_RIGHT);
make.top.mas_equalTo(self.messageBackgroundView).mas_offset(MSG_SPACE_TOP);
}];
[self.messageBackgroundView mas_updateConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.messageLabel).mas_offset(-MSG_SPACE_LEFT);
make.bottom.mas_equalTo(self.messageLabel).mas_offset(MSG_SPACE_BTM);
}];
}
else if (message.ownerTyper == TLMessageOwnerTypeFriend){
[self.messageBackgroundView setImage:[UIImage imageNamed:@"message_receiver_bg"]];
[self.messageBackgroundView setHighlightedImage:[UIImage imageNamed:@"message_receiver_bgHL"]];
[self.messageLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.messageBackgroundView).mas_offset(MSG_SPACE_LEFT);
make.top.mas_equalTo(self.messageBackgroundView).mas_offset(MSG_SPACE_TOP);
}];
[self.messageBackgroundView mas_updateConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self.messageLabel).mas_offset(MSG_SPACE_RIGHT);
make.bottom.mas_equalTo(self.messageLabel).mas_offset(MSG_SPACE_BTM);
}];
}
}
[self.messageLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(message.messageFrame.contentSize);
}];
}
#pragma mark - Getter -
- (UILabel *)messageLabel
{
if (_messageLabel == nil) {
_messageLabel = [[UILabel alloc] init];
[_messageLabel setFont:[UIFont fontTextMessageText]];
[_messageLabel setNumberOfLines:0];
}
return _messageLabel;
}
@end
@@ -0,0 +1,19 @@
//
// TLVoiceImageView.h
// TLChat
//
// Created by 李伯坤 on 16/7/11.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TLVoiceImageView : UIImageView
@property (nonatomic, assign) BOOL isFromMe;
- (void)startPlayingAnimation;
- (void)stopPlayingAnimation;
@end
@@ -0,0 +1,60 @@
//
// TLVoiceImageView.m
// TLChat
//
// Created by 李伯坤 on 16/7/11.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLVoiceImageView.h"
@interface TLVoiceImageView ()
@property (nonatomic, strong) NSArray *imagesArray;
@property (nonatomic, strong) UIImage *normalImage;
@end
@implementation TLVoiceImageView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setIsFromMe:YES];
}
return self;
}
- (void)setIsFromMe:(BOOL)isFromMe
{
_isFromMe = isFromMe;
if (isFromMe) {
self.imagesArray = @[[UIImage imageNamed:@"message_voice_sender_playing_1"],
[UIImage imageNamed:@"message_voice_sender_playing_2"],
[UIImage imageNamed:@"message_voice_sender_playing_3"]];
self.normalImage = [UIImage imageNamed:@"message_voice_sender_normal"];
}
else {
self.imagesArray = @[[UIImage imageNamed:@"message_voice_receiver_playing_1"],
[UIImage imageNamed:@"message_voice_receiver_playing_2"],
[UIImage imageNamed:@"message_voice_receiver_playing_3"]];
self.normalImage = [UIImage imageNamed:@"message_voice_receiver_normal"];
}
[self setImage:self.normalImage];
}
- (void)startPlayingAnimation
{
self.animationImages = self.imagesArray;
self.animationRepeatCount = 0;
self.animationDuration = 1.0;
[self startAnimating];
}
- (void)stopPlayingAnimation
{
[self stopAnimating];
}
@end
@@ -0,0 +1,14 @@
//
// TLVoiceMessageCell.h
// TLChat
//
// Created by 李伯坤 on 16/5/6.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMessageBaseCell.h"
#import "TLVoiceMessage.h"
@interface TLVoiceMessageCell : TLMessageBaseCell
@end
@@ -0,0 +1,174 @@
//
// TLVoiceMessageCell.m
// TLChat
//
// Created by 李伯坤 on 16/5/6.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLVoiceMessageCell.h"
#import "TLVoiceImageView.h"
@interface TLVoiceMessageCell ()
@property (nonatomic, strong) UILabel *voiceTimeLabel;
@property (nonatomic, strong) TLVoiceImageView *voiceImageView;
@end
@implementation TLVoiceMessageCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self.contentView addSubview:self.voiceTimeLabel];
[self.messageBackgroundView addSubview:self.voiceImageView];
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapMsgBGView)];
[self.messageBackgroundView addGestureRecognizer:tapGR];
}
return self;
}
- (void)setMessage:(TLVoiceMessage *)message
{
TLMessageOwnerType lastOwnType = self.message ? self.message.ownerTyper : -1;
[super setMessage:message];
[self.voiceTimeLabel setText:[NSString stringWithFormat:@"%.0lf\"\n", message.time]];
if (lastOwnType != message.ownerTyper) {
if (message.ownerTyper == TLMessageOwnerTypeSelf) {
[self.voiceImageView setIsFromMe:YES];
[self.messageBackgroundView setImage:[UIImage imageNamed:@"message_sender_bg"]];
[self.messageBackgroundView setHighlightedImage:[UIImage imageNamed:@"message_sender_bgHL"]];
[self.voiceTimeLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self.messageBackgroundView.mas_left);
make.top.mas_equalTo(self.messageBackgroundView.mas_centerY).mas_offset(-5);
}];
[self.voiceImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(-13);
make.centerY.mas_equalTo(self.avatarButton);
}];
}
else if (message.ownerTyper == TLMessageOwnerTypeFriend){
[self.voiceImageView setIsFromMe:NO];
[self.messageBackgroundView setImage:[UIImage imageNamed:@"message_receiver_bg"]];
[self.messageBackgroundView setHighlightedImage:[UIImage imageNamed:@"message_receiver_bgHL"]];
[self.voiceTimeLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.messageBackgroundView.mas_right);
make.top.mas_equalTo(self.messageBackgroundView.mas_centerY).mas_offset(-5);
}];
[self.voiceImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(13);
make.centerY.mas_equalTo(self.avatarButton);
}];
}
}
[self.messageBackgroundView mas_updateConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(message.messageFrame.contentSize);
}];
if (message.msgStatus == TLVoiceMessageStatusRecording) {
[self.voiceTimeLabel setHidden:YES];
[self.voiceImageView setHidden:YES];
[self p_startRecordingAnimation];
}
else {
[self.voiceTimeLabel setHidden:NO];
[self.voiceImageView setHidden:NO];
[self p_stopRecordingAnimation];
[self.messageBackgroundView setAlpha:1.0];
}
message.msgStatus == TLVoiceMessageStatusPlaying ? [self.voiceImageView startPlayingAnimation] : [self.voiceImageView stopPlayingAnimation];
}
- (void)updateMessage:(TLVoiceMessage *)message
{
[super setMessage:message];
[self.voiceTimeLabel setText:[NSString stringWithFormat:@"%.0lf\"\n", message.time]];
if (message.msgStatus == TLVoiceMessageStatusRecording) {
[self.voiceTimeLabel setHidden:YES];
[self.voiceImageView setHidden:YES];
[self p_startRecordingAnimation];
}
else {
[self.voiceTimeLabel setHidden:NO];
[self.voiceImageView setHidden:NO];
[self p_stopRecordingAnimation];
}
message.msgStatus == TLVoiceMessageStatusPlaying ? [self.voiceImageView startPlayingAnimation] : [self.voiceImageView stopPlayingAnimation];
[UIView animateWithDuration:0.5 animations:^{
[self.messageBackgroundView mas_updateConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(message.messageFrame.contentSize);
}];
[self layoutIfNeeded];
}];
}
#pragma mark - # Event Response
- (void)didTapMsgBGView
{
[self.voiceImageView startPlayingAnimation];
if (self.delegate && [self.delegate respondsToSelector:@selector(messageCellTap:)]) {
[self.delegate messageCellTap:self.message];
}
}
#pragma mark - # Private Methods
static bool isStartAnimation = NO;
static float bgAlpha = 1.0;
- (void)p_startRecordingAnimation
{
isStartAnimation = YES;
bgAlpha = 0.4;
[self p_repeatAnimation];
}
- (void)p_repeatAnimation
{
[UIView animateWithDuration:1.0 animations:^{
[self.messageBackgroundView setAlpha:bgAlpha];
} completion:^(BOOL finished) {
if (finished) {
bgAlpha = bgAlpha > 0.9 ? 0.4 : 1.0;
if (isStartAnimation) {
[self p_repeatAnimation];
}
else {
[self.messageBackgroundView setAlpha:1.0];
}
}
}];
}
- (void)p_stopRecordingAnimation
{
isStartAnimation = NO;
}
#pragma mark - # Getter
- (UILabel *)voiceTimeLabel
{
if (_voiceTimeLabel == nil) {
_voiceTimeLabel = [[UILabel alloc] init];
[_voiceTimeLabel setTextColor:[UIColor grayColor]];
[_voiceTimeLabel setFont:[UIFont systemFontOfSize:14.0f]];
}
return _voiceTimeLabel;
}
- (TLVoiceImageView *)voiceImageView
{
if (_voiceImageView == nil) {
_voiceImageView = [[TLVoiceImageView alloc] init];
}
return _voiceImageView;
}
@end