chore: import upstream snapshot with attribution
This commit is contained in:
+14
@@ -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
|
||||
+124
@@ -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
|
||||
+14
@@ -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
|
||||
+118
@@ -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
|
||||
+32
@@ -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
|
||||
+221
@@ -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
|
||||
+24
@@ -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
|
||||
+23
@@ -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
|
||||
+66
@@ -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
|
||||
+15
@@ -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
|
||||
+88
@@ -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
|
||||
+19
@@ -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
|
||||
+60
@@ -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
|
||||
+14
@@ -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
|
||||
+174
@@ -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
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TLChatMessageDisplayView+Delegate.h
|
||||
// TLChat
|
||||
//
|
||||
// Created by 李伯坤 on 16/3/17.
|
||||
// Copyright © 2016年 李伯坤. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TLChatMessageDisplayView.h"
|
||||
#import "TLTextMessageCell.h"
|
||||
#import "TLImageMessageCell.h"
|
||||
#import "TLExpressionMessageCell.h"
|
||||
#import "TLVoiceMessageCell.h"
|
||||
|
||||
@interface TLChatMessageDisplayView (Delegate) <UITableViewDelegate, UITableViewDataSource, TLMessageCellDelegate, TLActionSheetDelegate>
|
||||
|
||||
- (void)registerCellClassForTableView:(UITableView *)tableView;
|
||||
|
||||
@end
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// TLChatMessageDisplayView+Delegate.m
|
||||
// TLChat
|
||||
//
|
||||
// Created by 李伯坤 on 16/3/17.
|
||||
// Copyright © 2016年 李伯坤. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TLChatMessageDisplayView+Delegate.h"
|
||||
#import "TLTextDisplayView.h"
|
||||
#import "TLChatEventStatistics.h"
|
||||
|
||||
@implementation TLChatMessageDisplayView (Delegate)
|
||||
|
||||
#pragma mark - # Public Methods
|
||||
- (void)registerCellClassForTableView:(UITableView *)tableView
|
||||
{
|
||||
[tableView registerClass:[TLTextMessageCell class] forCellReuseIdentifier:@"TLTextMessageCell"];
|
||||
[tableView registerClass:[TLImageMessageCell class] forCellReuseIdentifier:@"TLImageMessageCell"];
|
||||
[tableView registerClass:[TLExpressionMessageCell class] forCellReuseIdentifier:@"TLExpressionMessageCell"];
|
||||
[tableView registerClass:[TLVoiceMessageCell class] forCellReuseIdentifier:@"TLVoiceMessageCell"];
|
||||
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"EmptyCell"];
|
||||
}
|
||||
|
||||
#pragma mark - # Delegate
|
||||
//MARK: UITableViewDataSouce
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return self.data.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TLMessage * message = self.data[indexPath.row];
|
||||
if (message.messageType == TLMessageTypeText) {
|
||||
TLTextMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TLTextMessageCell"];
|
||||
[cell setMessage:message];
|
||||
[cell setDelegate:self];
|
||||
return cell;
|
||||
}
|
||||
else if (message.messageType == TLMessageTypeImage) {
|
||||
TLImageMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TLImageMessageCell"];
|
||||
[cell setMessage:message];
|
||||
[cell setDelegate:self];
|
||||
return cell;
|
||||
}
|
||||
else if (message.messageType == TLMessageTypeExpression) {
|
||||
TLExpressionMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TLExpressionMessageCell"];
|
||||
[cell setMessage:message];
|
||||
[cell setDelegate:self];
|
||||
return cell;
|
||||
}
|
||||
else if (message.messageType == TLMessageTypeVoice) {
|
||||
TLVoiceMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TLVoiceMessageCell"];
|
||||
[cell setMessage:message];
|
||||
[cell setDelegate:self];
|
||||
return cell;
|
||||
}
|
||||
|
||||
return [tableView dequeueReusableCellWithIdentifier:@"EmptyCell"];
|
||||
}
|
||||
|
||||
//MARK: UITableViewDelegate
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.row >= self.data.count) {
|
||||
return 0.0f;
|
||||
}
|
||||
TLMessage * message = self.data[indexPath.row];
|
||||
return message.messageFrame.height;
|
||||
}
|
||||
|
||||
//MARK: TLMessageCellDelegate
|
||||
- (void)messageCellDidClickAvatarForUser:(TLUser *)user
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(chatMessageDisplayView:didClickUserAvatar:)]) {
|
||||
[self.delegate chatMessageDisplayView:self didClickUserAvatar:user];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)messageCellTap:(TLMessage *)message
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(chatMessageDisplayView:didClickMessage:)]) {
|
||||
[self.delegate chatMessageDisplayView:self didClickMessage:message];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 双击Message Cell
|
||||
*/
|
||||
- (void)messageCellDoubleClick:(TLMessage *)message
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(chatMessageDisplayView:didDoubleClickMessage:)]) {
|
||||
[self.delegate chatMessageDisplayView:self didDoubleClickMessage:message];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)messageCellLongPress:(TLMessage *)message rect:(CGRect)rect
|
||||
{
|
||||
NSInteger row = [self.data indexOfObject:message];
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
|
||||
if (self.disableLongPressMenu) {
|
||||
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
|
||||
return;
|
||||
}
|
||||
|
||||
CGRect cellRect = [self.tableView rectForRowAtIndexPath:indexPath];
|
||||
rect.origin.y += cellRect.origin.y - self.tableView.contentOffset.y;
|
||||
__weak typeof(self)weakSelf = self;
|
||||
[self.menuView showInView:self withMessageType:message.messageType rect:rect actionBlock:^(TLChatMenuItemType type) {
|
||||
[weakSelf.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
|
||||
if (type == TLChatMenuItemTypeCopy) {
|
||||
NSString *str = [message messageCopy];
|
||||
[[UIPasteboard generalPasteboard] setString:str];
|
||||
}
|
||||
else if (type == TLChatMenuItemTypeDelete) {
|
||||
TLActionSheet *actionSheet = [[TLActionSheet alloc] initWithTitle:@"是否删除该条消息" delegate:weakSelf cancelButtonTitle:@"取消" destructiveButtonTitle:@"确定" otherButtonTitles: nil];
|
||||
actionSheet.tag = [weakSelf.data indexOfObject:message];
|
||||
[actionSheet show];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
//MARK: UIScrollViewDelegate
|
||||
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(chatMessageDisplayViewDidTouched:)]) {
|
||||
[self.delegate chatMessageDisplayViewDidTouched:self];
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: TLActionSheetDelegate
|
||||
- (void)actionSheet:(TLActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
|
||||
{
|
||||
if (buttonIndex == 0) {
|
||||
TLMessage * message = [self.data objectAtIndex:actionSheet.tag];
|
||||
[self p_deleteMessage:message];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - # Private Methods
|
||||
- (void)p_deleteMessage:(TLMessage *)message
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(chatMessageDisplayView:deleteMessage:)]) {
|
||||
BOOL ok = [self.delegate chatMessageDisplayView:self deleteMessage:message];
|
||||
if (ok) {
|
||||
[self deleteMessage:message withAnimation:YES];
|
||||
[MobClick event:EVENT_DELETE_MESSAGE];
|
||||
}
|
||||
else {
|
||||
[TLUIUtility showAlertWithTitle:@"错误" message:@"从数据库中删除消息失败。"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// TLChatMessageDisplayView.h
|
||||
// TLChat
|
||||
//
|
||||
// Created by 李伯坤 on 16/3/9.
|
||||
// Copyright © 2016年 李伯坤. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TLChatMessageDisplayViewDelegate.h"
|
||||
#import "TLChatCellMenuView.h"
|
||||
|
||||
#import "TLTextMessage.h"
|
||||
#import "TLImageMessage.h"
|
||||
#import "TLExpressionMessage.h"
|
||||
#import "TLVoiceMessage.h"
|
||||
|
||||
|
||||
@interface TLChatMessageDisplayView : UIView
|
||||
|
||||
@property (nonatomic, assign) id<TLChatMessageDisplayViewDelegate>delegate;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *data;
|
||||
|
||||
@property (nonatomic, strong, readonly) UITableView *tableView;
|
||||
|
||||
/// 禁用下拉刷新
|
||||
@property (nonatomic, assign) BOOL disablePullToRefresh;
|
||||
|
||||
/// 禁用长按菜单
|
||||
@property (nonatomic, assign) BOOL disableLongPressMenu;
|
||||
|
||||
@property (nonatomic, strong) TLChatCellMenuView *menuView;
|
||||
|
||||
|
||||
/**
|
||||
* 发送消息(在列表展示)
|
||||
*/
|
||||
- (void)addMessage:(TLMessage *)message;
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
*/
|
||||
- (void)deleteMessage:(TLMessage *)message;
|
||||
- (void)deleteMessage:(TLMessage *)message withAnimation:(BOOL)animation;
|
||||
|
||||
/**
|
||||
* 更新消息状态
|
||||
*/
|
||||
- (void)updateMessage:(TLMessage *)message;
|
||||
- (void)reloadData;
|
||||
|
||||
/**
|
||||
* 滚动到底部
|
||||
*
|
||||
* @param animation 是否执行动画
|
||||
*/
|
||||
- (void)scrollToBottomWithAnimation:(BOOL)animation;
|
||||
|
||||
/**
|
||||
* 重新加载聊天信息
|
||||
*/
|
||||
- (void)resetMessageView;
|
||||
|
||||
@end
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
//
|
||||
// TLChatMessageDisplayView.m
|
||||
// TLChat
|
||||
//
|
||||
// Created by 李伯坤 on 16/3/9.
|
||||
// Copyright © 2016年 李伯坤. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TLChatMessageDisplayView.h"
|
||||
#import "TLChatMessageDisplayView+Delegate.h"
|
||||
#import <MJRefresh/MJRefresh.h>
|
||||
#import "TLMessageBaseCell.h"
|
||||
#import "TLChatEventStatistics.h"
|
||||
|
||||
#define PAGE_MESSAGE_COUNT 15
|
||||
|
||||
@interface TLChatMessageDisplayView ()
|
||||
|
||||
@property (nonatomic, strong) MJRefreshNormalHeader *refresHeader;
|
||||
|
||||
/// 用户决定新消息是否显示时间
|
||||
@property (nonatomic, strong) NSDate *curDate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TLChatMessageDisplayView
|
||||
@synthesize tableView = _tableView;
|
||||
@synthesize data = _data;
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self addSubview:self.tableView];
|
||||
[self setDisablePullToRefresh:NO];
|
||||
[self registerCellClassForTableView:self.tableView];
|
||||
|
||||
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.mas_equalTo(0);
|
||||
}];
|
||||
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTouchTableView)];
|
||||
[self.tableView addGestureRecognizer:tap];
|
||||
|
||||
[self.tableView addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self.menuView dismiss];
|
||||
[self.tableView removeObserver:self forKeyPath:@"bounds"];
|
||||
#ifdef DEBUG_MEMERY
|
||||
NSLog(@"dealloc MessageDisplayView");
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - # Public Methods
|
||||
- (void)resetMessageView
|
||||
{
|
||||
[self.data removeAllObjects];
|
||||
[self.tableView reloadData];
|
||||
self.curDate = [NSDate date];
|
||||
if (!self.disablePullToRefresh) {
|
||||
[self.tableView setMj_header:self.refresHeader];
|
||||
}
|
||||
TLWeakSelf(self);
|
||||
[self p_tryToRefreshMoreRecord:^(NSInteger count, BOOL hasMore) {
|
||||
if (!hasMore) {
|
||||
weakself.tableView.mj_header = nil;
|
||||
}
|
||||
if (count > 0) {
|
||||
[weakself.tableView reloadData];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakself.tableView scrollToBottomWithAnimation:NO];
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addMessage:(TLMessage *)message
|
||||
{
|
||||
[self.data addObject:message];
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)setData:(NSMutableArray *)data
|
||||
{
|
||||
_data = data;
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)deleteMessage:(TLMessage *)message
|
||||
{
|
||||
[self deleteMessage:message withAnimation:YES];
|
||||
}
|
||||
|
||||
- (void)deleteMessage:(TLMessage *)message withAnimation:(BOOL)animation
|
||||
{
|
||||
if (message == nil) {
|
||||
return;
|
||||
}
|
||||
NSInteger index = [self.data indexOfObject:message];
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(chatMessageDisplayView:deleteMessage:)]) {
|
||||
BOOL ok = [self.delegate chatMessageDisplayView:self deleteMessage:message];
|
||||
if (ok) {
|
||||
[self.data removeObject:message];
|
||||
[self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:animation ? UITableViewRowAnimationAutomatic : UITableViewRowAnimationNone];
|
||||
[MobClick event:EVENT_DELETE_MESSAGE];
|
||||
}
|
||||
else {
|
||||
[TLUIUtility showAlertWithTitle:@"错误" message:@"从数据库中删除消息失败。"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateMessage:(TLMessage *)message
|
||||
{
|
||||
NSArray *visibleCells = [self.tableView visibleCells];
|
||||
for (id cell in visibleCells) {
|
||||
if ([cell isKindOfClass:[TLMessageBaseCell class]]) {
|
||||
if ([[(TLMessageBaseCell *)cell message].messageID isEqualToString:message.messageID]) {
|
||||
[cell updateMessage:message];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)scrollToBottomWithAnimation:(BOOL)animation
|
||||
{
|
||||
[self.tableView scrollToBottomWithAnimation:animation];
|
||||
}
|
||||
|
||||
- (void)setDisablePullToRefresh:(BOOL)disablePullToRefresh
|
||||
{
|
||||
if (disablePullToRefresh) {
|
||||
[self.tableView setMj_header:nil];
|
||||
}
|
||||
else {
|
||||
[self.tableView setMj_header:self.refresHeader];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - # Event Response
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
|
||||
{
|
||||
if (object == self.tableView && [keyPath isEqualToString:@"bounds"]) { // tableView变小时,消息贴底
|
||||
CGRect oldBounds, newBounds;
|
||||
[change[@"old"] getValue:&oldBounds];
|
||||
[change[@"new"] getValue:&newBounds];
|
||||
CGFloat t = oldBounds.size.height - newBounds.size.height;
|
||||
if (t > 0 && fabs(self.tableView.contentOffset.y + t + newBounds.size.height - self.tableView.contentSize.height) < 1.0) {
|
||||
[self scrollToBottomWithAnimation:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didTouchTableView
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(chatMessageDisplayViewDidTouched:)]) {
|
||||
[self.delegate chatMessageDisplayViewDidTouched:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - # Private Methods
|
||||
/**
|
||||
* 获取聊天历史记录
|
||||
*/
|
||||
- (void)p_tryToRefreshMoreRecord:(void (^)(NSInteger count, BOOL hasMore))complete
|
||||
{
|
||||
TLWeakSelf(self);
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(chatMessageDisplayView:getRecordsFromDate:count:completed:)]) {
|
||||
[self.delegate chatMessageDisplayView:self
|
||||
getRecordsFromDate:self.curDate
|
||||
count:PAGE_MESSAGE_COUNT
|
||||
completed:^(NSDate *date, NSArray *array, BOOL hasMore) {
|
||||
if (array.count > 0 && [date isEqualToDate:weakself.curDate]) {
|
||||
weakself.curDate = [array[0] date];
|
||||
[weakself.data insertObjects:array atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, array.count)]];
|
||||
complete(array.count, hasMore);
|
||||
}
|
||||
else {
|
||||
complete(0, hasMore);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - # Getter
|
||||
- (UITableView *)tableView
|
||||
{
|
||||
if (_tableView == nil) {
|
||||
_tableView = [[UITableView alloc] init];
|
||||
[_tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
|
||||
[_tableView setBackgroundColor:[UIColor clearColor]];
|
||||
[_tableView setTableFooterView:[[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 10)]];
|
||||
[_tableView setDelegate:self];
|
||||
[_tableView setDataSource:self];
|
||||
}
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
- (TLChatCellMenuView *)menuView
|
||||
{
|
||||
if (!_menuView) {
|
||||
_menuView = [[TLChatCellMenuView alloc] init];
|
||||
}
|
||||
return _menuView;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)data
|
||||
{
|
||||
if (_data == nil) {
|
||||
_data = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
|
||||
- (MJRefreshNormalHeader *)refresHeader
|
||||
{
|
||||
if (_refresHeader == nil) {
|
||||
TLWeakSelf(self);
|
||||
_refresHeader = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
|
||||
[weakself p_tryToRefreshMoreRecord:^(NSInteger count, BOOL hasMore) {
|
||||
[weakself.tableView.mj_header endRefreshing];
|
||||
if (!hasMore) {
|
||||
weakself.tableView.mj_header = nil;
|
||||
}
|
||||
if (count > 0) {
|
||||
[weakself.tableView reloadData];
|
||||
[weakself.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:count inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
_refresHeader.lastUpdatedTimeLabel.hidden = YES;
|
||||
_refresHeader.stateLabel.hidden = YES;
|
||||
}
|
||||
return _refresHeader;
|
||||
}
|
||||
|
||||
@end
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// TLChatMessageDisplayViewDelegate.h
|
||||
// TLChat
|
||||
//
|
||||
// Created by 李伯坤 on 16/3/23.
|
||||
// Copyright © 2016年 李伯坤. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class TLUser;
|
||||
@class TLMessage;
|
||||
@class TLChatMessageDisplayView;
|
||||
@protocol TLChatMessageDisplayViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
* 聊天界面点击事件,用于收键盘
|
||||
*/
|
||||
- (void)chatMessageDisplayViewDidTouched:(TLChatMessageDisplayView *)chatTVC;
|
||||
|
||||
/**
|
||||
* 下拉刷新,获取某个时间段的聊天记录(异步)
|
||||
*
|
||||
* @param chatTVC chatTVC
|
||||
* @param date 开始时间
|
||||
* @param count 条数
|
||||
* @param completed 结果Blcok
|
||||
*/
|
||||
- (void)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC
|
||||
getRecordsFromDate:(NSDate *)date
|
||||
count:(NSUInteger)count
|
||||
completed:(void (^)(NSDate *, NSArray *, BOOL))completed;
|
||||
|
||||
@optional
|
||||
/**
|
||||
* 消息长按删除
|
||||
*
|
||||
* @return 删除是否成功
|
||||
*/
|
||||
- (BOOL)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC
|
||||
deleteMessage:(TLMessage *)message;
|
||||
|
||||
/**
|
||||
* 用户头像点击事件
|
||||
*/
|
||||
- (void)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC
|
||||
didClickUserAvatar:(TLUser *)user;
|
||||
|
||||
/**
|
||||
* Message点击事件
|
||||
*/
|
||||
- (void)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC
|
||||
didClickMessage:(TLMessage *)message;
|
||||
|
||||
/**
|
||||
* Message双击事件
|
||||
*/
|
||||
- (void)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC
|
||||
didDoubleClickMessage:(TLMessage *)message;
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user