A.需求
做出一个类似于QQ、微信的聊天界面
1.每个cell包含发送时间、发送人(头像)、发送信息
2.使用对方头像放在左边,我方头像在右边
3.对方信息使用白色背景对话框,我方信息使用蓝色背景对话框
4.隐藏相同的发送时间
5.底部功能按钮:语音按钮、消息输入框、表情按钮、附加按钮
6.响应键盘事件,呼出键盘、隐藏键盘时对上述的视图作出上移操作
7.键盘的发送事件处理
 
Code Source: https://github.com/hellovoidworld/ChatDemo
 
 
 
B.实现点
1.底层视图搭建
上部分聊天信息框:UITableView
下部分功能区:UIButton
信息输入框使用无边框,然后使用自带背景图片,以保证在不同版本的iOS中样式一致
 
 
2.构建框架
依照“微博展示”的代码框架,设计:
  • 自定义message模型
  • 自定义cell
  • 装载了message模型和cell子控件位置尺寸的frame
 
 
3.使用扩展,给NSString加上文本size计算的功能
 @implementation NSString (Extension)

 /** 测量文本的尺寸 */
- (CGSize)sizeWithFont:(UIFont *)font maxSize:(CGSize)maxSize {
NSDictionary *attrs = @{NSFontAttributeName: font};
CGSize size = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size; return size;
} @end
 
给信息文本框计算位置和尺寸
     // 3.信息,尺寸可变
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
// 3.1 设置最大尺寸
CGSize textMaxSize = CGSizeMake(screenWidth - iconWidth - padding * , MAXFLOAT);
// 3.2 计算真实尺寸
CGSize textRealSize = [message.text sizeWithFont:MESSAGE_TEXT_FONT maxSize:textMaxSize]; // 3.3 调整信息的位置
CGFloat textX;
if (MessageTypeMe == message.type) {
// 我方,放在靠右
textX = CGRectGetMinX(_iconFrame) - textRealSize.width - padding;
} else {
// 对方,放在靠左
textX = CGRectGetMaxX(_iconFrame) + padding;
} CGFloat textY = iconY;
_textFrame = CGRectMake(textX, textY, textRealSize.width, textRealSize.height);
 
4.使用数据中的信息类型,判断是我方发出的信息还是对方发出的信息,计算头像和信息的位置
     // 2.头像
CGFloat iconWidth = ;
CGFloat iconHeight = ; // 2.1 根据信息的发送方调整头像位置
CGFloat iconX;
if (MessageTypeMe == message.type) {
// 我方,放在右边
iconX = [UIScreen mainScreen].bounds.size.width - padding - iconWidth;
} else {
// 对方,放在左边
iconX = padding;
} CGFloat iconY = CGRectGetMaxY(_timeFrame) + padding;
_iconFrame = CGRectMake(iconX, iconY, iconWidth, iconHeight);
 
 
5.加上信息背景框
我方:使用蓝色背景聊天框
对方:使用白色背景聊天框
重点:图片的中心拉伸,利用图片分别水平、垂直方向某个区域进行拉伸,保持其他部分的图形 —>对UIImage使用扩展,返回具备了特定拉伸方式属性的图片
 
拉伸图片方式属性:
 @implementation UIImage (Extension)

 + (UIImage *) resizableImage:(NSString *) imageName {
UIImage *image = [UIImage imageNamed:imageName];
// 取图片中部的1 x 1进行拉伸
UIEdgeInsets insets = UIEdgeInsetsMake(image.size.height/, image.size.width/, image.size.height/ + , image.size.width/ + );
return [image resizableImageWithCapInsets:insets];
} @end
 
设置图片:
     // 3.1 设置聊天框
NSString *chatImageNormalName;
NSString *chatImageHighlightedName;
if (MessageTypeMe == messageFrame.message.type) {
chatImageNormalName = @"chat_send_nor";
chatImageHighlightedName = @"chat_send_press_pic";
} else {
chatImageNormalName = @"chat_receive_nor";
chatImageHighlightedName = @"chat_receive_press_pic";
} UIImage *chatImageNormal = [UIImage resizableImage:chatImageNormalName];
UIImage *chatImageHighlighted = [UIImage resizableImage:chatImageHighlightedName];
[self.textView setBackgroundImage:chatImageNormal forState:UIControlStateNormal];
[self.textView setBackgroundImage:chatImageHighlighted forState:UIControlStateHighlighted];
 
 
6.调整信息文字内边距,让文字被“包裹”在聊天框内
     // 3.2 调整文字的内边距
textView.contentEdgeInsets = UIEdgeInsetsMake(TEXT_INSET, TEXT_INSET, TEXT_INSET, TEXT_INSET);
 
