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,26 @@
//
// TLTalkButton.h
// TLChat
//
// Created by 李伯坤 on 16/7/11.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TLTalkButton : UIView
@property (nonatomic, strong) NSString *normalTitle;
@property (nonatomic, strong) NSString *cancelTitle;
@property (nonatomic, strong) NSString *highlightTitle;
@property (nonatomic, strong) UIColor *highlightColor;
@property (nonatomic, strong) UILabel *titleLabel;
- (void)setTouchBeginAction:(void (^)())touchBegin
willTouchCancelAction:(void (^)(BOOL cancel))willTouchCancel
touchEndAction:(void (^)())touchEnd
touchCancelAction:(void (^)())touchCancel;
@end
@@ -0,0 +1,115 @@
//
// TLTalkButton.m
// TLChat
//
// Created by 李伯坤 on 16/7/11.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLTalkButton.h"
#import "UIImage+Color.h"
@interface TLTalkButton ()
@property (nonatomic, strong) void (^touchBegin)();
@property (nonatomic, strong) void (^touchMove)(BOOL cancel);
@property (nonatomic, strong) void (^touchCancel)();
@property (nonatomic, strong) void (^touchEnd)();
@end
@implementation TLTalkButton
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setNormalTitle:@"按住 说话"];
[self setHighlightTitle:@"松开 结束"];
[self setCancelTitle:@"送开 取消"];
[self setHighlightColor:[UIColor colorWithWhite:0.0 alpha:0.1]];
[self.layer setMasksToBounds:YES];
[self.layer setCornerRadius:4.0f];
[self.layer setBorderWidth:BORDER_WIDTH_1PX];
[self.layer setBorderColor:[UIColor colorWithWhite:0.0 alpha:0.3].CGColor];
[self addSubview:self.titleLabel];
[self p_addMasonry];
}
return self;
}
#pragma mark - # Public Methods
- (void)setTouchBeginAction:(void (^)())touchBegin willTouchCancelAction:(void (^)(BOOL))willTouchCancel touchEndAction:(void (^)())touchEnd touchCancelAction:(void (^)())touchCancel
{
self.touchBegin = touchBegin;
self.touchMove = willTouchCancel;
self.touchCancel= touchCancel;
self.touchEnd = touchEnd;
}
#pragma mark - # Event Response
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self setBackgroundColor:self.highlightColor];
[self.titleLabel setText:self.highlightTitle];
if (self.touchBegin) {
self.touchBegin();
}
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (self.touchMove) {
CGPoint curPoint = [[touches anyObject] locationInView:self];
BOOL moveIn = curPoint.x >= 0 && curPoint.x <= self.width && curPoint.y >= 0 && curPoint.y <= self.height;
[self.titleLabel setText:(moveIn ? self.highlightTitle : self.cancelTitle)];
self.touchMove(!moveIn);
}
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self setBackgroundColor:[UIColor clearColor]];
[self.titleLabel setText:self.normalTitle];
CGPoint curPoint = [[touches anyObject] locationInView:self];
BOOL moveIn = curPoint.x >= 0 && curPoint.x <= self.width && curPoint.y >= 0 && curPoint.y <= self.height;
if (moveIn && self.touchEnd) {
self.touchEnd();
}
else if (!moveIn && self.touchCancel) {
self.touchCancel();
}
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self setBackgroundColor:[UIColor clearColor]];
[self.titleLabel setText:self.normalTitle];
if (self.touchCancel) {
self.touchCancel();
}
}
#pragma mark - # Private Methods
- (void)p_addMasonry
{
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self);
}];
}
#pragma mark - # Getter
- (UILabel *)titleLabel
{
if (_titleLabel == nil) {
_titleLabel = [[UILabel alloc] init];
[_titleLabel setFont:[UIFont boldSystemFontOfSize:16.0f]];
[_titleLabel setTextColor:[UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1.0]];
[_titleLabel setTextAlignment:NSTextAlignmentCenter];
[_titleLabel setText:self.normalTitle];
}
return _titleLabel;
}
@end
@@ -0,0 +1,38 @@
//
// TLChatBar.h
// TLChat
//
// Created by 李伯坤 on 16/2/15.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLChatBarDelegate.h"
@interface TLChatBar : UIView
@property (nonatomic, assign) id<TLChatBarDelegate> delegate;
@property (nonatomic, assign) TLChatBarStatus status;
@property (nonatomic, strong, readonly) NSString *curText;
/// 是否激活状态(浏览个性表情时应该设置为NO)
@property (nonatomic, assign) BOOL activity;
/**
* 添加EmojiB表情String
*/
- (void)addEmojiString:(NSString *)emojiString;
/**
* 发送文字消息
*/
- (void)sendCurrentText;
/**
* 删除最后一个字符
*/
- (void)deleteLastCharacter;
@end
@@ -0,0 +1,525 @@
//
// TLChatBar.m
// TLChat
//
// Created by 李伯坤 on 16/2/15.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatBar.h"
#import "TLChatMacros.h"
#import "TLTalkButton.h"
@interface TLChatBar () <UITextViewDelegate>
{
UIImage *kVoiceImage;
UIImage *kVoiceImageHL;
UIImage *kEmojiImage;
UIImage *kEmojiImageHL;
UIImage *kMoreImage;
UIImage *kMoreImageHL;
UIImage *kKeyboardImage;
UIImage *kKeyboardImageHL;
}
@property (nonatomic, strong) UIButton *modeButton;
@property (nonatomic, strong) UIButton *voiceButton;
@property (nonatomic, strong) UITextView *textView;
@property (nonatomic, strong) TLTalkButton *talkButton;
@property (nonatomic, strong) UIButton *emojiButton;
@property (nonatomic, strong) UIButton *moreButton;
@end
@implementation TLChatBar
- (id)init
{
if (self = [super init]) {
[self setBackgroundColor:[UIColor colorGrayForChatBar]];
[self p_initImage];
[self addSubview:self.modeButton];
[self addSubview:self.voiceButton];
[self addSubview:self.textView];
[self addSubview:self.talkButton];
[self addSubview:self.emojiButton];
[self addSubview:self.moreButton];
[self p_addMasonry];
self.status = TLChatBarStatusInit;
}
return self;
}
- (void)dealloc
{
#ifdef DEBUG_MEMERY
NSLog(@"dealloc ChatBar");
#endif
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self setNeedsDisplay];
}
#pragma mark - Public Methods
- (void)sendCurrentText
{
if (self.textView.text.length > 0) { // send Text
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:sendText:)]) {
[_delegate chatBar:self sendText:self.textView.text];
}
}
[self.textView setText:@""];
[self p_reloadTextViewWithAnimation:YES];
}
- (void)addEmojiString:(NSString *)emojiString
{
NSString *str = [NSString stringWithFormat:@"%@%@", self.textView.text, emojiString];
[self.textView setText:str];
[self p_reloadTextViewWithAnimation:YES];
}
- (void)deleteLastCharacter
{
if([self textView:self.textView shouldChangeTextInRange:NSMakeRange(self.textView.text.length - 1, 1) replacementText:@""]){
[self.textView deleteBackward];
}
}
- (void)setActivity:(BOOL)activity
{
_activity = activity;
if (activity) {
[self.textView setTextColor:[UIColor blackColor]];
}
else {
[self.textView setTextColor:[UIColor grayColor]];
}
}
- (BOOL)isFirstResponder
{
if (self.status == TLChatBarStatusEmoji || self.status == TLChatBarStatusKeyboard || self.status == TLChatBarStatusMore) {
return YES;
}
return NO;
}
- (BOOL)resignFirstResponder
{
[self.moreButton setImage:kMoreImage imageHL:kMoreImageHL];
[self.emojiButton setImage:kEmojiImage imageHL:kEmojiImageHL];
if (self.status == TLChatBarStatusKeyboard) {
[self.textView resignFirstResponder];
self.status = TLChatBarStatusInit;
if (self.delegate && [self.delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusInit];
}
}
return [super resignFirstResponder];
}
#pragma mark - Delegate -
//MARK: UITextViewDelegate
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
[self setActivity:YES];
if (self.status != TLChatBarStatusKeyboard) {
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusKeyboard];
}
if (self.status == TLChatBarStatusEmoji) {
[self.emojiButton setImage:kEmojiImage imageHL:kEmojiImageHL];
}
else if (self.status == TLChatBarStatusMore) {
[self.moreButton setImage:kMoreImage imageHL:kMoreImageHL];
}
self.status = TLChatBarStatusKeyboard;
}
return YES;
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if ([text isEqualToString:@"\n"]){
[self sendCurrentText];
return NO;
}
else if (textView.text.length > 0 && [text isEqualToString:@""]) { // delete
if ([textView.text characterAtIndex:range.location] == ']') {
NSUInteger location = range.location;
NSUInteger length = range.length;
while (location != 0) {
location --;
length ++ ;
char c = [textView.text characterAtIndex:location];
if (c == '[') {
textView.text = [textView.text stringByReplacingCharactersInRange:NSMakeRange(location, length) withString:@""];
[self p_reloadTextViewWithAnimation:YES];
return NO;
}
else if (c == ']') {
return YES;
}
}
}
}
return YES;
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
[self p_reloadTextViewWithAnimation:YES];
}
- (void)textViewDidChange:(UITextView *)textView
{
[self p_reloadTextViewWithAnimation:YES];
}
#pragma mark - Event Response
- (void)modeButtonDown
{
if (self.status == TLChatBarStatusEmoji) {
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusInit];
}
[self.emojiButton setImage:kEmojiImage imageHL:kEmojiImageHL];
self.status = TLChatBarStatusInit;
}
else if (self.status == TLChatBarStatusMore) {
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusInit];
}
[self.moreButton setImage:kMoreImage imageHL:kMoreImageHL];
self.status = TLChatBarStatusInit;
}
}
static NSString *textRec = @"";
- (void)voiceButtonDown
{
[self.textView resignFirstResponder];
// 开始文字输入
if (self.status == TLChatBarStatusVoice) {
if (textRec.length > 0) {
[self.textView setText:textRec];
textRec = @"";
[self p_reloadTextViewWithAnimation:YES];
}
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusKeyboard];
}
[self.voiceButton setImage:kVoiceImage imageHL:kVoiceImageHL];
[self.textView becomeFirstResponder];
[self.textView setHidden:NO];
[self.talkButton setHidden:YES];
self.status = TLChatBarStatusKeyboard;
}
else { // 开始语音
if (self.textView.text.length > 0) {
textRec = self.textView.text;
self.textView.text = @"";
[self p_reloadTextViewWithAnimation:YES];
}
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusVoice];
}
if (self.status == TLChatBarStatusKeyboard) {
[self.textView resignFirstResponder];
}
else if (self.status == TLChatBarStatusEmoji) {
[self.emojiButton setImage:kEmojiImage imageHL:kEmojiImageHL];
}
else if (self.status == TLChatBarStatusMore) {
[self.moreButton setImage:kMoreImage imageHL:kMoreImageHL];
}
[self.talkButton setHidden:NO];
[self.textView setHidden:YES];
[self.voiceButton setImage:kKeyboardImage imageHL:kKeyboardImageHL];
self.status = TLChatBarStatusVoice;
}
}
- (void)emojiButtonDown
{
// 开始文字输入
if (self.status == TLChatBarStatusEmoji) {
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusKeyboard];
}
[self.emojiButton setImage:kEmojiImage imageHL:kEmojiImageHL];
[self.textView becomeFirstResponder];
self.status = TLChatBarStatusKeyboard;
}
else { // 打开表情键盘
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusEmoji];
}
if (self.status == TLChatBarStatusVoice) {
[self.voiceButton setImage:kVoiceImage imageHL:kVoiceImageHL];
[self.talkButton setHidden:YES];
[self.textView setHidden:NO];
}
else if (self.status == TLChatBarStatusMore) {
[self.moreButton setImage:kMoreImage imageHL:kMoreImageHL];
}
[self.emojiButton setImage:kKeyboardImage imageHL:kKeyboardImageHL];
[self.textView resignFirstResponder];
self.status = TLChatBarStatusEmoji;
}
}
- (void)moreButtonDown
{
// 开始文字输入
if (self.status == TLChatBarStatusMore) {
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusKeyboard];
}
[self.moreButton setImage:kMoreImage imageHL:kMoreImageHL];
[self.textView becomeFirstResponder];
self.status = TLChatBarStatusKeyboard;
}
else { // 打开更多键盘
if (_delegate && [_delegate respondsToSelector:@selector(chatBar:changeStatusFrom:to:)]) {
[self.delegate chatBar:self changeStatusFrom:self.status to:TLChatBarStatusMore];
}
if (self.status == TLChatBarStatusVoice) {
[self.voiceButton setImage:kVoiceImage imageHL:kVoiceImageHL];
[self.talkButton setHidden:YES];
[self.textView setHidden:NO];
}
else if (self.status == TLChatBarStatusEmoji) {
[self.emojiButton setImage:kEmojiImage imageHL:kEmojiImageHL];
}
[self.moreButton setImage:kKeyboardImage imageHL:kKeyboardImageHL];
[self.textView resignFirstResponder];
self.status = TLChatBarStatusMore;
}
}
#pragma mark - Private Methods
- (void)p_reloadTextViewWithAnimation:(BOOL)animation
{
CGFloat textHeight = [self.textView sizeThatFits:CGSizeMake(self.textView.width, MAXFLOAT)].height;
CGFloat height = textHeight > HEIGHT_CHATBAR_TEXTVIEW ? textHeight : HEIGHT_CHATBAR_TEXTVIEW;
height = (textHeight <= HEIGHT_MAX_CHATBAR_TEXTVIEW ? textHeight : HEIGHT_MAX_CHATBAR_TEXTVIEW);
[self.textView setScrollEnabled:textHeight > height];
if (height != self.textView.height) {
if (animation) {
[UIView animateWithDuration:0.2 animations:^{
[self.textView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(height);
}];
if (self.superview) {
[self.superview layoutIfNeeded];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(chatBar:didChangeTextViewHeight:)]) {
[self.delegate chatBar:self didChangeTextViewHeight:self.textView.height];
}
} completion:^(BOOL finished) {
if (textHeight > height) {
[self.textView setContentOffset:CGPointMake(0, textHeight - height) animated:YES];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(chatBar:didChangeTextViewHeight:)]) {
[self.delegate chatBar:self didChangeTextViewHeight:height];
}
}];
}
else {
[self.textView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(height);
}];
if (self.superview) {
[self.superview layoutIfNeeded];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(chatBar:didChangeTextViewHeight:)]) {
[self.delegate chatBar:self didChangeTextViewHeight:height];
}
if (textHeight > height) {
[self.textView setContentOffset:CGPointMake(0, textHeight - height) animated:YES];
}
}
}
else if (textHeight > height) {
if (animation) {
CGFloat offsetY = self.textView.contentSize.height - self.textView.height;
dispatch_async(dispatch_get_main_queue(), ^{
[self.textView setContentOffset:CGPointMake(0, offsetY) animated:YES];
});
}
else {
[self.textView setContentOffset:CGPointMake(0, self.textView.contentSize.height - self.textView.height) animated:NO];
}
}
}
- (void)p_addMasonry
{
[self.modeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self);
make.bottom.mas_equalTo(self).mas_offset(-4);
make.width.mas_equalTo(0);
}];
[self.voiceButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(self).mas_offset(-7);
make.left.mas_equalTo(self.modeButton.mas_right).mas_offset(1);
make.width.mas_equalTo(38);
}];
[self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self).mas_offset(7);
make.bottom.mas_equalTo(self).mas_offset(-7);
make.left.mas_equalTo(self.voiceButton.mas_right).mas_offset(4);
make.right.mas_equalTo(self.emojiButton.mas_left).mas_offset(-4);
make.height.mas_equalTo(HEIGHT_CHATBAR_TEXTVIEW);
}];
[self.talkButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.textView);
make.size.mas_equalTo(self.textView);
}];
[self.moreButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.width.mas_equalTo(self.voiceButton);
make.right.mas_equalTo(self).mas_offset(-1);
}];
[self.emojiButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.width.mas_equalTo(self.voiceButton);
make.right.mas_equalTo(self.moreButton.mas_left);
}];
}
- (void)p_initImage
{
kVoiceImage = [UIImage imageNamed:@"chat_toolbar_voice"];
kVoiceImageHL = [UIImage imageNamed:@"chat_toolbar_voice_HL"];
kEmojiImage = [UIImage imageNamed:@"chat_toolbar_emotion"];
kEmojiImageHL = [UIImage imageNamed:@"chat_toolbar_emotion_HL"];
kMoreImage = [UIImage imageNamed:@"chat_toolbar_more"];
kMoreImageHL = [UIImage imageNamed:@"chat_toolbar_more_HL"];
kKeyboardImage = [UIImage imageNamed:@"chat_toolbar_keyboard"];
kKeyboardImageHL = [UIImage imageNamed:@"chat_toolbar_keyboard_HL"];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
CGContextSetStrokeColorWithColor(context, [UIColor colorGrayLine].CGColor);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, SCREEN_WIDTH, 0);
CGContextStrokePath(context);
}
#pragma mark - Getter
- (UIButton *)modeButton
{
if (_modeButton == nil) {
_modeButton = [[UIButton alloc] init];
[_modeButton setImage:[UIImage imageNamed:@"chat_toolbar_texttolist"] imageHL:[UIImage imageNamed:@"chat_toolbar_texttolist_HL"]];
[_modeButton addTarget:self action:@selector(modeButtonDown) forControlEvents:UIControlEventTouchUpInside];
}
return _modeButton;
}
- (UIButton *)voiceButton
{
if (_voiceButton == nil) {
_voiceButton = [[UIButton alloc] init];
[_voiceButton setImage:kVoiceImage imageHL:kVoiceImageHL];
[_voiceButton addTarget:self action:@selector(voiceButtonDown) forControlEvents:UIControlEventTouchUpInside];
}
return _voiceButton;
}
- (UITextView *)textView
{
if (_textView == nil) {
_textView = [[UITextView alloc] init];
[_textView setFont:[UIFont systemFontOfSize:16.0f]];
[_textView setReturnKeyType:UIReturnKeySend];
[_textView.layer setMasksToBounds:YES];
[_textView.layer setBorderWidth:BORDER_WIDTH_1PX];
[_textView.layer setBorderColor:[UIColor colorWithWhite:0.0 alpha:0.3].CGColor];
[_textView.layer setCornerRadius:4.0f];
[_textView setDelegate:self];
[_textView setScrollsToTop:NO];
}
return _textView;
}
- (TLTalkButton *)talkButton
{
if (_talkButton == nil) {
_talkButton = [[TLTalkButton alloc] init];
[_talkButton setHidden:YES];
__weak typeof(self) weakSelf = self;
[_talkButton setTouchBeginAction:^{
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(chatBarStartRecording:)]) {
[weakSelf.delegate chatBarStartRecording:weakSelf];
}
} willTouchCancelAction:^(BOOL cancel) {
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(chatBarWillCancelRecording:cancel:)]) {
[weakSelf.delegate chatBarWillCancelRecording:weakSelf cancel:cancel];
}
} touchEndAction:^{
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(chatBarFinishedRecoding:)]) {
[weakSelf.delegate chatBarFinishedRecoding:weakSelf];
}
} touchCancelAction:^{
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(chatBarDidCancelRecording:)]) {
[weakSelf.delegate chatBarDidCancelRecording:weakSelf];
}
}];
}
return _talkButton;
}
- (UIButton *)emojiButton
{
if (_emojiButton == nil) {
_emojiButton = [[UIButton alloc] init];
[_emojiButton setImage:kEmojiImage imageHL:kEmojiImageHL];
[_emojiButton addTarget:self action:@selector(emojiButtonDown) forControlEvents:UIControlEventTouchUpInside];
}
return _emojiButton;
}
- (UIButton *)moreButton
{
if (_moreButton == nil) {
_moreButton = [[UIButton alloc] init];
[_moreButton setImage:kMoreImage imageHL:kMoreImageHL];
[_moreButton addTarget:self action:@selector(moreButtonDown) forControlEvents:UIControlEventTouchUpInside];
}
return _moreButton;
}
- (NSString *)curText
{
return self.textView.text;
}
@end
@@ -0,0 +1,40 @@
//
// TLChatBarDelegate.h
// TLChat
//
// Created by 李伯坤 on 16/3/1.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TLChatMacros.h"
@class TLChatBar;
@protocol TLChatBarDelegate <NSObject>
/**
* chatBar状态改变
*/
- (void)chatBar:(TLChatBar *)chatBar changeStatusFrom:(TLChatBarStatus)fromStatus to:(TLChatBarStatus)toStatus;
/**
* 输入框高度改变
*/
- (void)chatBar:(TLChatBar *)chatBar didChangeTextViewHeight:(CGFloat)height;
/**
* 发送文字
*/
- (void)chatBar:(TLChatBar *)chatBar sendText:(NSString *)text;
// 录音控制
- (void)chatBarStartRecording:(TLChatBar *)chatBar;
- (void)chatBarWillCancelRecording:(TLChatBar *)chatBar cancel:(BOOL)cancel;
- (void)chatBarDidCancelRecording:(TLChatBar *)chatBar;
- (void)chatBarFinishedRecoding:(TLChatBar *)chatBar;
@end
@@ -0,0 +1,30 @@
//
// TLChatBaseViewController+ChatBar.h
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatBaseViewController.h"
#import "TLMoreKeyboard.h"
#import "TLEmojiKeyboard.h"
@interface TLChatBaseViewController (ChatBar) <TLChatBarDelegate, TLKeyboardDelegate, TLEmojiKeyboardDelegate>
/// 表情键盘
@property (nonatomic, strong, readonly) TLEmojiKeyboard *emojiKeyboard;
/// 更多键盘
@property (nonatomic, strong, readonly) TLMoreKeyboard *moreKeyboard;
- (void)loadKeyboard;
- (void)dismissKeyboard;
- (void)keyboardWillShow:(NSNotification *)notification;
- (void)keyboardDidShow:(NSNotification *)notification;
- (void)keyboardWillHide:(NSNotification *)notification;
- (void)keyboardFrameWillChange:(NSNotification *)notification;
@end
@@ -0,0 +1,319 @@
//
// TLChatBaseViewController+ChatBar.m
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatBaseViewController+ChatBar.h"
#import "TLChatBaseViewController+Proxy.h"
#import "TLChatBaseViewController+MessageDisplayView.h"
#import "TLAudioRecorder.h"
#import "TLAudioPlayer.h"
#import "TLUserHelper.h"
#import "NSFileManager+TLChat.h"
@implementation TLChatBaseViewController (ChatBar)
#pragma mark - # Public Methods
- (void)loadKeyboard
{
[self.emojiKeyboard setKeyboardDelegate:self];
[self.emojiKeyboard setDelegate:self];
[self.moreKeyboard setKeyboardDelegate:self];
[self.moreKeyboard setDelegate:self];
}
- (void)dismissKeyboard
{
if (curStatus == TLChatBarStatusMore) {
[self.moreKeyboard dismissWithAnimation:YES];
curStatus = TLChatBarStatusInit;
}
else if (curStatus == TLChatBarStatusEmoji) {
[self.emojiKeyboard dismissWithAnimation:YES];
curStatus = TLChatBarStatusInit;
}
[self.chatBar resignFirstResponder];
}
//MARK: 系统键盘回调
- (void)keyboardWillShow:(NSNotification *)notification
{
if (curStatus != TLChatBarStatusKeyboard) {
return;
}
[self.messageDisplayView scrollToBottomWithAnimation:YES];
}
- (void)keyboardDidShow:(NSNotification *)notification
{
if (curStatus != TLChatBarStatusKeyboard) {
return;
}
if (lastStatus == TLChatBarStatusMore) {
[self.moreKeyboard dismissWithAnimation:NO];
}
else if (lastStatus == TLChatBarStatusEmoji) {
[self.emojiKeyboard dismissWithAnimation:NO];
}
[self.messageDisplayView scrollToBottomWithAnimation:YES];
}
- (void)keyboardFrameWillChange:(NSNotification *)notification
{
if (curStatus != TLChatBarStatusKeyboard && lastStatus != TLChatBarStatusKeyboard) {
return;
}
CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
if (lastStatus == TLChatBarStatusMore || lastStatus == TLChatBarStatusEmoji) {
if (keyboardFrame.size.height <= HEIGHT_CHAT_KEYBOARD) {
return;
}
}
else if (curStatus == TLChatBarStatusEmoji || curStatus == TLChatBarStatusMore) {
return;
}
[self.chatBar mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(self.view).mas_offset(-keyboardFrame.size.height);
}];
[self.view layoutIfNeeded];
[self.messageDisplayView scrollToBottomWithAnimation:YES];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
if (curStatus != TLChatBarStatusKeyboard && lastStatus != TLChatBarStatusKeyboard) {
return;
}
if (curStatus == TLChatBarStatusEmoji || curStatus == TLChatBarStatusMore) {
return;
}
[self.chatBar mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(self.view);
}];
[self.view layoutIfNeeded];
}
#pragma mark - Delegate
//MARK: TLChatBarDelegate
// 发送文本消息
- (void)chatBar:(TLChatBar *)chatBar sendText:(NSString *)text
{
TLTextMessage *message = [[TLTextMessage alloc] init];
message.text = text;
[self sendMessage:message];
}
//MARK: - 录音相关
- (void)chatBarStartRecording:(TLChatBar *)chatBar
{
// 先停止播放
if ([TLAudioPlayer sharedAudioPlayer].isPlaying) {
[[TLAudioPlayer sharedAudioPlayer] stopPlayingAudio];
}
[self.recorderIndicatorView setStatus:TLRecorderStatusRecording];
[self.messageDisplayView addSubview:self.recorderIndicatorView];
[self.recorderIndicatorView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.view);
make.size.mas_equalTo(CGSizeMake(150, 150));
}];
__block NSInteger time_count = 0;
TLVoiceMessage *message = [[TLVoiceMessage alloc] init];
message.ownerTyper = TLMessageOwnerTypeSelf;
message.userID = [TLUserHelper sharedHelper].userID;
message.fromUser = (id<TLChatUserProtocol>)[TLUserHelper sharedHelper].user;
message.msgStatus = TLVoiceMessageStatusRecording;
message.date = [NSDate date];
[[TLAudioRecorder sharedRecorder] startRecordingWithVolumeChangedBlock:^(CGFloat volume) {
time_count ++;
if (time_count == 2) {
[self addToShowMessage:message];
}
[self.recorderIndicatorView setVolume:volume];
} completeBlock:^(NSString *filePath, CGFloat time) {
if (time < 1.0) {
[self.recorderIndicatorView setStatus:TLRecorderStatusTooShort];
return;
}
[self.recorderIndicatorView removeFromSuperview];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSString *fileName = [NSString stringWithFormat:@"%.0lf.caf", [NSDate date].timeIntervalSince1970 * 1000];
NSString *path = [NSFileManager pathUserChatVoice:fileName];
NSError *error;
[[NSFileManager defaultManager] moveItemAtPath:filePath toPath:path error:&error];
if (error) {
DDLogError(@"录音文件出错: %@", error);
return;
}
message.recFileName = fileName;
message.time = time;
message.msgStatus = TLVoiceMessageStatusNormal;
[message resetMessageFrame];
[self sendMessage:message];
}
} cancelBlock:^{
[self.messageDisplayView deleteMessage:message];
[self.recorderIndicatorView removeFromSuperview];
}];
}
- (void)chatBarWillCancelRecording:(TLChatBar *)chatBar cancel:(BOOL)cancel
{
[self.recorderIndicatorView setStatus:cancel ? TLRecorderStatusWillCancel : TLRecorderStatusRecording];
}
- (void)chatBarFinishedRecoding:(TLChatBar *)chatBar
{
[[TLAudioRecorder sharedRecorder] stopRecording];
}
- (void)chatBarDidCancelRecording:(TLChatBar *)chatBar
{
[[TLAudioRecorder sharedRecorder] cancelRecording];
}
//MARK: - chatBar状态切换
- (void)chatBar:(TLChatBar *)chatBar changeStatusFrom:(TLChatBarStatus)fromStatus to:(TLChatBarStatus)toStatus
{
if (curStatus == toStatus) {
return;
}
lastStatus = fromStatus;
curStatus = toStatus;
if (toStatus == TLChatBarStatusInit) {
if (fromStatus == TLChatBarStatusMore) {
[self.moreKeyboard dismissWithAnimation:YES];
}
else if (fromStatus == TLChatBarStatusEmoji) {
[self.emojiKeyboard dismissWithAnimation:YES];
}
}
else if (toStatus == TLChatBarStatusVoice) {
if (fromStatus == TLChatBarStatusMore) {
[self.moreKeyboard dismissWithAnimation:YES];
}
else if (fromStatus == TLChatBarStatusEmoji) {
[self.emojiKeyboard dismissWithAnimation:YES];
}
}
else if (toStatus == TLChatBarStatusEmoji) {
[self.emojiKeyboard showInView:self.view withAnimation:YES];
}
else if (toStatus == TLChatBarStatusMore) {
[self.moreKeyboard showInView:self.view withAnimation:YES];
}
}
- (void)chatBar:(TLChatBar *)chatBar didChangeTextViewHeight:(CGFloat)height
{
[self.messageDisplayView scrollToBottomWithAnimation:NO];
}
//MARK: TLKeyboardDelegate
- (void)chatKeyboardWillShow:(id)keyboard animated:(BOOL)animated
{
[self.messageDisplayView scrollToBottomWithAnimation:YES];
}
- (void)chatKeyboardDidShow:(id)keyboard animated:(BOOL)animated
{
if (curStatus == TLChatBarStatusMore && lastStatus == TLChatBarStatusEmoji) {
[self.emojiKeyboard dismissWithAnimation:NO];
}
else if (curStatus == TLChatBarStatusEmoji && lastStatus == TLChatBarStatusMore) {
[self.moreKeyboard dismissWithAnimation:NO];
}
[self.messageDisplayView scrollToBottomWithAnimation:YES];
}
- (void)chatKeyboard:(id)keyboard didChangeHeight:(CGFloat)height
{
[self.chatBar mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(self.view).mas_offset(-height);
}];
[self.view layoutIfNeeded];
[self.messageDisplayView scrollToBottomWithAnimation:YES];
}
//MARK: TLEmojiKeyboardDelegate
- (void)emojiKeyboard:(TLEmojiKeyboard *)emojiKB didSelectedEmojiItem:(TLExpressionModel *)emoji
{
if (emoji.type == TLEmojiTypeEmoji || emoji.type == TLEmojiTypeFace) {
[self.chatBar addEmojiString:emoji.name];
}
else {
TLExpressionMessage *message = [[TLExpressionMessage alloc] init];
message.emoji = emoji;
[self sendMessage:message];
}
}
- (void)emojiKeyboardSendButtonDown
{
[self.chatBar sendCurrentText];
}
- (void)emojiKeyboardDeleteButtonDown
{
[self.chatBar deleteLastCharacter];
}
- (void)emojiKeyboard:(TLEmojiKeyboard *)emojiKB didTouchEmojiItem:(TLExpressionModel *)emoji atRect:(CGRect)rect
{
if (emoji.type == TLEmojiTypeEmoji || emoji.type == TLEmojiTypeFace) {
if (self.emojiDisplayView.superview == nil) {
[self.emojiKeyboard addSubview:self.emojiDisplayView];
}
[self.emojiDisplayView displayEmoji:emoji atRect:rect];
}
else {
if (self.imageExpressionDisplayView.superview == nil) {
[self.emojiKeyboard addSubview:self.imageExpressionDisplayView];
}
[self.imageExpressionDisplayView displayEmoji:emoji atRect:rect];
}
}
- (void)emojiKeyboardCancelTouchEmojiItem:(TLEmojiKeyboard *)emojiKB
{
if (self.emojiDisplayView.superview != nil) {
[self.emojiDisplayView removeFromSuperview];
}
else if (self.imageExpressionDisplayView.superview != nil) {
[self.imageExpressionDisplayView removeFromSuperview];
}
}
- (void)emojiKeyboard:(TLEmojiKeyboard *)emojiKB selectedEmojiGroupType:(TLEmojiType)type
{
if (type == TLEmojiTypeEmoji || type == TLEmojiTypeFace) {
[self.chatBar setActivity:YES];
}
else {
[self.chatBar setActivity:NO];
}
}
- (BOOL)chatInputViewHasText
{
return self.chatBar.curText.length == 0 ? NO : YES;
}
#pragma mark - # Getter
- (TLEmojiKeyboard *)emojiKeyboard
{
return [TLEmojiKeyboard keyboard];
}
- (TLMoreKeyboard *)moreKeyboard
{
return [TLMoreKeyboard keyboard];
}
@end
@@ -0,0 +1,24 @@
//
// TLChatBaseViewController+MessageDisplayView.h
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatBaseViewController.h"
#import "TLAudioPlayer.h"
#define MAX_SHOWTIME_MSG_COUNT 10
#define MAX_SHOWTIME_MSG_SECOND 30
@interface TLChatBaseViewController (MessageDisplayView) <TLChatMessageDisplayViewDelegate>
/**
* 添加展示消息(添加到chatVC)
*/
- (void)addToShowMessage:(TLMessage *)message;
- (void)resetChatTVC;
@end
@@ -0,0 +1,146 @@
//
// TLChatBaseViewController+MessageDisplayView.m
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatBaseViewController+MessageDisplayView.h"
#import "TLChatBaseViewController+ChatBar.h"
#import "TLTextDisplayView.h"
@implementation TLChatBaseViewController (MessageDisplayView)
#pragma mark - # Public Methods
- (void)addToShowMessage:(TLMessage *)message
{
message.showTime = [self p_needShowTime:message.date];
[self.messageDisplayView addMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
[self.messageDisplayView scrollToBottomWithAnimation:YES];
});
}
- (void)addVoiceRecordingMessage:(TLMessage *)message
{
message.date = [NSDate date];
}
- (void)resetChatTVC
{
[self.messageDisplayView resetMessageView];
lastDateInterval = 0;
msgAccumulate = 0;
}
#pragma mark - # Delegate
//MARK: TLChatMessageDisplayViewDelegate
// chatView 点击事件
- (void)chatMessageDisplayViewDidTouched:(TLChatMessageDisplayView *)chatTVC
{
[self dismissKeyboard];
}
// chatView 获取历史记录
- (void)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC getRecordsFromDate:(NSDate *)date count:(NSUInteger)count completed:(void (^)(NSDate *, NSArray *, BOOL))completed
{
TLWeakSelf(self);
[[TLMessageManager sharedInstance] messageRecordForPartner:[self.partner chat_userID] fromDate:date count:count complete:^(NSArray *array, BOOL hasMore) {
if (array.count > 0) {
int count = 0;
NSTimeInterval tm = 0;
for (TLMessage *message in array) {
if (++count > MAX_SHOWTIME_MSG_COUNT || tm == 0 || message.date.timeIntervalSince1970 - tm > MAX_SHOWTIME_MSG_SECOND) {
tm = message.date.timeIntervalSince1970;
count = 0;
message.showTime = YES;
}
if (message.ownerTyper == TLMessageOwnerTypeSelf) {
message.fromUser = weakself.user;
}
else {
if ([weakself.partner chat_userType] == TLChatUserTypeUser) {
message.fromUser = weakself.partner;
}
else if ([weakself.partner chat_userType] == TLChatUserTypeGroup){
if ([weakself.partner respondsToSelector:@selector(groupMemberByID:)]) {
message.fromUser = [weakself.partner groupMemberByID:message.friendID];
}
}
}
}
}
completed(date, array, hasMore);
}];
}
- (BOOL)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC deleteMessage:(TLMessage *)message
{
return [[TLMessageManager sharedInstance] deleteMessageByMsgID:message.messageID];
}
- (void)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC didClickUserAvatar:(TLUser *)user
{
if ([self respondsToSelector:@selector(didClickedUserAvatar:)]) {
[self didClickedUserAvatar:user];
}
}
- (void)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC didDoubleClickMessage:(TLMessage *)message
{
if (message.messageType == TLMessageTypeText) {
TLTextDisplayView *displayView = [[TLTextDisplayView alloc] init];
[displayView showInView:self.navigationController.view withAttrText:[(TLTextMessage *)message attrText] animation:YES];
}
}
- (void)chatMessageDisplayView:(TLChatMessageDisplayView *)chatTVC didClickMessage:(TLMessage *)message
{
if (message.messageType == TLMessageTypeImage && [self respondsToSelector:@selector(didClickedImageMessages:atIndex:)]) {
// 展示聊天图片
[[TLMessageManager sharedInstance] chatImagesAndVideosForPartnerID:[self.partner chat_userID] completed:^(NSArray *imagesData) {
NSInteger index = -1;
for (int i = 0; i < imagesData.count; i ++) {
if ([message.messageID isEqualToString:[imagesData[i] messageID]]) {
index = i;
break;
}
}
if (index >= 0) {
[self didClickedImageMessages:imagesData atIndex:index];
}
}];
}
else if (message.messageType == TLMessageTypeVoice) {
if ([(TLVoiceMessage *)message msgStatus] == TLVoiceMessageStatusNormal) {
// 播放语音消息
[(TLVoiceMessage *)message setMsgStatus:TLVoiceMessageStatusPlaying];
[[TLAudioPlayer sharedAudioPlayer] playAudioAtPath:[(TLVoiceMessage *)message path] complete:^(BOOL finished) {
[(TLVoiceMessage *)message setMsgStatus:TLVoiceMessageStatusNormal];
[self.messageDisplayView updateMessage:message];
}];
}
else {
// 停止播放语音消息
[[TLAudioPlayer sharedAudioPlayer] stopPlayingAudio];
}
}
}
#pragma mark - # Private Methods
static NSTimeInterval lastDateInterval = 0;
static NSInteger msgAccumulate = 0;
- (BOOL)p_needShowTime:(NSDate *)date
{
if (++msgAccumulate > MAX_SHOWTIME_MSG_COUNT || lastDateInterval == 0 || date.timeIntervalSince1970 - lastDateInterval > MAX_SHOWTIME_MSG_SECOND) {
lastDateInterval = date.timeIntervalSince1970;
msgAccumulate = 0;
return YES;
}
return NO;
}
@end
@@ -0,0 +1,18 @@
//
// TLChatBaseViewController+Proxy.h
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatBaseViewController.h"
@interface TLChatBaseViewController (Proxy) <TLMessageManagerChatVCDelegate>
/**
* 发送消息
*/
- (void)sendMessage:(TLMessage *)message;
@end
@@ -0,0 +1,54 @@
//
// TLChatBaseViewController+Proxy.m
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatBaseViewController+Proxy.h"
#import "TLChatBaseViewController+MessageDisplayView.h"
#import "TLUserHelper.h"
@implementation TLChatBaseViewController (Proxy)
- (void)sendMessage:(TLMessage *)message
{
message.ownerTyper = TLMessageOwnerTypeSelf;
message.userID = [TLUserHelper sharedHelper].userID;
message.fromUser = (id<TLChatUserProtocol>)[TLUserHelper sharedHelper].user;
message.date = [NSDate date];
if ([self.partner chat_userType] == TLChatUserTypeUser) {
message.partnerType = TLPartnerTypeUser;
message.friendID = [self.partner chat_userID];
}
else if ([self.partner chat_userType] == TLChatUserTypeGroup) {
message.partnerType = TLPartnerTypeGroup;
message.groupID = [self.partner chat_userID];
}
if (message.messageType != TLMessageTypeVoice) {
[self addToShowMessage:message]; // 添加到列表
}
else {
[self.messageDisplayView updateMessage:message];
}
[[TLMessageManager sharedInstance] sendMessage:message progress:^(TLMessage * message, CGFloat pregress) {
} success:^(TLMessage * message) {
NSLog(@"send success");
} failure:^(TLMessage * message) {
NSLog(@"send failure");
}];
}
- (void)didReceivedMessage:(TLMessage *)message;
{
if ([message.userID isEqualToString:self.user.chat_userID]) {
[self addToShowMessage:message]; // 添加到列表
}
}
@end
@@ -0,0 +1,72 @@
//
// TLChatBaseViewController.h
// TLChat
//
// Created by 李伯坤 on 16/2/15.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLChatViewControllerProxy.h"
#import "TLChatMessageDisplayView.h"
#import "TLEmojiDisplayView.h"
#import "TLImageExpressionDisplayView.h"
#import "TLRecorderIndicatorView.h"
#import "TLMoreKeyboardDelegate.h"
#import "TLMessageManager+MessageRecord.h"
#import "TLChatBar.h"
#import "TLMoreKeyboardDelegate.h"
#import "TLChatUserProtocol.h"
#import "TLUser.h"
@interface TLChatBaseViewController : UIViewController <TLChatViewControllerProxy, TLMoreKeyboardDelegate>
{
TLChatBarStatus lastStatus;
TLChatBarStatus curStatus;
}
/// 用户信息
@property (nonatomic, strong) id<TLChatUserProtocol> user;
/// 聊天对象
@property (nonatomic, strong) id<TLChatUserProtocol> partner;
/// 消息展示页面
@property (nonatomic, strong) TLChatMessageDisplayView *messageDisplayView;
/// 聊天输入栏
@property (nonatomic, strong) TLChatBar *chatBar;
/// emoji展示view
@property (nonatomic, strong) TLEmojiDisplayView *emojiDisplayView;
/// 图片表情展示view
@property (nonatomic, strong) TLImageExpressionDisplayView *imageExpressionDisplayView;
/// 录音展示view
@property (nonatomic, strong) TLRecorderIndicatorView *recorderIndicatorView;;
/**
* 设置“更多”键盘元素
*/
- (void)setChatMoreKeyboardData:(NSMutableArray *)moreKeyboardData;
/**
* 设置“表情”键盘元素
*/
- (void)setChatEmojiKeyboardData:(NSMutableArray *)emojiKeyboardData;
/**
* 重置chatVC
*/
- (void)resetChatVC;
/**
* 发送图片信息
*/
- (void)sendImageMessage:(UIImage *)image;
@end
@@ -0,0 +1,189 @@
//
// TLChatBaseViewController.m
// TLChat
//
// Created by 李伯坤 on 16/2/15.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatBaseViewController.h"
#import "TLChatBaseViewController+Proxy.h"
#import "TLChatBaseViewController+ChatBar.h"
#import "TLChatBaseViewController+MessageDisplayView.h"
#import "UIImage+Size.h"
#import "NSFileManager+TLChat.h"
@implementation TLChatBaseViewController
- (void)loadView
{
[super loadView];
[[TLMessageManager sharedInstance] setMessageDelegate:self];
[self.view addSubview:self.messageDisplayView];
[self.view addSubview:self.chatBar];
[self p_addMasonry];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadKeyboard];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[TLAudioPlayer sharedAudioPlayer] stopPlayingAudio];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)dealloc
{
[[TLMoreKeyboard keyboard] dismissWithAnimation:NO];
[[TLEmojiKeyboard keyboard] dismissWithAnimation:NO];
[[NSNotificationCenter defaultCenter] removeObserver:self];
#ifdef DEBUG_MEMERY
NSLog(@"dealloc ChatBaseVC");
#endif
}
#pragma mark - # Public Methods
- (void)setPartner:(id<TLChatUserProtocol>)partner
{
if (_partner && [[_partner chat_userID] isEqualToString:[partner chat_userID]]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.messageDisplayView scrollToBottomWithAnimation:NO];
});
return;
}
_partner = partner;
[self.navigationItem setTitle:[_partner chat_username]];
[self resetChatVC];
}
- (void)setChatMoreKeyboardData:(NSMutableArray *)moreKeyboardData
{
[self.moreKeyboard setChatMoreKeyboardData:moreKeyboardData];
}
- (void)setChatEmojiKeyboardData:(NSMutableArray *)emojiKeyboardData
{
[self.emojiKeyboard setEmojiGroupData:emojiKeyboardData];
}
- (void)resetChatVC
{
NSString *chatViewBGImage;
if (self.partner) {
chatViewBGImage = [[NSUserDefaults standardUserDefaults] objectForKey:[@"CHAT_BG_" stringByAppendingString:[self.partner chat_userID]]];
}
if (chatViewBGImage == nil) {
chatViewBGImage = [[NSUserDefaults standardUserDefaults] objectForKey:@"CHAT_BG_ALL"];
if (chatViewBGImage == nil) {
[self.view setBackgroundColor:[UIColor colorGrayCharcoalBG]];
}
else {
NSString *imagePath = [NSFileManager pathUserChatBackgroundImage:chatViewBGImage];
UIImage *image = [UIImage imageNamed:imagePath];
[self.view setBackgroundColor:[UIColor colorWithPatternImage:image]];
}
}
else {
NSString *imagePath = [NSFileManager pathUserChatBackgroundImage:chatViewBGImage];
UIImage *image = [UIImage imageNamed:imagePath];
[self.view setBackgroundColor:[UIColor colorWithPatternImage:image]];
}
[self resetChatTVC];
}
/**
* 发送图片消息
*/
- (void)sendImageMessage:(UIImage *)image
{
NSData *imageData = (UIImagePNGRepresentation(image) ? UIImagePNGRepresentation(image) :UIImageJPEGRepresentation(image, 0.5));
NSString *imageName = [NSString stringWithFormat:@"%lf.jpg", [NSDate date].timeIntervalSince1970];
NSString *imagePath = [NSFileManager pathUserChatImage:imageName];
[[NSFileManager defaultManager] createFileAtPath:imagePath contents:imageData attributes:nil];
TLImageMessage *message = [[TLImageMessage alloc] init];
message.fromUser = self.user;
message.ownerTyper = TLMessageOwnerTypeSelf;
message.imagePath = imageName;
message.imageSize = image.size;
[self sendMessage:message];
}
#pragma mark - # Private Methods
- (void)p_addMasonry
{
[self.messageDisplayView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.left.and.right.mas_equalTo(self.view);
make.bottom.mas_equalTo(self.chatBar.mas_top);
}];
[self.chatBar mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.and.bottom.mas_equalTo(self.view);
make.height.mas_greaterThanOrEqualTo(TABBAR_HEIGHT);
}];
[self.view layoutIfNeeded];
}
#pragma mark - # Getter
- (TLChatMessageDisplayView *)messageDisplayView
{
if (_messageDisplayView == nil) {
_messageDisplayView = [[TLChatMessageDisplayView alloc] init];
[_messageDisplayView setDelegate:self];
}
return _messageDisplayView;
}
- (TLChatBar *)chatBar
{
if (_chatBar == nil) {
_chatBar = [[TLChatBar alloc] init];
[_chatBar setDelegate:self];
}
return _chatBar;
}
- (TLEmojiDisplayView *)emojiDisplayView
{
if (_emojiDisplayView == nil) {
_emojiDisplayView = [[TLEmojiDisplayView alloc] init];
}
return _emojiDisplayView;
}
- (TLImageExpressionDisplayView *)imageExpressionDisplayView
{
if (_imageExpressionDisplayView == nil) {
_imageExpressionDisplayView = [[TLImageExpressionDisplayView alloc] init];
}
return _imageExpressionDisplayView;
}
- (TLRecorderIndicatorView *)recorderIndicatorView
{
if (_recorderIndicatorView == nil) {
_recorderIndicatorView = [[TLRecorderIndicatorView alloc] init];
}
return _recorderIndicatorView;
}
@end
@@ -0,0 +1,20 @@
//
// TLChatViewControllerProxy.h
// TLChat
//
// Created by 李伯坤 on 16/5/1.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
@class TLUser;
@class TLImageMessage;
@protocol TLChatViewControllerProxy <NSObject>
@optional;
- (void)didClickedUserAvatar:(TLUser *)user;
- (void)didClickedImageMessages:(NSArray *)imageMessages atIndex:(NSInteger)index;
@end
@@ -0,0 +1,30 @@
//
// TLChatCellMenuView.h
// TLChat
//
// Created by 李伯坤 on 16/3/16.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLMessageConstants.h"
typedef NS_ENUM(NSInteger, TLChatMenuItemType) {
TLChatMenuItemTypeCancel,
TLChatMenuItemTypeCopy,
TLChatMenuItemTypeDelete,
};
@interface TLChatCellMenuView : UIView
@property (nonatomic, assign, readonly) BOOL isShow;
@property (nonatomic, assign) TLMessageType messageType;
@property (nonatomic, copy) void (^actionBlcok)();
- (void)showInView:(UIView *)view withMessageType:(TLMessageType)messageType rect:(CGRect)rect actionBlock:(void (^)(TLChatMenuItemType))actionBlock;
- (void)dismiss;
@end
@@ -0,0 +1,99 @@
//
// TLChatCellMenuView.m
// TLChat
//
// Created by 李伯坤 on 16/3/16.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatCellMenuView.h"
@interface TLChatCellMenuView ()
@property (nonatomic, strong) UIMenuController *menuController;
@end
@implementation TLChatCellMenuView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setBackgroundColor:[UIColor clearColor]];
self.menuController = [UIMenuController sharedMenuController];
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismiss)];
[self addGestureRecognizer:tapGR];
}
return self;
}
- (void)showInView:(UIView *)view withMessageType:(TLMessageType)messageType rect:(CGRect)rect actionBlock:(void (^)(TLChatMenuItemType))actionBlock
{
_isShow = YES;
[self setFrame:view.bounds];
[view addSubview:self];
[self setActionBlcok:actionBlock];
[self setMessageType:messageType];
[self.menuController setTargetRect:rect inView:self];
[self becomeFirstResponder];
[self.menuController setMenuVisible:YES animated:YES];
}
- (void)setMessageType:(TLMessageType)messageType
{
UIMenuItem *copy = [[UIMenuItem alloc] initWithTitle:@"复制" action:@selector(copyButtonDown:)];
UIMenuItem *transmit = [[UIMenuItem alloc] initWithTitle:@"转发" action:@selector(transmitButtonDown:)];
UIMenuItem *collect = [[UIMenuItem alloc] initWithTitle:@"收藏" action:@selector(collectButtonDown:)];
UIMenuItem *del = [[UIMenuItem alloc] initWithTitle:@"删除" action:@selector(deleteButtonDown:)];
[self.menuController setMenuItems:@[copy, transmit, collect, del]];
}
- (void)dismiss
{
_isShow = NO;
if (self.actionBlcok) {
self.actionBlcok(TLChatMenuItemTypeCancel);
}
[self.menuController setMenuVisible:NO animated:YES];
[self removeFromSuperview];
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
#pragma mark - Event Response -
- (void)copyButtonDown:(UIMenuController *)sender
{
[self p_clickedMenuItemType:TLChatMenuItemTypeCopy];
}
- (void)transmitButtonDown:(UIMenuController *)sender
{
[self p_clickedMenuItemType:TLChatMenuItemTypeCopy];
}
- (void)collectButtonDown:(UIMenuController *)sender
{
[self p_clickedMenuItemType:TLChatMenuItemTypeCopy];
}
- (void)deleteButtonDown:(UIMenuController *)sender
{
[self p_clickedMenuItemType:TLChatMenuItemTypeDelete];
}
#pragma mark - Private Methods -
- (void)p_clickedMenuItemType:(TLChatMenuItemType)type
{
_isShow = NO;
[self removeFromSuperview];
if (self.actionBlcok) {
self.actionBlcok(type);
}
}
@end
@@ -0,0 +1,20 @@
//
// TLEmojiDisplayView.h
// TLChat
//
// Created by 李伯坤 on 16/3/16.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLExpressionModel.h"
@interface TLEmojiDisplayView : UIImageView
@property (nonatomic, strong) TLExpressionModel *emoji;
@property (nonatomic, assign) CGRect rect;
- (void)displayEmoji:(TLExpressionModel *)emoji atRect:(CGRect)rect;
@end
@@ -0,0 +1,117 @@
//
// TLEmojiDisplayView.m
// TLChat
//
// Created by 李伯坤 on 16/3/16.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiDisplayView.h"
#define SIZE_TIPS CGSizeMake(55, 100)
@interface TLEmojiDisplayView ()
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UILabel *imageLabel;
@property (nonatomic, strong) UILabel *titleLabel;
@end
@implementation TLEmojiDisplayView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:CGRectMake(0, 0, SIZE_TIPS.width, SIZE_TIPS.height)]) {
[self setImage:[UIImage imageNamed:@"emojiKB_tips"]];
[self addSubview:self.imageLabel];
[self addSubview:self.imageView];
[self addSubview:self.titleLabel];
[self p_addMasonry];
}
return self;
}
- (void)displayEmoji:(TLExpressionModel *)emoji atRect:(CGRect)rect
{
[self setRect:rect];
[self setEmoji:emoji];
}
- (void)setEmoji:(TLExpressionModel *)emoji
{
_emoji = emoji;
if (emoji.type == TLEmojiTypeEmoji) {
[self.imageLabel setHidden:NO];
[self.imageView setHidden:YES];
[self.titleLabel setHidden:YES];
[self.imageLabel setText:emoji.name];
}
else if (emoji.type == TLEmojiTypeFace) {
[self.imageLabel setHidden:YES];
[self.imageView setHidden:NO];
[self.titleLabel setHidden:NO];
[self.imageView setImage:[UIImage imageNamed:emoji.name]];
[self.titleLabel setText:[emoji.name substringWithRange:NSMakeRange(1, emoji.name.length - 2)]];
}
}
- (void)setRect:(CGRect)rect
{
[self setCenterX:rect.origin.x + rect.size.width / 2];
[self setY:rect.origin.y + rect.size.height - self.height - 5];
}
#pragma mark - Private Methods -
- (void)p_addMasonry
{
[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self).mas_offset(10);
make.left.mas_equalTo(self).mas_offset(12);
make.right.mas_equalTo(self).mas_equalTo(-12);
make.height.mas_equalTo(self.imageView.mas_width);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.imageView.mas_bottom).mas_offset(5);
make.centerX.mas_equalTo(self);
make.width.mas_lessThanOrEqualTo(self);
}];
[self.imageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.and.height.and.centerX.mas_equalTo(self.imageView);
make.top.mas_equalTo(self).mas_offset(12);
}];
}
#pragma mark - Getter -
- (UIImageView *)imageView
{
if (_imageView == nil) {
_imageView = [[UIImageView alloc] init];
}
return _imageView;
}
- (UILabel *)titleLabel
{
if (_titleLabel == nil) {
_titleLabel = [[UILabel alloc] init];
[_titleLabel setFont:[UIFont systemFontOfSize:12.0f]];
[_titleLabel setTextColor:[UIColor grayColor]];
}
return _titleLabel;
}
- (UILabel *)imageLabel
{
if (_imageLabel == nil) {
_imageLabel = [[UILabel alloc] init];
[_imageLabel setTextAlignment:NSTextAlignmentCenter];
[_imageLabel setHidden:YES];
[_imageLabel setFont:[UIFont systemFontOfSize:30.0f]];
}
return _imageLabel;
}
@end
@@ -0,0 +1,20 @@
//
// TLImageExpressionDisplayView.h
// TLChat
//
// Created by 李伯坤 on 16/3/16.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLExpressionModel.h"
@interface TLImageExpressionDisplayView : UIView
@property (nonatomic, strong) TLExpressionModel *emoji;
@property (nonatomic, assign) CGRect rect;
- (void)displayEmoji:(TLExpressionModel *)emoji atRect:(CGRect)rect;
@end
@@ -0,0 +1,161 @@
//
// TLImageExpressionDisplayView.m
// TLChat
//
// Created by 李伯坤 on 16/3/16.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLImageExpressionDisplayView.h"
#import <SDWebImage/UIImage+GIF.h>
#define WIDTH_TIPS 150
#define HEIGHT_TIPS 162
#define WIDTH_CENTER 25
#define SPACE_IMAGE 16
@interface TLImageExpressionDisplayView ()
@property (nonatomic, strong) UIImageView *bgLeftView;
@property (nonatomic, strong) UIImageView *bgCenterView;
@property (nonatomic, strong) UIImageView *bgRightView;
@property (nonatomic, strong) UIImageView *imageView;
@end
@implementation TLImageExpressionDisplayView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:CGRectMake(0, 0, WIDTH_TIPS, HEIGHT_TIPS)]) {
[self addSubview:self.bgLeftView];
[self addSubview:self.bgCenterView];
[self addSubview:self.bgRightView];
[self addSubview:self.imageView];
[self p_addMasonry];
}
return self;
}
- (void)displayEmoji:(TLExpressionModel *)emoji atRect:(CGRect)rect
{
[self setRect:rect];
[self setEmoji:emoji];
}
static NSString *curID;
- (void)setEmoji:(TLExpressionModel *)emoji
{
if (_emoji == emoji) {
return;
}
_emoji = emoji;
curID = emoji.eId;
NSData *data = [NSData dataWithContentsOfFile:emoji.path];
if (data) {
[self.imageView setImage:[UIImage sd_animatedGIFWithData:data]];
}
else {
NSString *urlString = [TLExpressionModel expressionDownloadURLWithEid:emoji.eId];
[self.imageView tt_setImageWithURL:TLURL(emoji.url) completed:^(UIImage *image, NSError *error, TLImageCacheType cacheType, NSURL *imageURL) {
if ([urlString containsString:curID]) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:TLURL(urlString)];
if ([urlString containsString:curID]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.imageView setImage:[UIImage sd_animatedGIFWithData:data]];
});
}
});
}
}];
}
}
- (void)setRect:(CGRect)rect
{
self.y = rect.origin.y - self.height + 3;
CGFloat w = WIDTH_TIPS - WIDTH_CENTER;
CGFloat centerX = rect.origin.x + rect.size.width / 2;
if (rect.origin.x + rect.size.width < self.width) { // 箭头在左边
self.centerX = centerX + (WIDTH_TIPS - w / 4 - WIDTH_CENTER) / 2 - SPACE_IMAGE;
[self.bgLeftView mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(w / 4);
}];
}
else if (SCREEN_WIDTH - rect.origin.x < self.width) { // 箭头在右边
self.centerX = centerX - (WIDTH_TIPS - w / 4 - WIDTH_CENTER) / 2 + SPACE_IMAGE;
[self.bgLeftView mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(w / 4 * 3);
}];
}
else {
self.centerX = centerX;
[self.bgLeftView mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(w / 2);
}];
}
}
#pragma mark - Private Methods -
- (void)p_addMasonry
{
[self.bgLeftView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.left.and.bottom.mas_equalTo(self);
}];
[self.bgCenterView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.bgLeftView.mas_right);
make.top.and.bottom.mas_equalTo(self.bgLeftView);
make.width.mas_equalTo(WIDTH_CENTER);
}];
[self.bgRightView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.bgCenterView.mas_right);
make.top.and.bottom.mas_equalTo(self.bgLeftView);
make.right.mas_equalTo(self);
}];
[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self).mas_offset(10);
make.left.mas_equalTo(self).mas_offset(10);
make.right.mas_equalTo(self).mas_offset(-10);
make.height.mas_equalTo(self.imageView.mas_width);
}];
}
#pragma mark - Getter -
- (UIImageView *)imageView
{
if (_imageView == nil) {
_imageView = [[UIImageView alloc] init];
}
return _imageView;
}
- (UIImageView *)bgLeftView
{
if (_bgLeftView == nil) {
_bgLeftView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"emojiKB_bigTips_left"]];
}
return _bgLeftView;
}
- (UIImageView *)bgCenterView
{
if (_bgCenterView == nil) {
_bgCenterView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"emojiKB_bigTips_middle"]];
}
return _bgCenterView;
}
- (UIImageView *)bgRightView
{
if (_bgRightView == nil) {
_bgRightView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"emojiKB_bigTips_right"]];
}
return _bgRightView;
}
@end
@@ -0,0 +1,26 @@
//
// TLRecorderIndicatorView.h
// TLChat
//
// Created by 李伯坤 on 16/7/12.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, TLRecorderStatus) {
TLRecorderStatusRecording,
TLRecorderStatusWillCancel,
TLRecorderStatusTooShort,
};
@interface TLRecorderIndicatorView : UIView
@property (nonatomic, assign) TLRecorderStatus status;
/**
* 音量大小,取值(0-1
*/
@property (nonatomic, assign) CGFloat volume;
@end
@@ -0,0 +1,177 @@
//
// TLRecorderIndicatorView.m
// TLChat
//
// Created by 李伯坤 on 16/7/12.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLRecorderIndicatorView.h"
#define STR_RECORDING @"手指上滑,取消发送"
#define STR_CANCEL @"手指松开,取消发送"
#define STR_REC_SHORT @"说话时间太短"
@interface TLRecorderIndicatorView ()
@property (nonatomic, strong) UIView *backgroundView;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIView *titleBackgroundView;
@property (nonatomic, strong) UIImageView *recImageView;
@property (nonatomic, strong) UIImageView *centerImageView;
@property (nonatomic, strong) UIImageView *volumeImageView;
@end
@implementation TLRecorderIndicatorView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self addSubview:self.backgroundView];
[self addSubview:self.recImageView];
[self addSubview:self.volumeImageView];
[self addSubview:self.centerImageView];
[self addSubview:self.titleBackgroundView];
[self addSubview:self.titleLabel];
[self p_addMasonry];
}
return self;
}
- (void)setStatus:(TLRecorderStatus)status
{
if (status == TLRecorderStatusWillCancel) {
[self.centerImageView setHidden:NO];
[self.centerImageView setImage:[UIImage imageNamed:@"chat_record_cancel"]];
[self.titleBackgroundView setHidden:NO];
[self.recImageView setHidden:YES];
[self.volumeImageView setHidden:YES];
[self.titleLabel setText:STR_CANCEL];
}
else if (status == TLRecorderStatusRecording) {
[self.centerImageView setHidden:YES];
[self.titleBackgroundView setHidden:YES];
[self.recImageView setHidden:NO];
[self.volumeImageView setHidden:NO];
[self.titleLabel setText:STR_RECORDING];
}
else if (status == TLRecorderStatusTooShort) {
[self.centerImageView setHidden:NO];
[self.centerImageView setImage:[UIImage imageNamed:@"chat_record_cancel"]];
[self.titleBackgroundView setHidden:YES];
[self.recImageView setHidden:YES];
[self.volumeImageView setHidden:YES];
[self.titleLabel setText:STR_REC_SHORT];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (status == TLRecorderStatusTooShort) {
[self removeFromSuperview];
}
});
}
}
- (void)setVolume:(CGFloat)volume
{
_volume = volume;
NSInteger picId = 10 * (volume < 0 ? 0 : (volume > 1.0 ? 1.0 : volume));
picId = picId > 8 ? 8 : picId;
[self.volumeImageView setImage:[UIImage imageNamed:[NSString stringWithFormat:@"chat_record_signal_%ld", (long)picId]]];
}
#pragma mark - # Private Methods
- (void)p_addMasonry
{
[self.backgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(0);
}];
[self.recImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(15);
make.left.mas_equalTo(21);
}];
[self.volumeImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.height.mas_equalTo(self.recImageView);
make.right.mas_equalTo(-21);
}];
[self.centerImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(0);
make.top.mas_equalTo(15);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(0);
make.bottom.mas_equalTo(-15);
}];
[self.titleBackgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.titleLabel).mas_offset(UIEdgeInsetsMake(-2, -5, -2, -5)).priorityLow();
}];
}
#pragma mark - # Getter
- (UIView *)backgroundView
{
if (_backgroundView == nil) {
_backgroundView = [[UIView alloc] init];
[_backgroundView setBackgroundColor:[UIColor blackColor]];
[_backgroundView setAlpha:0.6f];
[_backgroundView.layer setMasksToBounds:YES];
[_backgroundView.layer setCornerRadius:5.0f];
}
return _backgroundView;
}
- (UILabel *)titleLabel
{
if (_titleLabel == nil) {
_titleLabel = [[UILabel alloc] init];
[_titleLabel setFont:[UIFont systemFontOfSize:14.0f]];
[_titleLabel setTextAlignment:NSTextAlignmentCenter];
[_titleLabel setTextColor:[UIColor whiteColor]];
[_titleLabel setText:STR_RECORDING];
}
return _titleLabel;
}
- (UIView *)titleBackgroundView
{
if (_titleBackgroundView == nil) {
_titleBackgroundView = [[UIView alloc] init];
[_titleBackgroundView setHidden:YES];
[_titleBackgroundView setBackgroundColor:[UIColor redColor]];
[_titleBackgroundView.layer setMasksToBounds:YES];
[_titleBackgroundView.layer setCornerRadius:2.0f];
}
return _titleBackgroundView;
}
- (UIImageView *)recImageView
{
if (_recImageView == nil) {
_recImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"chat_record_recording"]];
}
return _recImageView;
}
- (UIImageView *)centerImageView
{
if (_centerImageView == nil) {
_centerImageView = [[UIImageView alloc] init];
[_centerImageView setHidden:YES];
}
return _centerImageView;
}
- (UIImageView *)volumeImageView
{
if (_volumeImageView == nil) {
_volumeImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"chat_record_signal_1"]];
}
return _volumeImageView;
}
@end
@@ -0,0 +1,17 @@
//
// TLTextDisplayView.h
// TLChat
//
// Created by 李伯坤 on 16/3/16.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TLTextDisplayView : UIView
@property (nonatomic, strong) NSAttributedString *attrString;
- (void)showInView:(UIView *)view withAttrText:(NSAttributedString *)attrText animation:(BOOL)animation;
@end
@@ -0,0 +1,84 @@
//
// TLTextDisplayView.m
// TLChat
//
// Created by 李伯坤 on 16/3/16.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLTextDisplayView.h"
#define WIDTH_TEXTVIEW self.width * 0.94
@interface TLTextDisplayView ()
@property (nonatomic, strong) UITextView *textView;
@end
@implementation TLTextDisplayView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setBackgroundColor:[UIColor whiteColor]];
[self addSubview:self.textView];
[self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self);
}];
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismiss)];
[self addGestureRecognizer:tapGR];
}
return self;
}
- (void)showInView:(UIView *)view withAttrText:(NSAttributedString *)attrText animation:(BOOL)animation
{
[view addSubview:self];
[self setFrame:view.bounds];
[self setAttrString:attrText];
[self setAlpha:0];
[UIView animateWithDuration:0.1 animations:^{
[self setAlpha:1.0];
} completion:^(BOOL finished) {
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
}];
}
- (void)setAttrString:(NSAttributedString *)attrString
{
_attrString = attrString;
NSMutableAttributedString *mutableAttrString = [[NSMutableAttributedString alloc] initWithAttributedString:attrString];
[mutableAttrString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:25.0f] range:NSMakeRange(0, attrString.length)];
[self.textView setAttributedText:mutableAttrString];
CGSize size = [self.textView sizeThatFits:CGSizeMake(WIDTH_TEXTVIEW, MAXFLOAT)];
size.height = size.height > SCREEN_HEIGHT ? SCREEN_HEIGHT : size.height;
[self.textView mas_updateConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(size);
}];
}
- (void)dismiss
{
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
[UIView animateWithDuration:0.2 animations:^{
self.alpha = 0;
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
#pragma mark - Getter -
- (UITextView *)textView
{
if (_textView == nil) {
_textView = [[UITextView alloc] init];
[_textView setBackgroundColor:[UIColor clearColor]];
[_textView setTextAlignment:NSTextAlignmentCenter];
[_textView setEditable:NO];
}
return _textView;
}
@end
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,31 @@
//
// TLEmojiBaseCell.h
// TLChat
//
// Created by 李伯坤 on 16/3/9.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLExpressionModel.h"
@protocol TLEmojiCellProtocol <NSObject>
- (CGRect)displayBaseRect;
@end
@interface TLEmojiBaseCell : UICollectionViewCell <TLEmojiCellProtocol>
@property (nonatomic, strong) TLExpressionModel *emojiItem;
@property (nonatomic, strong) UIImageView *bgView;
/**
* 选中时的背景图片,默认nil
*/
@property (nonatomic, strong) UIImage *highlightImage;
@property (nonatomic, assign) BOOL showHighlightImage;
@end
@@ -0,0 +1,50 @@
//
// TLEmojiBaseCell.m
// TLChat
//
// Created by 李伯坤 on 16/3/9.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiBaseCell.h"
@implementation TLEmojiBaseCell
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self.contentView addSubview:self.bgView];
[self.bgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.contentView);
}];
}
return self;
}
- (CGRect)displayBaseRect
{
return self.frame;
}
- (void)setShowHighlightImage:(BOOL)showHighlightImage
{
if (showHighlightImage) {
[self.bgView setImage:self.highlightImage];
}
else {
[self.bgView setImage:nil];
}
}
#pragma mark - Getter -
- (UIImageView *)bgView
{
if (_bgView == nil) {
_bgView = [[UIImageView alloc] init];
[_bgView.layer setMasksToBounds:YES];
[_bgView.layer setCornerRadius:5.0f];
}
return _bgView;
}
@end
@@ -0,0 +1,13 @@
//
// TLEmojiFaceItemCell.h
// TLChat
//
// Created by 李伯坤 on 16/3/9.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiBaseCell.h"
@interface TLEmojiFaceItemCell : TLEmojiBaseCell
@end
@@ -0,0 +1,79 @@
//
// TLEmojiFaceItemCell.m
// TLChat
//
// Created by 李伯坤 on 16/3/9.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiFaceItemCell.h"
@interface TLEmojiFaceItemCell ()
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UILabel *label;
@end
@implementation TLEmojiFaceItemCell
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self.contentView addSubview:self.imageView];
[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self);
make.size.mas_equalTo(CGSizeMake(32, 32));
}];
[self.contentView addSubview:self.label];
[self.label mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.imageView);
}];
}
return self;
}
- (void)setEmojiItem:(TLExpressionModel *)emojiItem
{
[super setEmojiItem:emojiItem];
if ([emojiItem.eId isEqualToString:@"-1"]) {
[self.imageView setHidden:NO];
[self.label setHidden:YES];
[self.imageView setImage:[UIImage imageNamed:@"emojiKB_emoji_delete"]];
}
else {
if (emojiItem.type == TLEmojiTypeFace) {
[self.imageView setHidden:NO];
[self.label setHidden:YES];
[self.imageView setImage:emojiItem.name == nil ? nil : [UIImage imageNamed:emojiItem.name]];
}
else {
[self.imageView setHidden:YES];
[self.label setHidden:NO];
[self.label setText:emojiItem.name];
}
}
}
#pragma mark - # Getter
- (UIImageView *)imageView
{
if (_imageView == nil) {
_imageView = [[UIImageView alloc] init];
}
return _imageView;
}
- (UILabel *)label
{
if (_label == nil) {
_label = [[UILabel alloc] init];
[_label setFont:[UIFont systemFontOfSize:28.0f]];
[_label setTextAlignment:NSTextAlignmentCenter];
}
return _label;
}
@end
@@ -0,0 +1,14 @@
//
// TLEmojiImageItemCell.h
// TLChat
//
// Created by 李伯坤 on 16/2/20.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiBaseCell.h"
@interface TLEmojiImageItemCell : TLEmojiBaseCell
@end
@@ -0,0 +1,61 @@
//
// TLEmojiImageItemCell.m
// TLChat
//
// Created by 李伯坤 on 16/2/20.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiImageItemCell.h"
@interface TLEmojiImageItemCell ()
@property (nonatomic, strong) UIImageView *imageView;
@end
@implementation TLEmojiImageItemCell
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self.contentView addSubview:self.imageView];
[self setHighlightImage:[UIImage imageNamed:@"emoji_hl_background"]];
[self p_addMasonry];
}
return self;
}
- (CGRect)displayBaseRect
{
CGRect rect = self.imageView.frame;
rect.origin.x += self.x;
rect.origin.y += self.y;
return rect;
}
- (void)setEmojiItem:(TLExpressionModel *)emojiItem
{
[super setEmojiItem:emojiItem];
[self.imageView setImage:emojiItem.path == nil ? nil : [UIImage imageNamed:emojiItem.path]];
}
#pragma mark - Private Methods -
- (void)p_addMasonry
{
[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.contentView);
make.size.mas_equalTo(CGSizeMake(52, 52));
}];
}
#pragma mark - Getter -
- (UIImageView *)imageView
{
if (_imageView == nil) {
_imageView = [[UIImageView alloc] init];
}
return _imageView;
}
@end
@@ -0,0 +1,14 @@
//
// TLEmojiImageTitleItemCell.h
// TLChat
//
// Created by 李伯坤 on 16/2/21.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiBaseCell.h"
@interface TLEmojiImageTitleItemCell : TLEmojiBaseCell
@end
@@ -0,0 +1,86 @@
//
// TLEmojiImageTitleItemCell.m
// TLChat
//
// Created by 李伯坤 on 16/2/21.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiImageTitleItemCell.h"
#import "UIImage+Color.h"
@interface TLEmojiImageTitleItemCell ()
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UILabel *label;
@end
@implementation TLEmojiImageTitleItemCell
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setHighlightImage:[UIImage imageNamed:@"emoji_hl_background"]];
[self.contentView addSubview:self.imageView];
[self.contentView addSubview:self.label];
[self p_addMasonry];
}
return self;
}
- (void)setEmojiItem:(TLExpressionModel *)emojiItem
{
[super setEmojiItem:emojiItem];
[self.imageView setImage:emojiItem.path == nil ? nil : [UIImage imageNamed:emojiItem.path]];
[self.label setText:emojiItem.name];
}
- (CGRect)displayBaseRect
{
CGRect rect = self.imageView.frame;
rect.origin.x += self.x;
rect.origin.y += self.y;
return rect;
}
#pragma mark - Private Methods -
- (void)p_addMasonry
{
[self.bgView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.imageView).mas_offset(UIEdgeInsetsMake(-3, -3, -3, -3));
}];
[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(3);
make.centerX.mas_equalTo(0);
make.size.mas_equalTo(CGSizeMake(50, 50));
}];
[self.label mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(0);
make.width.mas_lessThanOrEqualTo(self).mas_offset(-8);
make.top.mas_equalTo(self.imageView.mas_bottom).mas_offset(4);
}];
}
#pragma mark - Getter -
- (UIImageView *)imageView
{
if (_imageView == nil) {
_imageView = [[UIImageView alloc] init];
}
return _imageView;
}
- (UILabel *)label
{
if (_label == nil) {
_label = [[UILabel alloc] init];
[_label setFont:[UIFont systemFontOfSize:12.0f]];
[_label setTextColor:[UIColor grayColor]];
[_label setTextAlignment:NSTextAlignmentCenter];
}
return _label;
}
@end
@@ -0,0 +1,18 @@
//
// TLEmojiGroupDisplayView+CollectionView.h
// TLChat
//
// Created by 李伯坤 on 16/9/27.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiGroupDisplayView.h"
@interface TLEmojiGroupDisplayView (CollectionView) <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
- (void)registerCellClass;
- (NSUInteger)transformModelByRowCount:(NSInteger)rowCount colCount:(NSInteger)colCount andIndex:(NSInteger)index;
@end
@@ -0,0 +1,124 @@
//
// TLEmojiGroupDisplayView+CollectionView.m
// TLChat
//
// Created by 李伯坤 on 16/9/27.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiGroupDisplayView+CollectionView.h"
#import "TLEmojiFaceItemCell.h"
#import "TLEmojiImageItemCell.h"
#import "TLEmojiImageTitleItemCell.h"
@implementation TLEmojiGroupDisplayView (CollectionView)
- (void)registerCellClass
{
[self.collectionView registerClass:[TLEmojiFaceItemCell class] forCellWithReuseIdentifier:@"TLEmojiFaceItemCell"];
[self.collectionView registerClass:[TLEmojiImageItemCell class] forCellWithReuseIdentifier:@"TLEmojiImageItemCell"];
[self.collectionView registerClass:[TLEmojiImageTitleItemCell class] forCellWithReuseIdentifier:@"TLEmojiImageTitleItemCell"];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"UICollectionViewCell"];
}
- (NSUInteger)transformModelByRowCount:(NSInteger)rowCount colCount:(NSInteger)colCount andIndex:(NSInteger)index
{
NSUInteger x = index / rowCount;
NSUInteger y = index % rowCount;
return colCount * y + x;
}
#pragma mark - # Delegate
//MARK: UICollectionViewDelegate
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return self.displayData.count;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
TLEmojiGroupDisplayModel *group = self.displayData[section];
return group.pageItemCount;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
TLEmojiGroupDisplayModel *group = self.displayData[indexPath.section];
NSInteger index = [self transformModelByRowCount:group.rowNumber colCount:group.colNumber andIndex:indexPath.row];
TLExpressionModel *emoji = [group objectAtIndex:index];
TLEmojiBaseCell *cell;
if (group.type == TLEmojiTypeEmoji || group.type == TLEmojiTypeFace) {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TLEmojiFaceItemCell" forIndexPath:indexPath];
}
else if (group.type == TLEmojiTypeImageWithTitle) {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TLEmojiImageTitleItemCell" forIndexPath:indexPath];
}
else {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TLEmojiImageItemCell" forIndexPath:indexPath];
}
[cell setEmojiItem:emoji];
return cell;
}
//MARK: UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
TLEmojiGroupDisplayModel *group = self.displayData[indexPath.section];
NSInteger index = [self transformModelByRowCount:group.rowNumber colCount:group.colNumber andIndex:indexPath.row];
TLExpressionModel *emoji = [group objectAtIndex:index];
if (emoji) {
if ([emoji.eId isEqualToString:@"-1"]) {
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiGroupDisplayViewDeleteButtonPressed:)]) {
[self.delegate emojiGroupDisplayViewDeleteButtonPressed:self];
}
}
else if (self.delegate && [self.delegate respondsToSelector:@selector(emojiGroupDisplayView:didClickedEmoji:)]) {
//FIXME: 表情类型
emoji.type = group.type;
[self.delegate emojiGroupDisplayView:self didClickedEmoji:emoji];
}
}
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
TLEmojiGroupDisplayModel *group = self.displayData[indexPath.section];
return group.cellSize;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 0;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return 0;
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
TLEmojiGroupDisplayModel *group = self.displayData[section];
return group.sectionInsets;
}
//MARK: UIScrollViewDelegate
static float lastX = 0;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSInteger page = (scrollView.contentOffset.x + 2.0) / scrollView.width;
if (scrollView.contentOffset.x < lastX) { // 右滑坐标修复
page += (scrollView.contentOffset.x - scrollView.width * page > 3.0) ? 1 : 0;
}
lastX = scrollView.contentOffset.x;
if (self.curPageIndex != page) {
self.curPageIndex = page;
if (page >= 0 && page < self.displayData.count && self.delegate && [self.delegate respondsToSelector:@selector(emojiGroupDisplayView:didScrollToPageIndex:forGroupIndex:)]) {
TLEmojiGroupDisplayModel *group = self.displayData[page];
[self.delegate emojiGroupDisplayView:self didScrollToPageIndex:group.pageIndex forGroupIndex:group.emojiGroupIndex];
}
}
}
@end
@@ -0,0 +1,15 @@
//
// TLEmojiGroupDisplayView+Gesture.h
// TLChat
//
// Created by 李伯坤 on 16/9/28.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiGroupDisplayView.h"
@interface TLEmojiGroupDisplayView (Gesture)
- (void)addGusture;
@end
@@ -0,0 +1,115 @@
//
// TLEmojiGroupDisplayView+Gesture.m
// TLChat
//
// Created by 李伯坤 on 16/9/28.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiGroupDisplayView+Gesture.h"
#import "TLEmojiGroupDisplayView+CollectionView.h"
#import "TLEmojiBaseCell.h"
@implementation TLEmojiGroupDisplayView (Gesture)
#pragma mark - # Public Methods
- (void)addGusture
{
UILongPressGestureRecognizer *longPressGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressAction:)];
[self.collectionView addGestureRecognizer:longPressGR];
}
#pragma mark - # Event Response
static UICollectionViewCell *lastCell;
- (void)longPressAction:(UILongPressGestureRecognizer *)sender
{
TLEmojiGroupDisplayModel *curGroup = [self.displayData objectAtIndex:self.curPageIndex];
CGPoint point = [sender locationInView:self.collectionView];
if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateCancelled) { // 长按停止
[self p_cancelLongPressEmoji];
lastCell = nil;
}
else if (sender.state == UIGestureRecognizerStateBegan) {
NSArray *visableCells = self.collectionView.visibleCells;
for (TLEmojiBaseCell *cell in visableCells) {
if (cell.x <= point.x && cell.y <= point.y && cell.x + curGroup.cellSize.width >= point.x && cell.y + curGroup.cellSize.height >= point.y) {
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
NSInteger index = [self transformModelByRowCount:curGroup.rowNumber colCount:curGroup.colNumber andIndex:indexPath.row]; // 矩阵坐标转置
TLExpressionModel *emoji = [curGroup objectAtIndex:index];
if (emoji) {
if ((emoji.type == TLEmojiTypeEmoji || emoji.type == TLEmojiTypeFace) && [emoji.eId isEqualToString:@"-1"]) { // 删除
}
else {
CGRect rect = [cell displayBaseRect];
rect.origin.x = rect.origin.x - self.width * (int)(rect.origin.x / self.width);
[self p_startLongPressEmoji:emoji atRect:rect];
}
}
return;
}
}
lastCell = nil;
}
else {
NSArray *visableCells = self.collectionView.visibleCells;
for (TLEmojiBaseCell *cell in visableCells) {
if (cell.x <= point.x && cell.y <= point.y && cell.x + curGroup.cellSize.width >= point.x && cell.y + curGroup.cellSize.height >= point.y) {
if (cell == lastCell) {
return;
}
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
NSInteger index = [self transformModelByRowCount:curGroup.rowNumber colCount:curGroup.colNumber andIndex:indexPath.row]; // 矩阵坐标转置
TLExpressionModel *emoji = [curGroup objectAtIndex:index];
if (emoji) {
if ((emoji.type == TLEmojiTypeEmoji || emoji.type == TLEmojiTypeFace) && [emoji.eId isEqualToString:@"-1"]) { // 删除
[self p_cancelLongPressEmoji];
}
else {
CGRect rect = [cell displayBaseRect];
rect.origin.x = rect.origin.x - self.width * (int)(rect.origin.x / self.width);
[self p_startLongPressEmoji:emoji atRect:rect];
}
}
else { // 空白cell
[self p_cancelLongPressEmoji];
}
lastCell = cell;
return;
}
}
// 超出界限
if (lastCell) {
[self p_cancelLongPressEmoji];
lastCell = nil;
}
}
}
#pragma mark - # Private Methods
static TLExpressionModel *lastEmoji;
- (void)p_startLongPressEmoji:(TLExpressionModel *)emoji atRect:(CGRect)rect
{
if (emoji != lastEmoji) {
lastEmoji = emoji;
TLEmojiGroupDisplayModel *curGroup = [self.displayData objectAtIndex:self.curPageIndex];
emoji.type = curGroup.type;
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiGroupDisplayView:didLongPressEmoji:atRect:)]) {
[self.delegate emojiGroupDisplayView:self didLongPressEmoji:emoji atRect:rect];
}
}
}
- (void)p_cancelLongPressEmoji
{
if (lastEmoji) {
lastEmoji = nil;
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiGroupDisplayViewDidStopLongPressEmoji:)]) { // 停止长按表情
[self.delegate emojiGroupDisplayViewDidStopLongPressEmoji:self];
}
}
}
@end
@@ -0,0 +1,27 @@
//
// TLEmojiGroupDisplayView.h
// TLChat
//
// Created by 李伯坤 on 16/9/27.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLEmojiGroupDisplayViewDelegate.h"
#import "TLEmojiGroupDisplayModel.h"
@interface TLEmojiGroupDisplayView : UIView
@property (nonatomic, weak) id<TLEmojiGroupDisplayViewDelegate> delegate;
@property (nonatomic, weak) NSMutableArray *data;
@property (nonatomic, strong) NSMutableArray *displayData;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, assign) NSInteger curPageIndex;
- (void)scrollToEmojiGroupAtIndex:(NSInteger)index;
@end
@@ -0,0 +1,125 @@
//
// TLEmojiGroupDisplayView.m
// TLChat
//
// Created by 李伯坤 on 16/9/27.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiGroupDisplayView.h"
#import "TLEmojiGroupDisplayView+CollectionView.h"
#import "TLEmojiGroupDisplayView+Gesture.h"
#import "TLExpressionGroupModel+TLEmojiKB.h"
@implementation TLEmojiGroupDisplayView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self addSubview:self.collectionView];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(0);
}];
[self registerCellClass];
[self addGusture];
}
return self;
}
- (void)setData:(NSMutableArray *)data
{
if (data && _data && [_data isEqualToArray:data]) {
return;
}
_data = data;
self.height = HEIGHT_CHAT_KEYBOARD - 57;
self.width = SCREEN_WIDTH;
NSMutableArray *displayData = [[NSMutableArray alloc] init];
for (NSInteger emojiGroupIndex = 0; emojiGroupIndex < data.count; emojiGroupIndex++) {
TLExpressionGroupModel *group = data[emojiGroupIndex];
if (group.count > 0) { // 已下载的表情包
NSInteger cellWidth, cellHeight;
CGFloat spaceX, spaceYTop, spaceYBottom;
cellWidth = ((self.width - 20) / group.colNumber);
spaceX = (self.width - cellWidth * group.colNumber) / 2.0;
if (group.type == TLEmojiTypeEmoji || group.type == TLEmojiTypeFace) {
cellHeight = (self.height - 15) / group.rowNumber;
spaceYTop = 10;
}
else if (group.type == TLEmojiTypeImageWithTitle) {
cellHeight = (self.height - 10) / group.rowNumber;
spaceYTop = 10;
}
else {
cellHeight = (self.height - 40) / group.rowNumber;
spaceYTop = 20;
}
spaceYBottom = (self.height - cellHeight * group.rowNumber) - spaceYTop;
for (NSInteger pageIndex = 0; pageIndex < group.pageNumber; pageIndex++) {
TLEmojiGroupDisplayModel *model = [[TLEmojiGroupDisplayModel alloc] initWithEmojiGroup:group pageNumber:pageIndex andCount:group.pageItemCount];
if (model.type == TLEmojiTypeEmoji || group.type == TLEmojiTypeFace) { // 为默认表情包添加删除按钮
TLExpressionModel *emoji = [[TLExpressionModel alloc] init];
emoji.eId = @"-1";
emoji.name = @"del";
[model addEmoji:emoji];
model.pageItemCount ++;
}
model.emojiGroupIndex = emojiGroupIndex;
model.pageIndex = pageIndex;
model.cellSize = CGSizeMake(cellWidth, cellHeight);
model.sectionInsets = UIEdgeInsetsMake(spaceYTop, spaceX, spaceYBottom, spaceX);
[displayData addObject:model];
}
}
}
self.displayData = displayData;
[self.collectionView reloadData];
if (self.displayData.count > 0 && self.delegate && [self.delegate respondsToSelector:@selector(emojiGroupDisplayView:didScrollToPageIndex:forGroupIndex:)]) {
TLEmojiGroupDisplayModel *group = self.displayData[0];
[self.collectionView setContentOffset:CGPointZero];
[self.delegate emojiGroupDisplayView:self didScrollToPageIndex:0 forGroupIndex:group.emojiGroupIndex];
}
}
- (void)scrollToEmojiGroupAtIndex:(NSInteger)index
{
if (index > self.data.count) {
return;
}
_curPageIndex = index;
NSInteger page = 0;
for (int i = 0; i < index; i ++) {
TLExpressionGroupModel *group = self.data[i];
page += group.pageNumber;
}
[self.collectionView setContentOffset:CGPointMake(page * self.collectionView.width, 0)];
if (self.displayData.count > page) {
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiGroupDisplayView:didScrollToPageIndex:forGroupIndex:)]) {
TLEmojiGroupDisplayModel *group = self.displayData[page];
[self.delegate emojiGroupDisplayView:self didScrollToPageIndex:0 forGroupIndex:group.emojiGroupIndex];
}
}
}
#pragma mark - # Getter
- (UICollectionView *)collectionView
{
if (_collectionView == nil) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
[layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
[_collectionView setBackgroundColor:[UIColor clearColor]];
[_collectionView setPagingEnabled:YES];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView setShowsHorizontalScrollIndicator:NO];
[_collectionView setShowsHorizontalScrollIndicator:NO];
[_collectionView setScrollsToTop:NO];
}
return _collectionView;
}
@end
@@ -0,0 +1,40 @@
//
// TLEmojiGroupDisplayViewDelegate.h
// TLChat
//
// Created by 李伯坤 on 16/9/27.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
@class TLExpressionModel;
@class TLEmojiGroupDisplayView;
@protocol TLEmojiGroupDisplayViewDelegate <NSObject>
/**
* 发送按钮点击事件
*/
- (void)emojiGroupDisplayViewDeleteButtonPressed:(TLEmojiGroupDisplayView *)displayView;
/**
* 选中表情
*/
- (void)emojiGroupDisplayView:(TLEmojiGroupDisplayView *)displayView didClickedEmoji:(TLExpressionModel *)emoji;
/**
* 翻页
*/
- (void)emojiGroupDisplayView:(TLEmojiGroupDisplayView *)displayView didScrollToPageIndex:(NSInteger)pageIndex forGroupIndex:(NSInteger)groupIndex;
/**
* 表情长按
*/
- (void)emojiGroupDisplayView:(TLEmojiGroupDisplayView *)displayView didLongPressEmoji:(TLExpressionModel *)emoji atRect:(CGRect)rect;
/**
* 停止表情长按
*/
- (void)emojiGroupDisplayViewDidStopLongPressEmoji:(TLEmojiGroupDisplayView *)displayView;
@end
@@ -0,0 +1,16 @@
//
// TLEmojiGroupCell.h
// TLChat
//
// Created by 李伯坤 on 16/2/19.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLExpressionGroupModel.h"
@interface TLEmojiGroupCell : UICollectionViewCell
@property (nonatomic, strong) TLExpressionGroupModel *emojiGroup;
@end
@@ -0,0 +1,69 @@
//
// TLEmojiGroupCell.m
// TLChat
//
// Created by 李伯坤 on 16/2/19.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiGroupCell.h"
@interface TLEmojiGroupCell ()
@property (nonatomic, strong) UIImageView *groupIconView;
@end
@implementation TLEmojiGroupCell
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setBackgroundColor:[UIColor clearColor]];
UIView *selectedView = [[UIView alloc] init];
[selectedView setBackgroundColor:[UIColor colorGrayForChatBar]];
[self setSelectedBackgroundView:selectedView];
[self.contentView addSubview:self.groupIconView];
[self p_addMasonry];
}
return self;
}
- (void)setEmojiGroup:(TLExpressionGroupModel *)emojiGroup
{
_emojiGroup = emojiGroup;
[self.groupIconView setImage:[UIImage imageNamed:emojiGroup.iconPath]];
}
#pragma mark - # Private Methods
- (void)p_addMasonry
{
[self.groupIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.contentView);
make.width.and.height.mas_lessThanOrEqualTo(30);
}];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
CGContextSetStrokeColorWithColor(context, [UIColor colorGrayLine].CGColor);
CGContextBeginPath(context);
CGContextMoveToPoint(context, self.width - 0.5, 5);
CGContextAddLineToPoint(context, self.width - 0.5, self.height - 5);
CGContextStrokePath(context);
}
#pragma mark - Getter -
- (UIImageView *)groupIconView
{
if (_groupIconView == nil) {
_groupIconView = [[UIImageView alloc] init];
}
return _groupIconView;
}
@end
@@ -0,0 +1,41 @@
//
// TLEmojiGroupControl.h
// TLChat
//
// Created by 李伯坤 on 16/2/19.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLExpressionGroupModel.h"
typedef NS_ENUM(NSInteger, TLGroupControlSendButtonStatus) {
TLGroupControlSendButtonStatusGray,
TLGroupControlSendButtonStatusBlue,
TLGroupControlSendButtonStatusNone,
};
@class TLEmojiGroupControl;
@protocol TLEmojiGroupControlDelegate <NSObject>
- (void)emojiGroupControl:(TLEmojiGroupControl*)emojiGroupControl didSelectedGroup:(TLExpressionGroupModel *)group;
- (void)emojiGroupControlEditButtonDown:(TLEmojiGroupControl *)emojiGroupControl;
- (void)emojiGroupControlEditMyEmojiButtonDown:(TLEmojiGroupControl *)emojiGroupControl;
- (void)emojiGroupControlSendButtonDown:(TLEmojiGroupControl *)emojiGroupControl;
@end
@interface TLEmojiGroupControl : UIView
@property (nonatomic, assign) TLGroupControlSendButtonStatus sendButtonStatus;
@property (nonatomic, strong) NSMutableArray *emojiGroupData;
@property (nonatomic, assign) id<TLEmojiGroupControlDelegate>delegate;
- (void)selectEmojiGroupAtIndex:(NSInteger)index;
@end
@@ -0,0 +1,278 @@
//
// TLEmojiGroupControl.m
// TLChat
//
// Created by 李伯坤 on 16/2/19.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiGroupControl.h"
#import "TLEmojiGroupCell.h"
#define WIDTH_EMOJIGROUP_CELL 46
#define WIDTH_SENDBUTTON 60
@interface TLEmojiGroupControl () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) NSIndexPath *curIndexPath;
@property (nonatomic, strong) UIButton *addButton;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) UIButton *sendButton;
@end
@implementation TLEmojiGroupControl
- (id)init
{
if (self = [super init]) {
[self setBackgroundColor:[UIColor whiteColor]];
[self addSubview:self.addButton];
[self addSubview:self.collectionView];
[self addSubview:self.sendButton];
[self p_addMasonry];
[self.collectionView registerClass:[TLEmojiGroupCell class] forCellWithReuseIdentifier:@"TLEmojiGroupCell"];
}
return self;
}
- (void)setSendButtonStatus:(TLGroupControlSendButtonStatus)sendButtonStatus
{
if (_sendButtonStatus != sendButtonStatus) {
if (_sendButtonStatus == TLGroupControlSendButtonStatusNone) {
[self.sendButton mas_updateConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self);
}];
[UIView animateWithDuration:0.3 animations:^{
[self layoutIfNeeded];
} completion:^(BOOL finished) {
[self.collectionView reloadData];
[self.collectionView selectItemAtIndexPath:self.curIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
}];
if (sendButtonStatus == TLGroupControlSendButtonStatusBlue) {
[_sendButton setBackgroundImage:[UIImage imageNamed:@"emojiKB_sendBtn_blue"] forState:UIControlStateNormal];
[_sendButton setBackgroundImage:[UIImage imageNamed:@"emojiKB_sendBtn_blueHL"] forState:UIControlStateHighlighted];
[_sendButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
else if (sendButtonStatus == TLGroupControlSendButtonStatusGray) {
[_sendButton setBackgroundImage:[UIImage imageNamed:@"emojiKB_sendBtn_gray"] forState:UIControlStateNormal];
[_sendButton setBackgroundImage:[UIImage imageNamed:@"emojiKB_sendBtn_gray"] forState:UIControlStateHighlighted];
[_sendButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
}
}
else if (sendButtonStatus == TLGroupControlSendButtonStatusNone) {
[self.sendButton mas_updateConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self).mas_offset(WIDTH_SENDBUTTON);
}];
[UIView animateWithDuration:0.3 animations:^{
[self layoutIfNeeded];
} completion:^(BOOL finished) {
[self.collectionView reloadData];
[self.collectionView selectItemAtIndexPath:self.curIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
}];
}
_sendButtonStatus = sendButtonStatus;
}
}
- (void)setEmojiGroupData:(NSMutableArray *)emojiGroupData
{
if (_emojiGroupData == emojiGroupData || [_emojiGroupData isEqualToArray:emojiGroupData]) {
return;
}
_emojiGroupData = emojiGroupData;
[self.collectionView reloadData];
if (emojiGroupData && emojiGroupData.count > 0) {
[self setCurIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
}
}
- (void)selectEmojiGroupAtIndex:(NSInteger)index
{
if (index < self.emojiGroupData.count) {
_curIndexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.collectionView selectItemAtIndexPath:_curIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
CGFloat width = WIDTH_EMOJIGROUP_CELL;
CGFloat x = width * index;
if (x < self.collectionView.contentOffset.x) {
[self.collectionView setContentOffset:CGPointMake(x, 0) animated:YES];
}
else if (x + width > self.collectionView.contentOffset.x + self.collectionView.width) {
[self.collectionView setContentOffset:CGPointMake(x + width - self.collectionView.width, 0) animated:YES];
}
}
}
- (void)setCurIndexPath:(NSIndexPath *)curIndexPath
{
if (curIndexPath.row < self.emojiGroupData.count) {
[self.collectionView scrollToItemAtIndexPath:curIndexPath atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
[self.collectionView selectItemAtIndexPath:curIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
if (_curIndexPath && _curIndexPath.section == curIndexPath.section && _curIndexPath.row == curIndexPath.row) {
return;
}
CGFloat width = WIDTH_EMOJIGROUP_CELL;
CGFloat x = width * curIndexPath.row;
if (x < self.collectionView.contentOffset.x) {
[self.collectionView setContentOffset:CGPointMake(x, 0) animated:YES];
}
else if (x + width > self.collectionView.contentOffset.x + self.collectionView.width) {
[self.collectionView setContentOffset:CGPointMake(x + width - self.collectionView.width, 0) animated:YES];
}
_curIndexPath = curIndexPath;
if (_delegate && [_delegate respondsToSelector:@selector(emojiGroupControl:didSelectedGroup:)]) {
TLExpressionGroupModel *group = [self.emojiGroupData objectAtIndex:curIndexPath.row];
[_delegate emojiGroupControl:self didSelectedGroup:group];
}
}
}
#pragma mark - # Delegate
//MARK: UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.emojiGroupData.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
TLEmojiGroupCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TLEmojiGroupCell" forIndexPath:indexPath];
TLExpressionGroupModel *group = [self.emojiGroupData objectAtIndex:indexPath.row];
[cell setEmojiGroup:group];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(WIDTH_EMOJIGROUP_CELL, self.height);
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
{
if (section == self.emojiGroupData.count - 1) {
return CGSizeMake(WIDTH_EMOJIGROUP_CELL * 2, self.height);
}
return CGSizeZero;
}
//MARK: UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
TLExpressionGroupModel *group = [self.emojiGroupData objectAtIndex:indexPath.row];
if (group.type == TLEmojiTypeOther) {
//???: 存在冲突:用户选中cellA,再此方法中立马调用方法选中cellB时,所有cell都不会被选中
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self setCurIndexPath:_curIndexPath];
});
[self p_eidtMyEmojiButtonDown];
}
else {
[self setCurIndexPath:indexPath];
}
}
#pragma mark - # Event Response
- (void)emojiAddButtonDown
{
if (_delegate && [_delegate respondsToSelector:@selector(emojiGroupControlEditButtonDown:)]) {
[_delegate emojiGroupControlEditButtonDown:self];
}
}
- (void)sendButtonDown
{
if (_delegate && [_delegate respondsToSelector:@selector(emojiGroupControlSendButtonDown:)]) {
[_delegate emojiGroupControlSendButtonDown:self];
}
}
#pragma mark - # Private Methods
- (void)p_addMasonry
{
[self.addButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.top.and.bottom.mas_equalTo(self);
make.width.mas_equalTo(WIDTH_EMOJIGROUP_CELL);
}];
[self.sendButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.bottom.and.right.mas_equalTo(self);
make.width.mas_equalTo(WIDTH_SENDBUTTON);
}];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.bottom.mas_equalTo(self);
make.left.mas_equalTo(self.addButton.mas_right);
make.right.mas_equalTo(self.sendButton.mas_left);
}];
}
- (void)p_eidtMyEmojiButtonDown
{
if (_delegate && [_delegate respondsToSelector:@selector(emojiGroupControlEditMyEmojiButtonDown:)]) {
[_delegate emojiGroupControlEditMyEmojiButtonDown:self];
}
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
CGContextSetStrokeColorWithColor(context, [UIColor colorGrayLine].CGColor);
CGContextBeginPath(context);
CGContextMoveToPoint(context, WIDTH_EMOJIGROUP_CELL, 5);
CGContextAddLineToPoint(context, WIDTH_EMOJIGROUP_CELL, self.height - 5);
CGContextStrokePath(context);
}
#pragma mark - # Getter
- (UIButton *)addButton
{
if (_addButton == nil) {
_addButton = [[UIButton alloc] init];
[_addButton setImage:[UIImage imageNamed:@"emojiKB_groupControl_add"] forState:UIControlStateNormal];
[_addButton addTarget:self action:@selector(emojiAddButtonDown) forControlEvents:UIControlEventTouchUpInside];
}
return _addButton;
}
- (UICollectionView *)collectionView
{
if (_collectionView == nil) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
[layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[layout setMinimumLineSpacing:0];
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
[_collectionView setBackgroundColor:[UIColor clearColor]];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView setShowsHorizontalScrollIndicator:NO];
[_collectionView setShowsHorizontalScrollIndicator:NO];
[_collectionView setScrollsToTop:NO];
}
return _collectionView;
}
- (UIButton *)sendButton
{
if (_sendButton == nil) {
_sendButton = [[UIButton alloc] init];
[_sendButton.titleLabel setFont:[UIFont systemFontOfSize:15.0f]];
[_sendButton setTitle:@" 发送" forState:UIControlStateNormal];
[_sendButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
[_sendButton setBackgroundColor:[UIColor clearColor]];
[_sendButton setBackgroundImage:[UIImage imageNamed:@"emojiKB_sendBtn_gray"] forState:UIControlStateNormal];
[_sendButton setBackgroundImage:[UIImage imageNamed:@"emojiKB_sendBtn_gray"] forState:UIControlStateHighlighted];
[_sendButton addTarget:self action:@selector(sendButtonDown) forControlEvents:UIControlEventTouchUpInside];
}
return _sendButton;
}
@end
@@ -0,0 +1,57 @@
//
// TLEmojiGroupDisplayModel.h
// TLChat
//
// Created by 李伯坤 on 16/9/27.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TLExpressionGroupModel.h"
@interface TLEmojiGroupDisplayModel : NSObject
@property (nonatomic, assign) TLEmojiType type;
@property (nonatomic, strong) NSString *groupID;
@property (nonatomic, strong) NSString *groupName;
@property (nonatomic, strong) NSString *groupIconPath;
@property (nonatomic, assign) NSUInteger count;
@property (nonatomic, strong) NSArray *data;
#pragma mark - 标记
@property (nonatomic, assign) NSInteger emojiGroupIndex;
@property (nonatomic, assign) NSInteger pageIndex;
#pragma mark - 展示用
/// 每页个数
@property (nonatomic, assign) NSUInteger pageItemCount;
/// 页数
@property (nonatomic, assign) NSUInteger pageNumber;
/// 行数
@property (nonatomic, assign) NSUInteger rowNumber;
/// 列数
@property (nonatomic, assign) NSUInteger colNumber;
@property (nonatomic, assign) CGSize cellSize;
@property (nonatomic, assign) UIEdgeInsets sectionInsets;
- (id)initWithEmojiGroup:(TLExpressionGroupModel *)emojiGroup pageNumber:(NSInteger)pageNumber andCount:(NSInteger)count;
- (id)objectAtIndex:(NSUInteger)index;
- (void)addEmoji:(TLExpressionModel *)emoji;
@end
@@ -0,0 +1,44 @@
//
// TLEmojiGroupDisplayModel.m
// TLChat
//
// Created by 李伯坤 on 16/9/27.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiGroupDisplayModel.h"
#import "TLExpressionGroupModel+TLEmojiKB.h"
@implementation TLEmojiGroupDisplayModel
- (id)initWithEmojiGroup:(TLExpressionGroupModel *)emojiGroup pageNumber:(NSInteger)pageNumber andCount:(NSInteger)count
{
if (self = [super init]) {
self.groupID = emojiGroup.gId;
self.groupName = emojiGroup.name;
self.type = emojiGroup.type;
self.rowNumber = emojiGroup.rowNumber;
self.colNumber = emojiGroup.colNumber;
self.pageItemCount = emojiGroup.pageItemCount;
NSInteger start = pageNumber * count;
if (emojiGroup.data.count > start) {
NSInteger len = MIN(count, emojiGroup.count - start);
self.data = [emojiGroup.data subarrayWithRange:NSMakeRange(pageNumber * count, len)];
}
}
return self;
}
- (id)objectAtIndex:(NSUInteger)index
{
return index < self.data.count ? [self.data objectAtIndex:index] : nil;
}
- (void)addEmoji:(TLExpressionModel *)emoji
{
NSMutableArray *emojis = [NSMutableArray arrayWithArray:self.data];
[emojis addObject:emoji];
self.data = emojis;
}
@end
@@ -0,0 +1,59 @@
//
// TLEmojiKeyboardDelegate.h
// TLChat
//
// Created by 李伯坤 on 16/2/20.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TLExpressionModel.h"
@class TLEmojiKeyboard;
@protocol TLEmojiKeyboardDelegate <NSObject>
- (BOOL)chatInputViewHasText;
@optional
/**
* 长按某个表情(展示)
*/
- (void)emojiKeyboard:(TLEmojiKeyboard *)emojiKB didTouchEmojiItem:(TLExpressionModel *)emoji atRect:(CGRect)rect;
/**
* 取消长按某个表情(停止展示)
*/
- (void)emojiKeyboardCancelTouchEmojiItem:(TLEmojiKeyboard *)emojiKB;
/**
* 点击事件 —— 选中某个表情
*/
- (void)emojiKeyboard:(TLEmojiKeyboard *)emojiKB didSelectedEmojiItem:(TLExpressionModel *)emoji;
/**
* 点击事件 —— 表情发送按钮
*/
- (void)emojiKeyboardSendButtonDown;
/**
* 点击事件 —— 删除按钮
*/
- (void)emojiKeyboardDeleteButtonDown;
/**
* 点击事件 —— 表情编辑按钮
*/
- (void)emojiKeyboardEmojiEditButtonDown;
/**
* 点击事件 —— 我的表情按钮
*/
- (void)emojiKeyboardMyEmojiEditButtonDown;
/**
* 选中不同类型的表情组回调
* 用于改变chatBar状态(个性表情组展示时textView不可用)
*/
- (void)emojiKeyboard:(TLEmojiKeyboard *)emojiKB selectedEmojiGroupType:(TLEmojiType)type;
@end
@@ -0,0 +1,26 @@
//
// TLExpressionGroupModel+TLEmojiKB.h
// TLChat
//
// Created by 李伯坤 on 2018/1/2.
// Copyright © 2018年 李伯坤. All rights reserved.
//
#import "TLExpressionGroupModel.h"
@interface TLExpressionGroupModel (TLEmojiKB)
/// 每页个数
@property (nonatomic, assign, readonly) NSUInteger pageItemCount;
/// 页数
@property (nonatomic, assign, readonly) NSUInteger pageNumber;
/// 行数
@property (nonatomic, assign, readonly) NSUInteger rowNumber;
/// 列数
@property (nonatomic, assign, readonly) NSUInteger colNumber;
@end
@@ -0,0 +1,42 @@
//
// TLExpressionGroupModel+TLEmojiKB.m
// TLChat
//
// Created by 李伯坤 on 2018/1/2.
// Copyright © 2018年 李伯坤. All rights reserved.
//
#import "TLExpressionGroupModel+TLEmojiKB.h"
@implementation TLExpressionGroupModel (TLEmojiKB)
- (NSUInteger)rowNumber
{
if (self.type == TLEmojiTypeFace || self.type == TLEmojiTypeEmoji) {
return 3;
}
return 2;
}
- (NSUInteger)colNumber
{
if (self.type == TLEmojiTypeFace || self.type == TLEmojiTypeEmoji) {
return 7;
}
return 4;
}
- (NSUInteger)pageItemCount
{
if (self.type == TLEmojiTypeFace || self.type == TLEmojiTypeEmoji) {
return self.rowNumber * self.colNumber - 1;
}
return self.rowNumber * self.colNumber;
}
- (NSUInteger)pageNumber
{
return self.count / self.pageItemCount + (self.count % self.pageItemCount == 0 ? 0 : 1);
}
@end
@@ -0,0 +1,14 @@
//
// TLEmojiKeyboard+DisplayView.h
// TLChat
//
// Created by 李伯坤 on 16/9/27.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiKeyboard.h"
#import "TLEmojiGroupDisplayViewDelegate.h"
@interface TLEmojiKeyboard (DisplayView) <TLEmojiGroupDisplayViewDelegate>
@end
@@ -0,0 +1,62 @@
//
// TLEmojiKeyboard+DisplayView.m
// TLChat
//
// Created by 李伯坤 on 16/9/27.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiKeyboard+DisplayView.h"
#import "TLExpressionGroupModel+TLEmojiKB.h"
@implementation TLEmojiKeyboard (DisplayView)
#pragma mark - # Delegate
//MARK: TLEmojiGroupDisplayViewDelegate
- (void)emojiGroupDisplayView:(TLEmojiGroupDisplayView *)displayView didClickedEmoji:(TLExpressionModel *)emoji
{
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiKeyboard:didSelectedEmojiItem:)]) {
[self.delegate emojiKeyboard:self didSelectedEmojiItem:emoji];
}
}
- (void)emojiGroupDisplayViewDeleteButtonPressed:(TLEmojiGroupDisplayView *)displayView;
{
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiKeyboardDeleteButtonDown)]) {
[self.delegate emojiKeyboardDeleteButtonDown];
}
}
- (void)emojiGroupDisplayView:(TLEmojiGroupDisplayView *)displayView didScrollToPageIndex:(NSInteger)pageIndex forGroupIndex:(NSInteger)groupIndex
{
TLExpressionGroupModel *group = self.emojiGroupData[groupIndex];
if (self.curGroup != group) {
self.curGroup = group;
[self.pageControl setHidden:group.pageNumber <= 1];
[self.pageControl setNumberOfPages:group.pageNumber];
[self.groupControl selectEmojiGroupAtIndex:groupIndex];
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiKeyboard:selectedEmojiGroupType:)]) {
[self.delegate emojiKeyboard:self selectedEmojiGroupType:group.type];
}
}
[self.pageControl setCurrentPage:pageIndex];
}
static UICollectionViewCell *lastCell;
- (void)emojiGroupDisplayView:(TLEmojiGroupDisplayView *)displayView didLongPressEmoji:(TLExpressionModel *)emoji atRect:(CGRect)rect
{
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiKeyboard:didTouchEmojiItem:atRect:)]) {
[self.delegate emojiKeyboard:self didTouchEmojiItem:emoji atRect:rect];
}
}
- (void)emojiGroupDisplayViewDidStopLongPressEmoji:(TLEmojiGroupDisplayView *)displayView
{
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiKeyboardCancelTouchEmojiItem:)]) {
[self.delegate emojiKeyboardCancelTouchEmojiItem:self];
}
}
@end
@@ -0,0 +1,13 @@
//
// TLEmojiKeyboard+EmojiGroupControl.h
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiKeyboard.h"
@interface TLEmojiKeyboard (EmojiGroupControl) <TLEmojiGroupControlDelegate>
@end
@@ -0,0 +1,50 @@
//
// TLEmojiKeyboard+EmojiGroupControl.m
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiKeyboard+EmojiGroupControl.h"
#import "TLExpressionGroupModel+TLEmojiKB.h"
@implementation TLEmojiKeyboard (EmojiGroupControl)
#pragma mark - Delegate
//MARK: TLEmojiGroupControlDelegate
- (void)emojiGroupControl:(TLEmojiGroupControl *)emojiGroupControl didSelectedGroup:(TLExpressionGroupModel *)group
{
// 显示Group表情
self.curGroup = group;
[self.displayView scrollToEmojiGroupAtIndex:[self.emojiGroupData indexOfObject:group]];
[self.pageControl setNumberOfPages:group.pageNumber];
[self.pageControl setCurrentPage:0];
// 更新chatBar的textView状态
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiKeyboard:selectedEmojiGroupType:)]) {
[self.delegate emojiKeyboard:self selectedEmojiGroupType:group.type];
}
}
- (void)emojiGroupControlEditMyEmojiButtonDown:(TLEmojiGroupControl *)emojiGroupControl
{
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiKeyboardMyEmojiEditButtonDown)]) {
[self.delegate emojiKeyboardMyEmojiEditButtonDown];
}
}
- (void)emojiGroupControlEditButtonDown:(TLEmojiGroupControl *)emojiGroupControl
{
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiKeyboardEmojiEditButtonDown)]) {
[self.delegate emojiKeyboardEmojiEditButtonDown];
}
}
- (void)emojiGroupControlSendButtonDown:(TLEmojiGroupControl *)emojiGroupControl
{
if (self.delegate && [self.delegate respondsToSelector:@selector(emojiKeyboardSendButtonDown)]) {
[self.delegate emojiKeyboardSendButtonDown];
}
}
@end
@@ -0,0 +1,30 @@
//
// TLEmojiKeyboard.h
// TLChat
//
// Created by 李伯坤 on 16/2/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLBaseKeyboard.h"
#import "TLEmojiKeyboardDelegate.h"
#import "TLEmojiGroupControl.h"
#import "TLEmojiGroupDisplayView.h"
@interface TLEmojiKeyboard : TLBaseKeyboard
@property (nonatomic, strong) NSMutableArray *emojiGroupData;
@property (nonatomic, assign) id<TLEmojiKeyboardDelegate> delegate;
@property (nonatomic, strong) TLExpressionGroupModel *curGroup;
@property (nonatomic, strong) TLEmojiGroupDisplayView *displayView;
@property (nonatomic, strong) UIPageControl *pageControl;
@property (nonatomic, strong) TLEmojiGroupControl *groupControl;
+ (TLEmojiKeyboard *)keyboard;
@end
@@ -0,0 +1,121 @@
//
// TLEmojiKeyboard.m
// TLChat
//
// Created by 李伯坤 on 16/2/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLEmojiKeyboard.h"
#import "TLEmojiKeyboard+DisplayView.h"
#import "TLEmojiKeyboard+EmojiGroupControl.h"
#import "TLChatMacros.h"
static TLEmojiKeyboard *emojiKB;
@implementation TLEmojiKeyboard
+ (TLEmojiKeyboard *)keyboard
{
static dispatch_once_t once;
dispatch_once(&once, ^{
emojiKB = [[TLEmojiKeyboard alloc] init];
});
return emojiKB;
}
- (id)init
{
if (self = [super init]) {
[self setBackgroundColor:[UIColor colorGrayForChatBar]];
[self addSubview:self.displayView];
[self addSubview:self.pageControl];
[self addSubview:self.groupControl];
[self p_addMasonry];
}
return self;
}
- (void)setEmojiGroupData:(NSMutableArray *)emojiGroupData
{
_emojiGroupData = emojiGroupData;
[self.displayView setData:emojiGroupData];
[self.groupControl setEmojiGroupData:emojiGroupData];
}
#pragma mark - # Public Methods
- (void)reset
{
// [self.collectionView scrollRectToVisible:CGRectMake(0, 0, self.collectionView.width, self.collectionView.height) animated:NO];
}
#pragma mark - # Event Response
- (void)pageControlChanged:(UIPageControl *)pageControl
{
// [self.collectionView scrollRectToVisible:CGRectMake(SCREEN_WIDTH * pageControl.currentPage, 0, SCREEN_WIDTH, HEIGHT_PAGECONTROL) animated:YES];
}
#pragma mark - # Private Methods
- (void)p_addMasonry
{
[self.displayView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self);
make.left.and.right.mas_equalTo(self);
make.bottom.mas_equalTo(self.pageControl.mas_top);
}];
[self.pageControl mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.mas_equalTo(self);
make.bottom.mas_equalTo(self.groupControl.mas_top);
make.height.mas_equalTo(20);
}];
[self.groupControl mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.and.bottom.mas_equalTo(self);
make.height.mas_equalTo(37);
}];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
// 顶部直线
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
CGContextSetStrokeColorWithColor(context, [UIColor colorGrayLine].CGColor);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, SCREEN_WIDTH, 0);
CGContextStrokePath(context);
}
#pragma mark - # Getter
- (TLEmojiGroupDisplayView *)displayView
{
if (_displayView == nil) {
_displayView = [[TLEmojiGroupDisplayView alloc] init];
[_displayView setDelegate:self];
}
return _displayView;
}
- (UIPageControl *)pageControl
{
if (_pageControl == nil) {
_pageControl = [[UIPageControl alloc] init];
_pageControl.centerX = self.centerX;
[_pageControl setPageIndicatorTintColor:[UIColor colorGrayLine]];
[_pageControl setCurrentPageIndicatorTintColor:[UIColor grayColor]];
[_pageControl addTarget:self action:@selector(pageControlChanged:) forControlEvents:UIControlEventValueChanged];
}
return _pageControl;
}
- (TLEmojiGroupControl *)groupControl
{
if (_groupControl == nil) {
_groupControl = [[TLEmojiGroupControl alloc] init];
[_groupControl setDelegate:self];
}
return _groupControl;
}
@end
@@ -0,0 +1,18 @@
//
// TLMoreKeyboardCell.h
// TLChat
//
// Created by 李伯坤 on 16/2/18.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLMoreKeyboardItem.h"
@interface TLMoreKeyboardCell : UICollectionViewCell
@property (nonatomic, strong) TLMoreKeyboardItem *item;
@property (nonatomic, strong) void(^clickBlock)(TLMoreKeyboardItem *item);
@end
@@ -0,0 +1,94 @@
//
// TLMoreKeyboardCell.m
// TLChat
//
// Created by 李伯坤 on 16/2/18.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMoreKeyboardCell.h"
#import "UIImage+Color.h"
@interface TLMoreKeyboardCell()
@property (nonatomic, strong) UIButton *iconButton;
@property (nonatomic, strong) UILabel *titleLabel;
@end
@implementation TLMoreKeyboardCell
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self.contentView addSubview:self.iconButton];
[self.contentView addSubview:self.titleLabel];
[self p_addMasonry];
}
return self;
}
- (void)setItem:(TLMoreKeyboardItem *)item
{
_item = item;
if (item == nil) {
[self.titleLabel setHidden:YES];
[self.iconButton setHidden:YES];
[self setUserInteractionEnabled:NO];
return;
}
[self setUserInteractionEnabled:YES];
[self.titleLabel setHidden:NO];
[self.iconButton setHidden:NO];
[self.titleLabel setText:item.title];
[self.iconButton setImage:[UIImage imageNamed:item.imagePath] forState:UIControlStateNormal];
}
#pragma mark - Event Response -
- (void)iconButtonDown:(UIButton *)sender
{
self.clickBlock(self.item);
}
#pragma mark - Private Methods -
- (void)p_addMasonry
{
[self.iconButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.contentView);
make.centerX.mas_equalTo(self.contentView);
make.width.mas_equalTo(self.contentView);
make.height.mas_equalTo(self.iconButton.mas_width);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self.contentView);
make.bottom.mas_equalTo(self.contentView);
}];
}
#pragma mark - Getter -
- (UIButton *)iconButton
{
if (_iconButton == nil) {
_iconButton = [[UIButton alloc] init];
[_iconButton.layer setMasksToBounds:YES];
[_iconButton.layer setCornerRadius:5.0f];
[_iconButton.layer setBorderWidth:BORDER_WIDTH_1PX];
[_iconButton.layer setBorderColor:[UIColor grayColor].CGColor];
[_iconButton setBackgroundImage:[UIImage imageWithColor:[UIColor colorGrayLine]] forState:UIControlStateHighlighted];
[_iconButton addTarget:self action:@selector(iconButtonDown:) forControlEvents:UIControlEventTouchUpInside];
}
return _iconButton;
}
- (UILabel *)titleLabel
{
if (_titleLabel == nil) {
_titleLabel = [[UILabel alloc] init];
[_titleLabel setFont:[UIFont systemFontOfSize:12.0f]];
[_titleLabel setTextColor:[UIColor grayColor]];
}
return _titleLabel;
}
@end
@@ -0,0 +1,16 @@
//
// TLMoreKeyboardDelegate.h
// TLChat
//
// Created by 李伯坤 on 16/2/20.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TLMoreKeyboardItem.h"
@protocol TLMoreKeyboardDelegate <NSObject>
@optional
- (void)moreKeyboard:(id)keyboard didSelectedFunctionItem:(TLMoreKeyboardItem *)funcItem;
@end
@@ -0,0 +1,35 @@
//
// TLMoreKeyboardItem.h
// TLChat
//
// Created by 李伯坤 on 16/2/18.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, TLMoreKeyboardItemType) {
TLMoreKeyboardItemTypeImage,
TLMoreKeyboardItemTypeCamera,
TLMoreKeyboardItemTypeVideo,
TLMoreKeyboardItemTypeVideoCall,
TLMoreKeyboardItemTypeWallet,
TLMoreKeyboardItemTypeTransfer,
TLMoreKeyboardItemTypePosition,
TLMoreKeyboardItemTypeFavorite,
TLMoreKeyboardItemTypeBusinessCard,
TLMoreKeyboardItemTypeVoice,
TLMoreKeyboardItemTypeCards,
};
@interface TLMoreKeyboardItem : NSObject
@property (nonatomic, assign) TLMoreKeyboardItemType type;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *imagePath;
+ (TLMoreKeyboardItem *)createByType:(TLMoreKeyboardItemType)type title:(NSString *)title imagePath:(NSString *)imagePath;
@end
@@ -0,0 +1,23 @@
//
// TLMoreKeyboardItem.m
// TLChat
//
// Created by 李伯坤 on 16/2/18.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMoreKeyboardItem.h"
@implementation TLMoreKeyboardItem
+ (TLMoreKeyboardItem *)createByType:(TLMoreKeyboardItemType)type title:(NSString *)title imagePath:(NSString *)imagePath
{
TLMoreKeyboardItem *item = [[TLMoreKeyboardItem alloc] init];
item.type = type;
item.title = title;
item.imagePath = imagePath;
return item;
}
@end
@@ -0,0 +1,17 @@
//
// TLMoreKeyboard+CollectionView.h
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMoreKeyboard.h"
@interface TLMoreKeyboard (CollectionView) <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
@property (nonatomic, assign, readonly) NSInteger pageItemCount;
- (void)registerCellClass;
@end
@@ -0,0 +1,98 @@
//
// TLMoreKeyboard+CollectionView.m
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMoreKeyboard+CollectionView.h"
#import "TLMoreKeyboardCell.h"
#define SPACE_TOP 15
#define WIDTH_CELL 60
@implementation TLMoreKeyboard (CollectionView)
#pragma mark - Public Methods -
- (void)registerCellClass
{
[self.collectionView registerClass:[TLMoreKeyboardCell class] forCellWithReuseIdentifier:@"TLMoreKeyboardCell"];
}
#pragma mark - Delegate -
//MARK: UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return self.chatMoreKeyboardData.count / self.pageItemCount + (self.chatMoreKeyboardData.count % self.pageItemCount == 0 ? 0 : 1);
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.pageItemCount;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
TLMoreKeyboardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TLMoreKeyboardCell" forIndexPath:indexPath];
NSUInteger index = indexPath.section * self.pageItemCount + indexPath.row;
NSUInteger tIndex = [self p_transformIndex:index]; // 矩阵坐标转置
if (tIndex >= self.chatMoreKeyboardData.count) {
[cell setItem:nil];
}
else {
[cell setItem:self.chatMoreKeyboardData[tIndex]];
}
__weak typeof(self) weakSelf = self;
[cell setClickBlock:^(TLMoreKeyboardItem *sItem) {
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(moreKeyboard:didSelectedFunctionItem:)]) {
[weakSelf.delegate moreKeyboard:weakSelf didSelectedFunctionItem:sItem];
}
}];
return cell;
}
//MARK: UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(WIDTH_CELL, (collectionView.height - SPACE_TOP) / 2 * 0.93);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return (collectionView.width - WIDTH_CELL * self.pageItemCount / 2) / (self.pageItemCount / 2 + 1);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return (collectionView.height - SPACE_TOP) / 2 * 0.07;
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
CGFloat space = (collectionView.width - WIDTH_CELL * self.pageItemCount / 2) / (self.pageItemCount / 2 + 1);
return UIEdgeInsetsMake(SPACE_TOP, space, 0, space);
}
//Mark: UIScrollViewDelegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self.pageControl setCurrentPage:(int)(scrollView.contentOffset.x / scrollView.width)];
}
#pragma mark - Private Methods -
- (NSUInteger)p_transformIndex:(NSUInteger)index
{
NSUInteger page = index / self.pageItemCount;
index = index % self.pageItemCount;
NSUInteger x = index / 2;
NSUInteger y = index % 2;
return self.pageItemCount / 2 * y + x + page * self.pageItemCount;
}
#pragma mark - # Getter
- (NSInteger)pageItemCount
{
return 8;
}
@end
@@ -0,0 +1,26 @@
//
// TLMoreKeyboard.h
// TLChat
//
// Created by 李伯坤 on 16/2/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLBaseKeyboard.h"
#import "TLKeyboardDelegate.h"
#import "TLMoreKeyboardDelegate.h"
#import "TLMoreKeyboardItem.h"
@interface TLMoreKeyboard : TLBaseKeyboard
@property (nonatomic, assign) id<TLMoreKeyboardDelegate> delegate;
@property (nonatomic, strong) NSMutableArray *chatMoreKeyboardData;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) UIPageControl *pageControl;
+ (TLMoreKeyboard *)keyboard;
@end
@@ -0,0 +1,120 @@
//
// TLMoreKeyboard.m
// TLChat
//
// Created by 李伯坤 on 16/2/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMoreKeyboard.h"
#import "TLMoreKeyboard+CollectionView.h"
#import "TLChatMacros.h"
static TLMoreKeyboard *moreKB;
@implementation TLMoreKeyboard
+ (TLMoreKeyboard *)keyboard
{
static dispatch_once_t once;
dispatch_once(&once, ^{
moreKB = [[TLMoreKeyboard alloc] init];
});
return moreKB;
}
- (id)init
{
if (self = [super init]) {
[self setBackgroundColor:[UIColor colorGrayForChatBar]];
[self addSubview:self.collectionView];
[self addSubview:self.pageControl];
[self p_addMasonry];
[self registerCellClass];
}
return self;
}
- (CGFloat)keyboardHeight
{
return HEIGHT_CHAT_KEYBOARD;
}
#pragma mark - # Public Methods
- (void)setChatMoreKeyboardData:(NSMutableArray *)chatMoreKeyboardData
{
_chatMoreKeyboardData = chatMoreKeyboardData;
[self.collectionView reloadData];
NSUInteger pageNumber = chatMoreKeyboardData.count / self.pageItemCount + (chatMoreKeyboardData.count % self.pageItemCount == 0 ? 0 : 1);
[self.pageControl setNumberOfPages:pageNumber];
}
- (void)reset
{
[self.collectionView scrollRectToVisible:CGRectMake(0, 0, self.collectionView.width, self.collectionView.height) animated:NO];
}
#pragma mark - # Event Response
- (void)pageControlChanged:(UIPageControl *)pageControl
{
[self.collectionView scrollRectToVisible:CGRectMake(self.collectionView.width * pageControl.currentPage, 0, self.collectionView.width, self.collectionView.height) animated:YES];
}
#pragma mark - Private Methods -
- (void)p_addMasonry
{
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self);
make.left.and.right.mas_equalTo(self);
make.bottom.mas_equalTo(-25);
}];
[self.pageControl mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.mas_equalTo(self);
make.height.mas_equalTo(20);
make.bottom.mas_equalTo(-2);
}];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
CGContextSetStrokeColorWithColor(context, [UIColor colorGrayLine].CGColor);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, SCREEN_WIDTH, 0);
CGContextStrokePath(context);
}
#pragma mark - # Getter
- (UICollectionView *)collectionView
{
if (_collectionView == nil) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
[layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
[_collectionView setBackgroundColor:[UIColor clearColor]];
[_collectionView setPagingEnabled:YES];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView setShowsHorizontalScrollIndicator:NO];
[_collectionView setShowsHorizontalScrollIndicator:NO];
[_collectionView setScrollsToTop:NO];
}
return _collectionView;
}
- (UIPageControl *)pageControl
{
if (_pageControl == nil) {
_pageControl = [[UIPageControl alloc] init];
[_pageControl setPageIndicatorTintColor:[UIColor colorGrayLine]];
[_pageControl setCurrentPageIndicatorTintColor:[UIColor grayColor]];
[_pageControl addTarget:self action:@selector(pageControlChanged:) forControlEvents:UIControlEventValueChanged];
}
return _pageControl;
}
@end
@@ -0,0 +1,49 @@
//
// TLBaseKeyboard.h
// TLChat
//
// Created by 李伯坤 on 16/8/8.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TLChatMacros.h"
#import "TLKeyboardDelegate.h"
#import "TLKeyboardProtocol.h"
@interface TLBaseKeyboard : UIView <TLKeyboardProtocol>
/// 是否正在展示
@property (nonatomic, assign, readonly) BOOL isShow;
/// 键盘事件回调
@property (nonatomic, weak) id<TLKeyboardDelegate> keyboardDelegate;
/**
* 显示键盘(在keyWindow上)
*
* @param animation 是否显示动画
*/
- (void)showWithAnimation:(BOOL)animation;
/**
* 显示键盘
*
* @param view 父view
* @param animation 是否显示动画
*/
- (void)showInView:(UIView *)view withAnimation:(BOOL)animation;
/**
* 键盘消失
*
* @param animation 是否显示消失动画
*/
- (void)dismissWithAnimation:(BOOL)animation;
/**
* 重置键盘
*/
- (void)reset;
@end
@@ -0,0 +1,112 @@
//
// TLBaseKeyboard.m
// TLChat
//
// Created by 李伯坤 on 16/8/8.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLBaseKeyboard.h"
@implementation TLBaseKeyboard
#pragma mark - # Public Methods
- (void)showWithAnimation:(BOOL)animation
{
[self showInView:[UIApplication sharedApplication].keyWindow withAnimation:animation];
}
- (void)showInView:(UIView *)view withAnimation:(BOOL)animation
{
if (_isShow) {
return;
}
_isShow = YES;
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(chatKeyboardWillShow:animated:)]) {
[self.keyboardDelegate chatKeyboardWillShow:self animated:animation];
}
[view addSubview:self];
CGFloat keyboardHeight = [self keyboardHeight];
[self mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.mas_equalTo(view);
make.height.mas_equalTo(keyboardHeight);
make.bottom.mas_equalTo(view).mas_offset(keyboardHeight);
}];
[view layoutIfNeeded];
if (animation) {
[UIView animateWithDuration:0.3 animations:^{
[self mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(view);
}];
[view layoutIfNeeded];
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(chatKeyboard:didChangeHeight:)]) {
[self.keyboardDelegate chatKeyboard:self didChangeHeight:view.height - self.y];
}
} completion:^(BOOL finished) {
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(chatKeyboardDidShow:animated:)]) {
[self.keyboardDelegate chatKeyboardDidShow:self animated:animation];
}
}];
}
else {
[self mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(view);
}];
[view layoutIfNeeded];
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(chatKeyboardDidShow:animated:)]) {
[self.keyboardDelegate chatKeyboardDidShow:self animated:animation];
}
}
}
- (void)dismissWithAnimation:(BOOL)animation
{
if (!_isShow) {
if (!animation) {
[self removeFromSuperview];
}
return;
}
_isShow = NO;
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(chatKeyboardWillDismiss:animated:)]) {
[self.keyboardDelegate chatKeyboardWillDismiss:self animated:animation];
}
if (animation) {
CGFloat keyboardHeight = [self keyboardHeight];
[UIView animateWithDuration:0.3 animations:^{
[self mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(self.superview).mas_offset(keyboardHeight);
}];
[self.superview layoutIfNeeded];
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(chatKeyboard:didChangeHeight:)]) {
[self.keyboardDelegate chatKeyboard:self didChangeHeight:self.superview.height - self.y];
}
} completion:^(BOOL finished) {
[self removeFromSuperview];
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(chatKeyboardDidDismiss:animated:)]) {
[self.keyboardDelegate chatKeyboardDidDismiss:self animated:animation];
}
}];
}
else {
[self removeFromSuperview];
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(chatKeyboardDidDismiss:animated:)]) {
[self.keyboardDelegate chatKeyboardDidDismiss:self animated:animation];
}
}
}
- (void)reset
{
}
#pragma mark - # ZZKeyboardProtocol
- (CGFloat)keyboardHeight
{
return HEIGHT_CHAT_KEYBOARD;
}
@end
@@ -0,0 +1,25 @@
//
// TLKeyboardDelegate.h
// TLChat
//
// Created by 李伯坤 on 16/2/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol TLKeyboardDelegate <NSObject>
@optional
@optional
- (void)chatKeyboardWillShow:(id)keyboard animated:(BOOL)animated;
- (void)chatKeyboardDidShow:(id)keyboard animated:(BOOL)animated;
- (void)chatKeyboardWillDismiss:(id)keyboard animated:(BOOL)animated;
- (void)chatKeyboardDidDismiss:(id)keyboard animated:(BOOL)animated;
- (void)chatKeyboard:(id)keyboard didChangeHeight:(CGFloat)height;
@end
@@ -0,0 +1,16 @@
//
// TLKeyboardProtocol.h
// TLChat
//
// Created by 李伯坤 on 16/8/8.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol TLKeyboardProtocol <NSObject>
@required;
- (CGFloat)keyboardHeight;
@end
@@ -0,0 +1,35 @@
//
// TLChatMacros.h
// TLChat
//
// Created by 李伯坤 on 16/2/19.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#ifndef TLChatMacros_h
#define TLChatMacros_h
#import <UIKit/UIKit.h>
#define HEIGHT_CHATBAR_TEXTVIEW 36.0f
#define HEIGHT_MAX_CHATBAR_TEXTVIEW 111.5f
#define HEIGHT_CHAT_KEYBOARD 215.0f
typedef NS_ENUM(NSInteger, TLEmojiType) {
TLEmojiTypeEmoji,
TLEmojiTypeFavorite,
TLEmojiTypeFace,
TLEmojiTypeImage,
TLEmojiTypeImageWithTitle,
TLEmojiTypeOther,
};
typedef NS_ENUM(NSInteger, TLChatBarStatus) {
TLChatBarStatusInit,
TLChatBarStatusVoice,
TLChatBarStatusEmoji,
TLChatBarStatusMore,
TLChatBarStatusKeyboard,
};
#endif /* TLChatMacros_h */
@@ -0,0 +1,34 @@
//
// TLChatUserProtocol.h
// TLChat
//
// Created by 李伯坤 on 16/5/6.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, TLChatUserType) {
TLChatUserTypeUser = 0,
TLChatUserTypeGroup,
};
@protocol TLChatUserProtocol <NSObject>
@property (nonatomic, strong, readonly) NSString *chat_userID;
@property (nonatomic, strong, readonly) NSString *chat_username;
@property (nonatomic, strong, readonly) NSString *chat_avatarURL;
@property (nonatomic, strong, readonly) NSString *chat_avatarPath;
@property (nonatomic, assign, readonly) NSInteger chat_userType;
@optional;
- (id)groupMemberByID:(NSString *)userID;
- (NSArray *)groupMembers;
@end
@@ -0,0 +1,14 @@
//
// TLGroup+ChatModel.h
// TLChat
//
// Created by 李伯坤 on 16/5/6.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLGroup.h"
#import "TLChatUserProtocol.h"
@interface TLGroup (ChatModel) <TLChatUserProtocol>
@end
@@ -0,0 +1,49 @@
//
// TLGroup+ChatModel.m
// TLChat
//
// Created by 李伯坤 on 16/5/6.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLGroup+ChatModel.h"
@implementation TLGroup (ChatModel)
- (NSString *)chat_userID
{
return self.groupID;
}
- (NSString *)chat_username
{
return self.groupName;
}
- (NSString *)chat_avatarURL
{
return nil;
}
- (NSString *)chat_avatarPath
{
return self.groupAvatarPath;
}
- (NSInteger)chat_userType
{
return TLChatUserTypeGroup;
}
- (id)groupMemberByID:(NSString *)userID
{
return [self memberByUserID:userID];
}
- (NSArray *)groupMembers
{
return self.users;
}
@end
@@ -0,0 +1,14 @@
//
// TLUser+ChatModel.h
// TLChat
//
// Created by 李伯坤 on 16/5/6.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLUser.h"
#import "TLChatUserProtocol.h"
@interface TLUser (ChatModel) <TLChatUserProtocol>
@end
@@ -0,0 +1,38 @@
//
// TLUser+ChatModel.m
// TLChat
//
// Created by 李伯坤 on 16/5/6.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLUser+ChatModel.h"
@implementation TLUser (ChatModel)
- (NSString *)chat_userID
{
return self.userID;
}
- (NSString *)chat_username
{
return self.showName;
}
- (NSString *)chat_avatarURL
{
return self.avatarURL;
}
- (NSString *)chat_avatarPath
{
return self.avatarPath;
}
- (NSInteger)chat_userType
{
return TLChatUserTypeUser;
}
@end
@@ -0,0 +1,13 @@
//
// TLChatViewController+Delegate.h
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatViewController.h"
@interface TLChatViewController (Delegate)
@end
@@ -0,0 +1,101 @@
//
// TLChatViewController+Delegate.m
// TLChat
//
// Created by 李伯坤 on 16/3/17.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatViewController+Delegate.h"
#import "TLExpressionViewController.h"
#import "TLMyExpressionViewController.h"
#import "TLUserDetailViewController.h"
#import <MWPhotoBrowser/MWPhotoBrowser.h>
#import "NSFileManager+TLChat.h"
@interface TLChatViewController ()
@end
@implementation TLChatViewController (Delegate)
#pragma mark - Delegate -
//MARK: TLMoreKeyboardDelegate
- (void)moreKeyboard:(id)keyboard didSelectedFunctionItem:(TLMoreKeyboardItem *)funcItem
{
if (funcItem.type == TLMoreKeyboardItemTypeCamera || funcItem.type == TLMoreKeyboardItemTypeImage) {
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
if (funcItem.type == TLMoreKeyboardItemTypeCamera) {
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[imagePickerController setSourceType:UIImagePickerControllerSourceTypeCamera];
}
else {
[TLUIUtility showAlertWithTitle:@"错误" message:@"相机初始化失败"];
return;
}
}
else {
[imagePickerController setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
}
[self presentViewController:imagePickerController animated:YES completion:nil];
__weak typeof(self) weakSelf = self;
[imagePickerController.rac_imageSelectedSignal subscribeNext:^(id x) {
[imagePickerController dismissViewControllerAnimated:YES completion:^{
UIImage *image = [x objectForKey:UIImagePickerControllerOriginalImage];
[weakSelf sendImageMessage:image];
}];
} completed:^{
[imagePickerController dismissViewControllerAnimated:YES completion:nil];
}];
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:[NSString stringWithFormat:@"选中”%@“ 按钮", funcItem.title] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil];
[alert show];
}
}
//MARK: TLEmojiKeyboardDelegate
- (void)emojiKeyboardEmojiEditButtonDown
{
TLExpressionViewController *expressionVC = [[TLExpressionViewController alloc] init];
UINavigationController *navC = [[UINavigationController alloc] initWithRootViewController:expressionVC];
[self presentViewController:navC animated:YES completion:nil];
}
- (void)emojiKeyboardMyEmojiEditButtonDown
{
TLMyExpressionViewController *myExpressionVC = [[TLMyExpressionViewController alloc] init];
UINavigationController *navC = [[UINavigationController alloc] initWithRootViewController:myExpressionVC];
[self presentViewController:navC animated:YES completion:nil];
}
//MARK: TLChatViewControllerProxy
- (void)didClickedUserAvatar:(TLUser *)user
{
TLUserDetailViewController *detailVC = [[TLUserDetailViewController alloc] initWithUserModel:user];
PushVC(detailVC);
}
- (void)didClickedImageMessages:(NSArray *)imageMessages atIndex:(NSInteger)index
{
NSMutableArray *data = [[NSMutableArray alloc] init];
for (TLMessage *message in imageMessages) {
NSURL *url;
if ([(TLImageMessage *)message imagePath]) {
NSString *imagePath = [NSFileManager pathUserChatImage:[(TLImageMessage *)message imagePath]];
url = [NSURL fileURLWithPath:imagePath];
}
else {
url = TLURL([(TLImageMessage *)message imageURL]);
}
MWPhoto *photo = [MWPhoto photoWithURL:url];
[data addObject:photo];
}
MWPhotoBrowser *browser = [[MWPhotoBrowser alloc] initWithPhotos:data];
[browser setDisplayNavArrows:YES];
[browser setCurrentPhotoIndex:index];
UINavigationController *broserNavC = [[UINavigationController alloc] initWithRootViewController:browser];
[self presentViewController:broserNavC animated:NO completion:nil];
}
@end
@@ -0,0 +1,17 @@
//
// TLChatViewController.h
// TLChat
//
// Created by 李伯坤 on 16/2/15.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatBaseViewController.h"
@interface TLChatViewController : TLChatBaseViewController
- (instancetype)initWithUserId:(NSString *)userId;
- (instancetype)initWithGroupId:(NSString *)groupId;
@end
@@ -0,0 +1,120 @@
//
// TLChatViewController.m
// TLChat
//
// Created by 李伯坤 on 16/2/15.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLChatViewController.h"
#import "TLChatViewController+Delegate.h"
#import "TLChatDetailViewController.h"
#import "TLChatGroupDetailViewController.h"
#import "TLMoreKBHelper.h"
#import "TLEmojiKBHelper.h"
#import "TLUserHelper.h"
#import "TLChatNotificationKey.h"
#import "TLFriendHelper.h"
static TLChatViewController *chatVC;
@interface TLChatViewController()
@property (nonatomic, strong) TLMoreKBHelper *moreKBhelper;
@property (nonatomic, strong) TLEmojiKBHelper *emojiKBHelper;
@property (nonatomic, strong) UIBarButtonItem *rightBarButton;
@end
@implementation TLChatViewController
- (instancetype)initWithUserId:(NSString *)userId
{
if (self = [super init]) {
TLUser *user = [[TLFriendHelper sharedFriendHelper] getFriendInfoByUserID:userId];
self.partner = user;
}
return self;
}
- (instancetype)initWithGroupId:(NSString *)groupId
{
if (self = [super init]) {
TLGroup *group = [[TLFriendHelper sharedFriendHelper] getGroupInfoByGroupID:groupId];
self.partner = group;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationItem setRightBarButtonItem:self.rightBarButton];
self.user = (id<TLChatUserProtocol>)[TLUserHelper sharedHelper].user;
self.moreKBhelper = [[TLMoreKBHelper alloc] init];
[self setChatMoreKeyboardData:self.moreKBhelper.chatMoreKeyboardData];
self.emojiKBHelper = [TLEmojiKBHelper sharedKBHelper];
TLWeakSelf(self);
[self.emojiKBHelper emojiGroupDataByUserID:[TLUserHelper sharedHelper].userID complete:^(NSMutableArray *emojiGroups) {
[weakself setChatEmojiKeyboardData:emojiGroups];
}];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resetChatVC) name:NOTI_CHAT_VIEW_RESET object:nil];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[MobClick beginLogPageView:@"ChatVC"];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[MobClick endLogPageView:@"ChatVC"];
}
- (void)dealloc
{
#ifdef DEBUG_MEMERY
NSLog(@"dealloc ChatVC");
#endif
}
#pragma mark - # Public Methods
- (void)setPartner:(id<TLChatUserProtocol>)partner
{
[super setPartner:partner];
if ([partner chat_userType] == TLChatUserTypeUser) {
[self.rightBarButton setImage:[UIImage imageNamed:@"nav_chat_single"]];
}
else if ([partner chat_userType] == TLChatUserTypeGroup) {
[self.rightBarButton setImage:[UIImage imageNamed:@"nav_chat_multi"]];
}
}
#pragma mark - # Event Response
- (void)rightBarButtonDown:(UINavigationBar *)sender
{
if ([self.partner chat_userType] == TLChatUserTypeUser) {
TLChatDetailViewController *chatDetailVC = [[TLChatDetailViewController alloc] initWithUserModel:(TLUser *)self.partner];
PushVC(chatDetailVC);
}
else if ([self.partner chat_userType] == TLChatUserTypeGroup) {
TLChatGroupDetailViewController *chatGroupDetailVC = [[TLChatGroupDetailViewController alloc] initWithGroupModel:(TLGroup *)self.partner];
PushVC(chatGroupDetailVC);
}
}
#pragma mark - # Getter
- (UIBarButtonItem *)rightBarButton
{
if (_rightBarButton == nil) {
_rightBarButton = [[UIBarButtonItem alloc] initWithImage:nil style:UIBarButtonItemStylePlain target:self action:@selector(rightBarButtonDown:)];
}
return _rightBarButton;
}
@end