一、Model
BWMessage.h
#import <Foundation/Foundation.h> typedef enum{
BWMessageMe = ,//表示自己
BWMessageOther = //表示对方
}BWMessageType; @interface BWMessage : NSObject
//消息正文
@property(nonatomic, copy) NSString *text;
//消息时间
@property(nonatomic, copy) NSString *time;
//消息类型
@property(nonatomic, assign) BWMessageType type; //记录是否隐藏时间
@property(nonatomic, assign) BOOL hideTime; - (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)messageWithDict:(NSDictionary *)dict; @end #import "BWMessage.h" @implementation BWMessage - (instancetype)initWithDict:(NSDictionary *)dict
{
if (self = [super init]) {
// _text = dict[@"text"];
// _time = dict[@"time"];
// _type =(BWMessageType)dict[@"type"];
[self setValuesForKeysWithDictionary:dict];
}
return self;
} + (instancetype)messageWithDict:(NSDictionary *)dict
{
return [[self alloc] initWithDict:dict];
} @end
BWMessageFrame.h
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h> #define textFont [UIFont systemFontOfSize:16] @class BWMessage; @interface BWMessageFrame : NSObject @property (nonatomic, strong) BWMessage *message;
@property (nonatomic, assign, readonly) CGRect timeFrame;
@property (nonatomic, assign, readonly) CGRect iconFrame;
@property (nonatomic, assign, readonly) CGRect textFrame; @property (nonatomic, assign, readonly) CGFloat rowHeight; @end #import "BWMessageFrame.h"
#import <UIKit/UIKit.h>
#import "BWMessage.h"
#import "NSString+BWTextSize.h" @implementation BWMessageFrame //重写setMessage
- (void)setMessage:(BWMessage *)message
{
_message = message; //计算每个控件的Frame //设置统一的间距
CGFloat margin = ;
//获取屏幕的宽度
CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
//计算时间label的frame
CGFloat timeX = ;
CGFloat timeY = ;
CGFloat timeW = screenW;
CGFloat timeH = ; if (!message.hideTime) {
//如果需要时间Label,那么再计算frame
_timeFrame = CGRectMake(timeX, timeY, timeW, timeH);
} //计算头像的frame
CGFloat iconW = ;
CGFloat iconH = ;
CGFloat iconY = CGRectGetMaxY(_timeFrame);
CGFloat iconX = message.type == BWMessageOther ? margin : screenW - iconW - margin;
_iconFrame = CGRectMake(iconX, iconY, iconW, iconH); //计算正文的frame
CGSize textSize = [message.text sizeOfTextWithMaxsize:CGSizeMake(, MAXFLOAT) andFont:textFont];
CGFloat textW = textSize.width+;
CGFloat textH = textSize.height+;
CGFloat textY = iconY;
CGFloat textX = message.type == BWMessageOther ? CGRectGetMaxX(_iconFrame) : screenW - margin - iconW - textW;
_textFrame = CGRectMake(textX, textY, textW, textH); //计算行高
//获取 头像的最大Y值和正文的最大Y值,然后用最大Y值+margin
CGFloat maxY = MAX(CGRectGetMaxY(_iconFrame), CGRectGetMaxY(_textFrame));
_rowHeight = maxY + margin; } @end
NSString+BWTextSize.h
// NSString+BWTextSize.h
// IOS_QQ聊天
//
// Created by ma c on 16/1/10.
// Copyright (c) 2016年 博文科技. All rights reserved.
// #import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface NSString (BWTextSize) //对象方法-计算文本尺寸
- (CGSize)sizeOfTextWithMaxsize:(CGSize)maxSize andFont:(UIFont *)font; //类方法-计算文本尺寸
+ (CGSize)sizeWithText:(NSString *)text andMaxSize:(CGSize)maxSize andFont:(UIFont *)font; @end // NSString+BWTextSize.m
// IOS_QQ聊天
//
// Created by ma c on 16/1/10.
// Copyright (c) 2016年 博文科技. All rights reserved.
// #import "NSString+BWTextSize.h" @implementation NSString (BWTextSize) - (CGSize)sizeOfTextWithMaxsize:(CGSize)maxSize andFont:(UIFont *)font
{
NSDictionary *attr = @{NSFontAttributeName : font};
return [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attr context:nil].size;
} + (CGSize)sizeWithText:(NSString *)text andMaxSize:(CGSize)maxSize andFont:(UIFont *)font
{
return [text sizeOfTextWithMaxsize:maxSize andFont:font];
} @end
三、View
// BWMessageCell.h
// IOS_QQ聊天
//
// Created by ma c on 16/1/10.
// Copyright (c) 2016年 博文科技. All rights reserved.
// #import <UIKit/UIKit.h>
@class BWMessageFrame; @interface BWMessageCell : UITableViewCell @property (nonatomic, strong) BWMessageFrame *messageFrame; + (instancetype) messageCellWithTableView:(UITableView *)tableView; @end // BWMessageCell.m
// IOS_QQ聊天
//
// Created by ma c on 16/1/10.
// Copyright (c) 2016年 博文科技. All rights reserved.
// #import "BWMessageCell.h"
#import "BWMessage.h"
#import "BWMessageFrame.h" @interface BWMessageCell () @property (nonatomic, strong) UILabel *lblTime;
@property (nonatomic, strong) UIImageView *imgViewHead;
@property (nonatomic, strong) UIButton *btnText; @end @implementation BWMessageCell //创建自定义Cell
+ (instancetype)messageCellWithTableView:(UITableView *)tableView
{
NSString *cellIdentifier = @"cellIdentifier";
BWMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[BWMessageCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
//设置单元格的背景颜色
cell.backgroundColor = [UIColor clearColor];
return cell;
} #pragma mark - 重写initWithStyle的方法
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
//创建子控件 //显示时间的label
self.lblTime = [[UILabel alloc] init];
self.lblTime.textAlignment = NSTextAlignmentCenter;
self.lblTime.font = [UIFont systemFontOfSize:];
[self.contentView addSubview:self.lblTime]; //显示头像的UIImageView
self.imgViewHead = [[UIImageView alloc] init];
[self.contentView addSubview:self.imgViewHead]; //显示正文的按钮
self.btnText = [[UIButton alloc] init];
self.btnText.titleLabel.font = textFont;
[self.btnText setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
self.btnText.titleLabel.numberOfLines = ;
//self.btnText.backgroundColor = [UIColor purpleColor];
//self.btnText.titleLabel.backgroundColor = [UIColor redColor]; //设置按钮的内边距
self.btnText.contentEdgeInsets = UIEdgeInsetsMake(, , , );
[self.contentView addSubview:self.btnText];
}
return self;
}
#pragma mark - 重写setMessageFrame的方法
- (void)setMessageFrame:(BWMessageFrame *)messageFrame
{
_messageFrame = messageFrame; //获取数据模型
BWMessage *message = messageFrame.message; //分别设置控件的数据和frame //设置时间的内容和frame
self.lblTime.text = message.time;
self.lblTime.frame = messageFrame.timeFrame;
self.lblTime.hidden = message.hideTime; //设置头像的图片和frame
//根据消息类型,判断应该使用哪张图片
NSString *iconImg = message.type == BWMessageMe ? @"me" : @"other";
self.imgViewHead.image = [UIImage imageNamed:iconImg];
self.imgViewHead.frame = messageFrame.iconFrame; //设置正文的内容和frame
[self.btnText setTitle:message.text forState:UIControlStateNormal];
self.btnText.frame = messageFrame.textFrame; //设置正文的背景图
NSString *imgNor,*imgHighlighted;
if (message.type == BWMessageMe) {
//自己发送的消息
imgNor = @"chat_send_nor";
imgHighlighted = @"chat_send_press_pic";
//设置消息字体的颜色
[self.btnText setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
else{
//对方发送的消息
imgNor = @"chat_recive_nor";
imgHighlighted = @"chat_recive_press_pic";
//设置消息的字体颜色
[self.btnText setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
//加载图片
UIImage *imageNor = [UIImage imageNamed:imgNor];
UIImage *imageHighlighted = [UIImage imageNamed:imgHighlighted]; //用平铺的方式拉伸图片
imageNor = [imageNor stretchableImageWithLeftCapWidth:imageNor.size.width/ topCapHeight:imageNor.size.height/];
imageHighlighted = [imageHighlighted stretchableImageWithLeftCapWidth:imageHighlighted.size.width/ topCapHeight:imageHighlighted.size.height/]; //设置背景图
[self.btnText setBackgroundImage:imageNor forState:UIControlStateNormal];
[self.btnText setBackgroundImage:imageHighlighted forState:UIControlStateHighlighted]; } - (void)awakeFromNib {
// Initialization code
} - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated]; // Configure the view for the selected state
} @end
三、Controller
// ViewController.m
// IOS_QQ聊天
//
// Created by ma c on 16/1/9.
// Copyright (c) 2016年 博文科技. All rights reserved.
// #import "ViewController.h"
#import "BWMessage.h"
#import "BWMessageFrame.h"
#import "BWMessageCell.h" @interface ViewController ()<UITableViewDataSource,UITableViewDelegate,UITextFieldDelegate>
//消息的Frame模型对象
@property (nonatomic, strong) NSMutableArray *arrMessageFrame; @property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UITextField *textInput; @end @implementation ViewController #pragma mark - 文本框代理方法 //return键被单击的时候触发
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
//1.获取用户输入的文本
NSString *text = textField.text; //2.发送用户的消息
[self sendMessage:text withType:BWMessageMe]; //3.发送系统的消息
[self sendMessage:@"不认识" withType:BWMessageOther]; //4.清空文本框
textField.text = nil; return YES;
}
//发送消息
- (void)sendMessage:(NSString *)msg withType:(BWMessageType)type
{
//1.创建一个数据模型和frame模型
//数据模型
BWMessage *model = [[BWMessage alloc] init];
//获取当前系统时间
NSDate *nowDate = [NSDate date];
//创建一个日期格式化器
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
//设置格式
formatter.dateFormat = @"今天:HH:mm";
//进行日期的格式化
model.time = [formatter stringFromDate:nowDate];
model.type = type;
model.text = msg; //frame模型
BWMessageFrame *frameModel = [[BWMessageFrame alloc] init];
frameModel.message = model;
//根据当前的消息时间和上一条消息的时间,来设置是否隐藏时间Label
BWMessageFrame *lastMessageFrame = [self.arrMessageFrame lastObject];
NSString *lastTime = lastMessageFrame.message.time;
if ([model.time isEqualToString:lastTime]) {
model.hideTime = YES;
} //2.把frame模型加载到集合中
[self.arrMessageFrame addObject:frameModel]; //3.刷新UITableView数据
[self.tableView reloadData]; //4.把最后一行滚动到最上面
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:self.arrMessageFrame.count - inSection:];
[self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
} #pragma mark - 滚动视图代理方法
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
//[self.textInput resignFirstResponder];
//滚动把键盘叫回去
[self.view endEditing:YES];
} #pragma mark - 数据源方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.arrMessageFrame.count;
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//1.获取模型数据
BWMessageFrame *frameModel = self.arrMessageFrame[indexPath.row];
//2.创建单元格
BWMessageCell *cell = [BWMessageCell messageCellWithTableView:tableView];
//3.把模型赋值给单元格
cell.messageFrame = frameModel;
//4.返回单元格
return cell;
} - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
BWMessageFrame *frameModel = self.arrMessageFrame[indexPath.row];
return frameModel.rowHeight;
} #pragma mark - 懒加载
- (NSMutableArray *)arrMessageFrame
{
if (_arrMessageFrame == nil) { NSString *path = [[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil]; NSArray *arrDict = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *arrModel = [NSMutableArray array]; for (NSDictionary *dict in arrDict) {
//创建一个数据模型
BWMessage *dataModel = [BWMessage messageWithDict:dict];
//创建一个Frame模型
BWMessageFrame *modelFrame = [[BWMessageFrame alloc] init]; //获取上一个数据模型
BWMessage *lastMessage = (BWMessage *)[[arrModel lastObject] message];
//判断“当前的模型时间”是否和“上一个模型时间”一样
if ([dataModel.time isEqualToString:lastMessage.time]) {
dataModel.hideTime = YES;
}
else
{
dataModel.hideTime = NO;
} modelFrame.message = dataModel; //把Frame模型加载到arrModel模型数组中
[arrModel addObject:modelFrame];
}
_arrMessageFrame = arrModel;
}
return _arrMessageFrame;
} #pragma mark - 视图加载
- (void)viewDidLoad {
[super viewDidLoad];
//取消分割线
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
//设置TableView的背景色
self.tableView.backgroundColor = [UIColor colorWithRed:236.0/ green:236.0/ blue:236.0/ alpha:];
//设置TableView的行不允许被选中
self.tableView.allowsSelection = NO;
//设置文本框距离最左侧有一段距离
UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(, , , )];
//把leftView设置给文本框
self.textInput.leftView = leftView;
self.textInput.leftViewMode = UITextFieldViewModeWhileEditing; //监听键盘的弹出事件
//1.创建一个NSNotificationCenter对象
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
//2.监听键盘弹出发出的通知
[center addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; }
//通知关联方法
- (void)keyboardWillChangeFrame:(NSNotification *)noteInfo
{
// NSLog(@"通知的名称:%@",noteInfo.name);
// NSLog(@"通知的发布者:%@",noteInfo.object);
// NSLog(@"通知的内容:%@",noteInfo.userInfo);
//1.获取键盘显示完毕或者隐藏完毕后的Y值
CGRect rectEnd = [noteInfo.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardY = rectEnd.origin.y;
//用键盘的Y值减去屏幕的高度计算平移的值
CGFloat transformValue = keyboardY - self.view.frame.size.height; [UIView animateWithDuration:0.25 animations:^{ self.view.transform = CGAffineTransformMakeTranslation(, transformValue);
}]; //让UITableView的最后一行滚动到最上面
NSIndexPath *lastRowindexPath = [NSIndexPath indexPathForRow:self.arrMessageFrame.count - inSection:];
[self.tableView scrollToRowAtIndexPath:lastRowindexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
} - (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
} - (BOOL)prefersStatusBarHidden
{
return YES;
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} @end