因为背景图片边缘有空白,改变了文字内边距之后,高度会变高,需要对装载信息的view的frame尺寸做出相应改变:
     // 3.信息,尺寸可变
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
// 3.1 设置文本最大尺寸
CGSize textMaxSize = CGSizeMake(screenWidth - iconWidth - padding * , MAXFLOAT);
// 3.2 计算文本真实尺寸
CGSize textRealSize = [message.text sizeWithFont:MESSAGE_TEXT_FONT maxSize:textMaxSize]; // 3.3 按钮尺寸
CGSize btnSize = CGSizeMake(textRealSize.width + TEXT_INSET*, textRealSize.height + TEXT_INSET*); // 3.4 调整信息的位置
CGFloat textX;
if (MessageTypeMe == message.type) {
// 我方,放在靠右
textX = CGRectGetMinX(_iconFrame) - btnSize.width - padding;
} else {
// 对方,放在靠左
textX = CGRectGetMaxX(_iconFrame) + padding;
} CGFloat textY = iconY;
_textFrame = CGRectMake(textX, textY, btnSize.width, btnSize.height);
 
 
7.屏蔽相同的发送时间
(1)在message模型中定义一个标志
 /** 是否隐藏发送时间 */
@property(nonatomic, assign) BOOL hideTime;
 
(2)当控制器从plist文件装载信息的时候初始化此标志
 // 判断是否发送时间与上一条信息的发送时间相同,若是则不用显示了
MessageFrame *lastMessageFrame = [mdictArray lastObject];
if (lastMessageFrame && [message.time isEqualToString:lastMessageFrame.message.time]) {
message.hideTime = YES;
}
              
(3)只有hideTime ==  NO,计算frame的时候,长期需要计算发送时间的frame
     // 1.发送时间
if (NO == message.hideTime) {
CGFloat timeWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat timeHeight = ;
CGFloat timeX = ;
CGFloat timeY = ;
_timeFrame = CGRectMake(timeX, timeY, timeWidth, timeHeight);
}
 
 
8.响应键盘呼出缩回事件,上移或下移恢复整个版面(聊天区和功能区)
(1)设置控制器为键盘监听器
    // 设置虚拟键盘监听器
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
 
(2)编写监听方法
 /** 点击拖曳聊天区的时候,缩回键盘 */
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// 1.缩回键盘
[self.view endEditing:YES];
} #pragma mark - 监听事件
- (void) keyboardWillChangeFrame:(NSNotification *) note {
// 1.取得弹出后的键盘frame
CGRect keyboardFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; // 2.键盘弹出的耗时时间
CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]; // 3.键盘变化时,view的位移,包括了上移/恢复下移
CGFloat transformY = keyboardFrame.origin.y - self.view.frame.size.height; [UIView animateWithDuration:duration animations:^{
self.view.transform = CGAffineTransformMakeTranslation(, transformY);
}]; }
 
 
9.设置数据框TextField,改变键盘
(1)当没有文字的时候禁用回车,设置回车样式
 
 
(2)调不出中文键盘
虽然在设置里面添加了中文键盘,但是依然找不到进入中文键盘的按钮
 
 
10.发送消息
(1)拖入信息输入框到控制器,设置控制器为输入框TextField的代理
     // 设置信息输入框的代理
self.inputView.delegate = self;
 
 
(2)响应回车事件
 #pragma mark - TextField 代理方法
