1,开始比较复杂的学习了,花了1天来琢磨和重写敲了一次代码。
2,按之前自定义CELL的方式建立数据模型和数据frame模型,遵循MVC设计模式
==============
// 拉伸图片的方法
@implementation UIImage (extension)
+ (UIImage *)resizableImage:(NSString *)name
{
UIImage *rtnImage = [UIImage imageNamed:name];
CGFloat w = rtnImage.size.width * 0.5;
CGFloat h = rtnImage.size.height * 0.5;
return [rtnImage resizableImageWithCapInsets:UIEdgeInsetsMake(h, w, h, w)];
}
@end
=============
// 得到文字长宽的方法
@implementation NSString (extension)
- (CGSize)sizeWithFont:(UIFont *)font maxSize:(CGSize)maxSize
{
NSDictionary *attrs = @{NSFontAttributeName:font};
return [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
}
@end
==================
@interface TXMessageCell()
@property (nonatomic, weak) UILabel *timeView;
@property (nonatomic, weak) UIImageView *iconView;
@property (nonatomic, weak) UIButton *textView;
@end
@implementation TXMessageCell
+ (instancetype)cellWithTableView:(UITableView *)tableView
{
NSString *ID = @"message";
TXMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if(cell == nil)
{
cell = [[TXMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// 1, time label
UILabel *timeView = [[UILabel alloc] init];
timeView.font = [UIFont systemFontOfSize:13];
timeView.textColor = [UIColor blackColor];
timeView.textAlignment = NSTextAlignmentCenter;
[self.contentView addSubview:timeView];
self.timeView = timeView;
// 2, icon ImageView
UIImageView *iconView = [[UIImageView alloc] init];
[self.contentView addSubview:iconView];
self.iconView = iconView;
// 3, text button
UIButton *textView = [[UIButton alloc] init];
textView.titleLabel.font = [UIFont systemFontOfSize:14];
textView.titleLabel.numberOfLines = 0;
textView.contentEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20); // 设置button里面的内边距,在设置长宽时应该加上这个内边距,文字才不会变形
[textView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.contentView addSubview:textView];
self.textView = textView;
// 4 clear cell backgroundcolor
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)setMessageFrame:(TXMessageFrame *)messageFrame
{
_messageFrame = messageFrame;
[self settingData];
[self settingFrame];
}
- (void)settingData
{
TXMessage *message = self.messageFrame.message;
if (message.hiddenTime == NO) {
self.timeView.text = message.time;
}
if (message.type == TXMessageTypeOther) {
self.iconView.image = [UIImage imageNamed:@"other"];
} else {
self.iconView.image = [UIImage imageNamed:@"me"];
}
[self.textView setTitle:message.text forState:UIControlStateNormal];
if (message.type == TXMessageTypeOther) {
[self.textView setBackgroundImage:[UIImage resizableImage:@"chat_recive_press_pic"] forState:UIControlStateNormal];
} else {
[self.textView setBackgroundImage:[UIImage resizableImage:@"chat_send_press_pic"] forState:UIControlStateNormal];
}
}
- (void)settingFrame
{
self.timeView.frame = self.messageFrame.timeF;
self.iconView.frame = self.messageFrame.iconF;
self.textView.frame = self.messageFrame.textF;
}
@end
=============
#import "TXMessageFrame.h"
#import "TXMessage.h"
#import "NSString+extension.h"
@implementation TXMessageFrame
- (void)setMessage:(TXMessage *)message
{
_message = message;
CGFloat padding = 10;
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
if(message.hiddenTime == NO){
// 1, time
CGFloat timeW = screenWidth;
CGFloat timeH = 40;
CGFloat timeX = 0;
CGFloat timeY = 0;
_timeF = CGRectMake(timeX, timeY, timeW, timeH);
}
// 2, icon
CGFloat iconW = 40;
CGFloat iconH = 40;
CGFloat iconX;
CGFloat iconY = CGRectGetMaxY(_timeF) + padding;
if(message.type == TXMessageTypeOther){
iconX = padding;
}else{
iconX = screenWidth - padding - iconW;
}
_iconF = CGRectMake(iconX, iconY, iconW, iconH);
// 3, text
CGSize textSize = [message.text sizeWithFont:[UIFont systemFontOfSize:14] maxSize:CGSizeMake(200, MAXFLOAT)];
CGFloat textW = textSize.width;
CGFloat textH = textSize.height;
CGFloat textX;
CGFloat textY = iconY;
if(message.type == TXMessageTypeOther){
textX = CGRectGetMaxX(_iconF) + padding;
} else {
textX = iconX - padding - textSize.width - 40;
}
_textF = CGRectMake(textX, textY, textW + 40, textH + 40); // 长宽各加上内边距的两倍
_cellHeight = MAX(CGRectGetMaxY(_iconF), CGRectGetMaxY(_textF)) + padding;
}
@end
==============
#import "TXViewController.h"
#import "TXMessageFrame.h"
#import "TXMessage.h"
#import "TXMessageCell.h"
@interface TXViewController () <UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate>
@property (nonatomic, strong) NSMutableArray *messageFrames;
@property (weak, nonatomic) IBOutlet UITableView *myTableView;
@property (weak, nonatomic) IBOutlet UITextField *myInputView;
@property (nonatomic, strong) NSDictionary *autoreplay;
@end
@implementation TXViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// tableView的颜色
self.myTableView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
self.myTableView.separatorStyle = UITableViewCellSeparatorStyleNone; // 去掉tableViewCell之间的分割线
self.myTableView.allowsSelection = NO; // 不允许选择Cell
self.myTableView.delegate = self; //代理对象
// 2 ,注册通知:把键盘改变自己的Frame的消息通过给self对象的keyBoardHandle方法去处理
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyBoardHandle:) name:UIKeyboardWillChangeFrameNotification object:nil];
// 3
UILabel *leftView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 5, 0)]; // 让输入框的光标与左边线有一定距离
self.myInputView.leftView = leftView;
self.myInputView.leftViewMode = UITextFieldViewModeAlways; //当view的高度设置为0时,设置总是显示的模式
self.myInputView.delegate = self; // 需要监听输入框的一些事件,所以把输入框的代理对象设置为控制器本身,在控制器中实现其代理方法
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self]; // 即有注册,也必有移除监听
}
// 向 frame模型中添加一条信息,并刷新滚动到最新发布的底部消息
- (void)addMessageWithText:(NSString *)text type:(TXMessageType)type
{
TXMessage *message = [[TXMessage alloc] init];
message.text = text;
message.type = type;
NSDate *now = [NSDate date];
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"HH:mm";
message.time = [fmt stringFromDate:now]; // 格式日期
TXMessageFrame *lastFrame = [self.messageFrames lastObject];
message.hiddenTime = [message.time isEqualToString:lastFrame.message.time];
TXMessageFrame *messageFrame = [[TXMessageFrame alloc] init];
messageFrame.message = message;
[self.messageFrames addObject:messageFrame];
[self.myTableView reloadData]; // 刷新整个tableView
NSIndexPath *indexpath = [NSIndexPath indexPathForRow:self.messageFrames.count - 1 inSection:0]; // 计算滚动到底部的那一行
[self.myTableView scrollToRowAtIndexPath:indexpath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; //
}
// UITextField代理方法,当点了键盘右下角的 发送按钮时,会被自动调用
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[self addMessageWithText:textField.text type:TXMessageTypeMe];
NSString *othermsg = [self getOneAutoReplay:textField.text];
[self addMessageWithText:othermsg type:TXMessageTypeOther]; // 加一条自动回复的消息
self.myInputView.text = nil;
return YES;
}
- (NSString *)getOneAutoReplay:(NSString *)text
{
NSString *word;
for (int i = 0; i < text.length; i++) {
word = [text substringWithRange:NSMakeRange(i, 1)];
if (self.autoreplay[word]) {
return self.autoreplay[word];
}
}
return text;
}
// 键盘发生FRAME改变时,通过NSNotification包装信息(userInfo),发送给监听对象的方法
/*
UIKeyboardDidChangeFrameNotification; userInfo = {
UIKeyboardAnimationCurveUserInfoKey = 7;
UIKeyboardAnimationDurationUserInfoKey = "0.25";
UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}";
UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 588}";
UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}";
UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 480}, {320, 216}}";
UIKeyboardFrameChangedByUserInteraction = 0;
UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}";
}}
*/
- (void)keyBoardHandle:(NSNotification *)note
{
self.view.window.backgroundColor = self.myTableView.backgroundColor;// 设置UIWindow的背景色与tableView一致(没怎么明白,为什么不能设置在didViewLoad中?)
CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; // 动画执行的时间
CGRect lastFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat lastY = lastFrame.origin.y - self.view.frame.size.height; // 键盘改变后的Y值 - view的高度
[UIView animateWithDuration:duration animations:^{ // 使用键盘相同的动画时间,将view往上/下垂直改变Y坐标,便产生动画
self.view.transform = CGAffineTransformMakeTranslation(0, lastY);
}];
}
// 懒加载
- (NSMutableArray *)messageFrames
{
if(_messageFrames == nil)
{
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil]];
NSMutableArray *framesArray = [NSMutableArray array];
for (NSDictionary *dict in dictArray) {
TXMessage *message = [TXMessage messageWithDict:dict];
TXMessageFrame *lastFrame = [framesArray lastObject];
message.hiddenTime = [message.time isEqualToString:lastFrame.message.time];
TXMessageFrame *messageFrame = [[TXMessageFrame alloc] init];
messageFrame.message = message;
[framesArray addObject:messageFrame];
}
_messageFrames = framesArray;
}
return _messageFrames;
}
- (NSDictionary *)autoreplay
{
if(_autoreplay == nil)
{
NSString *path = [[NSBundle mainBundle] pathForResource:@"autoreply.plist" ofType:nil];
_autoreplay = [NSDictionary dictionaryWithContentsOfFile:path];
}
return _autoreplay;
}
// tableView数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.messageFrames.count;
}
// tableView数据源方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TXMessageCell *cell = [TXMessageCell cellWithTableView:tableView];
cell.messageFrame = self.messageFrames[indexPath.row];
return cell;
}
// tableView代理方法
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [self.messageFrames[indexPath.row] cellHeight];
}
// tableView 继承 ScrollView, 也拥有“开始拖拽view”的代理方法,这里是退出键盘
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self.view endEditing:YES];
}
// 隐藏状态栏
- (BOOL)prefersStatusBarHidden
{
return YES;
}
@end
==========