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. Android studio在真机上进行调试

    1.在Android Studio中,把app的默认启动目标改为USB device,点击[app]→[app configuration],在[Target Device]选择[USB device ...

  2. PL/SQL database character set(AL32UTF8) and Client character set(ZHS16GBK) are different 2012-04-11 13:01

    启动PL/SQL Developer 报字符编码不一致错误 Database character set (AL32UTF8) and Client character set (ZHS16GBK) ...

  3. SGU 187 - Twist and whirl -- want to cheat

    原题地址:http://acm.sgu.ru/problem.php?contest=0&problem=187 太开心啦!!!!这道题从2013年开始困扰我!!今天晚上第四次下定决心把它写一 ...

  4. 基于XMPP的即时通信系统的建立(四)— 组件介绍

    服务端 服务器 许可证 操作系统 是否支持任意客户端登录 备注 ejabberd 开源 Elang 是 支持虚拟主机和集群 Openfire Apache Java 是 Tigase GPLv3 Ja ...

  5. ZJOI2006物流运输

    唉,没想出来…… 注意到预处理的作用.还有CLJ大牛说的话:这么小的数据,想干什么都可以. SPFA预处理+DP 够经典 var f:..,..]of longint; a:..,..]of bool ...

  6. 【转】JAVA之网络编程

    转自:火之光 网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分技术的学习. 在 学习网络编程以前,很多初学者 ...

  7. Android基础_3 Activity相对布局

    相对布局要比前面讲的线性布局和表格布局要灵活一些,所以平常用得也是比较多的.相对布局控件的位置是与其周围控件的位置相关的,从名字可以看出来,这些位置都是相对的,确定出了其中一个控件的位置就可以确定另一 ...

  8. CCScrollView 实现帮助界面、关卡选择

    本文出自[无间落叶]:http://blog.leafsoar.com/archives/2013/07-27.html 本文介绍了 CCScrollView 来编写帮助界面和关卡选择界面的方法,在编 ...

  9. Java [Leetcode 110]Balanced Binary Tree

    题目描述: Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced b ...

  10. Live555中RTP包的打包与发送过程分析

    这里主要分析一下,live555中关于RTP打包发送的部分.在处理完PLAY命令之后,就开始发送RTP数据包了(其实在发送PLAY命令的response包之前,就会发送一个RTP包,这里传输就已经开始 ...