/** 回车响应事件 */
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// 我方发出信息
[self sendMessageWithContent:textField.text andType:MessageTypeMe]; // 自动回复
[self sendMessageWithContent:[NSString stringWithFormat:@"%@\n%@", textField.text, @"你妹!!!"] andType:MessageTypeOhter]; // 消除消息框内容
self.inputView.text = nil; [self.tableView reloadData]; // 滚动到最新的消息
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:self.messages.count - inSection:];
[self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; return YES; // 返回值意义不明
} // 发送消息
- (void) sendMessageWithContent:(NSString *) text andType:(MessageType) type {
// 获取当前时间
NSDate *date = [NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MMM-dd hh:mm:ss";
NSString *dateStr = [formatter stringFromDate:date]; // 我方发出信息
NSDictionary *dict = @{@"text":text,
@"time":dateStr,
@"type":[NSString stringWithFormat:@"%d", type]}; Message *message = [[Message alloc] init];
[message setValuesForKeysWithDictionary:dict];
MessageFrame *messageFrame = [[MessageFrame alloc] init];
messageFrame.message = message; [self.messages addObject:messageFrame];
}
(3)自动滚动在最底端
     // 滚动到最新的消息
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:self.messages.count - inSection:];
[self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
 
 
 
 
 
 
 
 

[iOS基础控件 - 6.9] 聊天界面Demo的更多相关文章

  1. [iOS基础控件 - 5.5] 代理设计模式 (基于”APP列表"练习)

    A.概述      在"[iOS基础控件 - 4.4] APP列表 进一步封装,初见MVC模式”上进一步改进,给“下载”按钮加上效果.功能      1.按钮点击后,显示为“已下载”,并且不 ...

  2. [iOS基础控件 - 6.9.1] 聊天界面Demo 代码

    框架:   所有代码文件:   Model: // // Message.h // QQChatDemo // // Created by hellovoidworld on 14/12/8. // ...

  3. [iOS基础控件 - 3.1] QQ登陆界面

      A.storyboard 控件版 1.label 2.textfield      a.Keyboard Type           账号:Number Pad           密码:Num ...

  4. iOS 基础控件(下)

    上篇介绍了UIButton.UILabel.UIImageView和UITextField,这篇就简短一点介绍UIScrollView和UIAlertView. UIScrollView 顾名思义也知 ...

  5. [iOS基础控件 - 6.11.3] 私人通讯录Demo 控制器的数据传递、存储

    A.需求 1.搭建一个"私人通讯录"Demo 2.模拟登陆界面 账号 密码 记住密码开关 自动登陆开关 登陆按钮 3.退出注销 4.增删改查 5.恢复数据(取消修改)   这个代码 ...

  6. [iOS基础控件 - 6.10.2] PickerView 自定义row内容 国家选择Demo

    A.需求 1.自定义一个UIView和xib,包含国家名和国旗显示 2.学习row的重用   B.实现步骤 1.准备plist文件和国旗图片     2.创建模型 // // Flag.h // Co ...

  7. [iOS基础控件 - 7.0] UIWebView

    A.基本使用 1.概念 iOS内置的浏览器控件 Safari浏览器就是通过UIWebView实现的   2.用途:制作简易浏览器 (1)基本请求 创建请求 加载请求 (2)代理监听webView加载, ...

  8. [iOS基础控件 - 6.9.3] QQ好友列表Demo TableView

    A.需求 1.使用plist数据,展示类似QQ好友列表的分组.组内成员显示缩进功能 2.组名使用Header,展示箭头图标.组名.组内人数和上线人数 3.点击组名,伸展.缩回好友组   code so ...

  9. [iOS基础控件 - 6.7] 微博展示 使用代码自定义TableCell(动态尺寸)

    A.需求 1.类似于微博内容的展示 2.头像 3.名字 4.会员标志 5.内容 6.分割线 7.配图(可选,可有可无)   code source: https://github.com/hellov ...

随机推荐

  1. 函数 xdes_get_descriptor_with_space_hdr

    获得区描述符 xdes entry 的offset /********************************************************************//** ...

  2. django访问静态文件

    DJANGO 1.6 静态文件处理 添加 STATICFILES_DIRS = (os.path.abspath('static'),) 新建static目录

  3. apache开源项目--Synapse

    Apache Synapse一个易于使用.轻量级的XML与Web Services管理和集成中间件.可用于搭建SOA和ESB的基础平台.Apache Synapse支持多种标准包括:XML.XSLT. ...

  4. Java [Leetcode 160]Intersection of Two Linked Lists

    题目描述: Write a program to find the node at which the intersection of two singly linked lists begins. ...

  5. 构建属于自己的ORM框架之二--IQueryable的奥秘

    上篇文章标题乱起,被吐槽了,这次学乖了. 上篇文章中介绍了如何解析Expression生成对应的SQL语句,以及IQueryable的一些概念,以及我们所搭建的框架的思想等.但还没把它们结合并应用起来 ...

  6. Linux Kernel 4.8分支第4个候选版本发布

    导读 今天,大神Linus Torvalds宣布了Linux 4.8分支的第四个候选版本,该候选版本在提供常规驱动更新.架构改善和部分KVM调整之外最大的新功能就是修复了英特尔Skylake电源管理B ...

  7. jQuery 遍历 - parent() 方法

    ylbtech-jQuery-sizzle:jQuery 遍历 - parent() 方法  parent() 获得当前匹配元素集合中每个元素的父元素,使用选择器进行筛选是可选的. 1.A,jQuer ...

  8. Motan:目录结构

    motan是由maven管理的,在最外层的pom.xml中可以看出这个项目有多个模块组成. <modules> <module>motan-core</module> ...

  9. Excel动态生成JSON

    在最近的一个项目中,有大量的数据源来至Excel,转成JSON供前台使用.Excel数据是人工录入的,难免会有错误,所以中间会有逻辑检查.在C#中读取Excel的方式有很多,网上一搜一大堆,这里我也贴 ...

  10. Codevs No.1281 Xn数列

    2016-06-01 16:28:25 题目链接: Xn数列 (Codevs No.1281) 题目大意: 给定一种递推式为 Xn=(A*Xn-1+C)%M 的数列,求特定的某一项%G 解法: 矩阵乘 ...