一、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

iOS UI-QQ聊天布局的更多相关文章

  1. AJ学IOS(18)UI之QQ聊天布局_键盘通知实现自动弹出隐藏_自动回复

    AJ分享,必须精品 先看图片 第一步完成tableView和Cell的架子的图 完善图片 键盘弹出设置后图片: 自动回复图: 粗狂的架子 tableView和Cell的创建 首相tableView为了 ...

  2. iOS UI基础-10.0 QQ聊天布局之键盘及文本使用

    要实现的效果:   这里只说用到的几个知识点 1.图片包含文字 在设置文字的Frame的时候,使用背景(按钮)的尺寸,文字使用了内边距 背景图片,使用拉伸 /** * 返回一张可以随意拉伸不变形的图片 ...

  3. QQ聊天界面的布局和设计(IOS篇)-第一季

    我写的源文件整个工程会再第二季中发上来~,存在百度网盘, 感兴趣的童鞋, 可以关注我的博客更新,到时自己去下载~.喵~~~ QQChat Layout - 第一季 一.准备工作 1.将假数据messa ...

  4. QQ聊天界面的布局和设计(IOS篇)-第二季

    QQChat Layout - 第二季 本来第二季是快写好了, 也花了点功夫, 结果gitbook出了点问题, 给没掉了.有些细节可能会一带而过, 如有疑问, 相互交流进步~. 在第一季中我们完成了Q ...

  5. IOS详解TableView——对话聊天布局的实现

    上篇博客介绍了如何使用UITableView实现类似QQ的好友界面布局.这篇讲述如何利用自定义单元格来实现聊天界面的布局. 借助单元格实现聊天布局难度不大,主要要解决的问题有两个: 1.自己和其他人说 ...

  6. Objective-c——UI基础开发第八天(QQ聊天界面)

    一.知识点: QQ聊天界面 双模型的使用(dataModel和frameModel) UITextField的使用 通知的使用 拉伸图片的两种方法(slicing/image对象的resizeable ...

  7. 搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 (1)

    搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 原文地址(英文):http://www.networkcomms.net/creating ...

  8. Android—简单的仿QQ聊天界面

    最近仿照QQ聊天做了一个类似界面,先看下界面组成(画面不太美凑合凑合呗,,,,):

  9. 高仿qq聊天界面

    高仿qq聊天界面,给有需要的人,界面效果如下: 真心觉得做界面非常痛苦,给有需要的朋友. chat.xml <?xml version="1.0" encoding=&quo ...

  10. 【WPF】实现类似QQ聊天消息的界面

    最近公司有个项目,是要求实现类似 QQ 聊天这种功能的. 如下图 这没啥难的,稍微复杂的也就表情的解析而已. 表情在传输过程中的实现参考了新浪微博,采用半角中括号代表表情的方式.例如:“abc[dog ...

随机推荐

  1. HCNP学习笔记之IP地址、子网掩码、网关的关系

      0x00 概述 网络管理中的IP地址.子网掩码和网关是每个网管必须要掌握的基础知识,只有掌握它,才能够真正理解TCP/IP协议的设置. 以下我们就来深入浅出地讲解什么是子网掩码. IP地址的结构 ...

  2. Python3 判断文件和文件夹是否存在、创建文件夹

    Python3 判断文件和文件夹是否存在.创建文件夹 python中对文件.文件夹的操作需要涉及到os模块和shutil模块. 创建文件: 1) os.mknod(“test.txt”) 创建空文件  ...

  3. 20165310_获奖感想与Java阶段性学习总结

    获奖感想与Java阶段性学习总结 一.Learning By Doing ​ 在此之前,其实我并没有想到能够成为小黄杉的第一批成员之一,喜悦之余,也感受到了许多的压力.小黄杉一方面代表了老师对于我这一 ...

  4. JAVA I/O(六)多路复用IO

    在前边介绍Socket和ServerSocket连接交互的过程中,读写都是阻塞的.套接字写数据时,数据先写入操作系统的缓存中,形成TCP或UDP的负载,作为套接字传输到目标端,当缓存大小不足时,线程会 ...

  5. Python3基础 os.path.getsize 获得文件的大小

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  6. 加强树状数组luogu3368

    暴力树状数组30分,这该怎么办: 知识点回顾 差分数组中 开头结尾改变了值之后 求他的前缀,发现区间内所有数都改变 然后我们做差分树状数组 #include<cstdio> using n ...

  7. ubuntu12 root账户自动登录

    Ubuntu为了系统安全,root帐号的密码是随机的,如果临时需要提升至root权限以执行一些命令,需要使用sudo命令.产线上有几台使用Ubuntu的机器,因为使用者不固定,并且执行程序时需要使 用 ...

  8. Luogu P1533 可怜的狗狗

    题目链接:https://www.luogu.org/problemnew/show/P1533 没人写$fhq\ treap$做法,那我就补一篇qwq 看到这题第一时间想主席树,然后发现我还没学主席 ...

  9. ClickOnce部署winform

    1.完成winform程序. 示例: 2.项目->属性->发布. 有关ftp在iis中的配置,请参考:IIS中添加ftp站点 :url也需在iis中配置.flashfxp中显示ftp成功 ...

  10. Ambari安装指南

    一.准备工作 l 基本工具 1) 安装epel,epel是一个提供高质量软件包的项目.先检查主机上是否安装: rpm -q epel-release 2) 如果没有安装,使用rpm命令安装: rpm ...