[iOS基础控件 - 6.9] 聊天界面Demo

A.需求

做出一个类似于QQ、微信的聊天界面

1.每个cell包含发送时间、发送人(头像)、发送信息

2.使用对方头像放在左边,我方头像在右边

3.对方信息使用白色背景对话框,我方信息使用蓝色背景对话框

4.隐藏相同的发送时间

5.底部功能按钮:语音按钮、消息输入框、表情按钮、附加按钮

6.响应键盘事件,呼出键盘、隐藏键盘时对上述的视图作出上移操作

7.键盘的发送事件处理

B.实现点

1.底层视图搭建

上部分聊天信息框:UITableView

下部分功能区:UIButton

信息输入框使用无边框,然后使用自带背景图片,以保证在不同版本的iOS中样式一致

2.构建框架

依照“微博展示”的代码框架,设计:

  • 自定义message模型
  • 自定义cell
  • 装载了message模型和cell子控件位置尺寸的frame

3.使用扩展,给NSString加上文本size计算的功能

 1 @implementation NSString (Extension)
 2
 3 /** 测量文本的尺寸 */
 4 - (CGSize)sizeWithFont:(UIFont *)font maxSize:(CGSize)maxSize {
 5     NSDictionary *attrs = @{NSFontAttributeName: font};
 6     CGSize size =  [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
 7
 8     return size;
 9 }
10
11 @end

给信息文本框计算位置和尺寸

 1     // 3.信息,尺寸可变
 2     CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
 3     // 3.1 设置最大尺寸
 4     CGSize textMaxSize = CGSizeMake(screenWidth - iconWidth - padding * 5, MAXFLOAT);
 5     // 3.2 计算真实尺寸
 6     CGSize textRealSize = [message.text sizeWithFont:MESSAGE_TEXT_FONT maxSize:textMaxSize];
 7
 8     // 3.3 调整信息的位置
 9     CGFloat textX;
10     if (MessageTypeMe == message.type) {
11         // 我方,放在靠右
12         textX = CGRectGetMinX(_iconFrame) - textRealSize.width - padding;
13     } else {
14         // 对方,放在靠左
15         textX = CGRectGetMaxX(_iconFrame) + padding;
16     }
17
18     CGFloat textY = iconY;
19     _textFrame = CGRectMake(textX, textY, textRealSize.width, textRealSize.height);

4.使用数据中的信息类型,判断是我方发出的信息还是对方发出的信息,计算头像和信息的位置

 1     // 2.头像
 2     CGFloat iconWidth = 40;
 3     CGFloat iconHeight = 40;
 4
 5     // 2.1 根据信息的发送方调整头像位置
 6     CGFloat iconX;
 7     if (MessageTypeMe == message.type) {
 8         // 我方,放在右边
 9         iconX = [UIScreen mainScreen].bounds.size.width - padding - iconWidth;
10     } else {
11         // 对方,放在左边
12         iconX = padding;
13     }
14
15     CGFloat iconY = CGRectGetMaxY(_timeFrame) + padding;
16     _iconFrame = CGRectMake(iconX, iconY, iconWidth, iconHeight);

5.加上信息背景框

我方:使用蓝色背景聊天框

对方:使用白色背景聊天框

重点:图片的中心拉伸,利用图片分别水平、垂直方向某个区域进行拉伸,保持其他部分的图形 —>对UIImage使用扩展,返回具备了特定拉伸方式属性的图片

拉伸图片方式属性:

 1 @implementation UIImage (Extension)
 2
 3 + (UIImage *) resizableImage:(NSString *) imageName {
 4     UIImage *image = [UIImage imageNamed:imageName];
 5     // 取图片中部的1 x 1进行拉伸
 6     UIEdgeInsets insets = UIEdgeInsetsMake(image.size.height/2, image.size.width/2, image.size.height/2 + 1, image.size.width/2 + 1);
 7     return [image resizableImageWithCapInsets:insets];
 8 }
 9
10 @end

设置图片:

 1     // 3.1 设置聊天框
 2     NSString *chatImageNormalName;
 3     NSString *chatImageHighlightedName;
 4     if (MessageTypeMe == messageFrame.message.type) {
 5         chatImageNormalName = @"chat_send_nor";
 6         chatImageHighlightedName = @"chat_send_press_pic";
 7     } else {
 8         chatImageNormalName = @"chat_receive_nor";
 9         chatImageHighlightedName = @"chat_receive_press_pic";
10     }
11
12     UIImage *chatImageNormal = [UIImage resizableImage:chatImageNormalName];
13     UIImage *chatImageHighlighted = [UIImage resizableImage:chatImageHighlightedName];
14     [self.textView setBackgroundImage:chatImageNormal forState:UIControlStateNormal];
15     [self.textView setBackgroundImage:chatImageHighlighted forState:UIControlStateHighlighted];

6.调整信息文字内边距,让文字被“包裹”在聊天框内

1     // 3.2 调整文字的内边距
2     textView.contentEdgeInsets = UIEdgeInsetsMake(TEXT_INSET, TEXT_INSET, TEXT_INSET, TEXT_INSET);

因为背景图片边缘有空白,改变了文字内边距之后,高度会变高,需要对装载信息的view的frame尺寸做出相应改变:

 1     // 3.信息,尺寸可变
 2     CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
 3     // 3.1 设置文本最大尺寸
 4     CGSize textMaxSize = CGSizeMake(screenWidth - iconWidth - padding * 10, MAXFLOAT);
 5     // 3.2 计算文本真实尺寸
 6     CGSize textRealSize = [message.text sizeWithFont:MESSAGE_TEXT_FONT maxSize:textMaxSize];
 7
 8     // 3.3 按钮尺寸
 9     CGSize btnSize = CGSizeMake(textRealSize.width + TEXT_INSET*2, textRealSize.height + TEXT_INSET*2);
10
11     // 3.4 调整信息的位置
12     CGFloat textX;
13     if (MessageTypeMe == message.type) {
14         // 我方,放在靠右
15         textX = CGRectGetMinX(_iconFrame) - btnSize.width - padding;
16     } else {
17         // 对方,放在靠左
18         textX = CGRectGetMaxX(_iconFrame) + padding;
19     }
20
21     CGFloat textY = iconY;
22     _textFrame = CGRectMake(textX, textY, btnSize.width, btnSize.height);

7.屏蔽相同的发送时间

(1)在message模型中定义一个标志

1 /** 是否隐藏发送时间 */
2 @property(nonatomic, assign) BOOL hideTime;

(2)当控制器从plist文件装载信息的时候初始化此标志

1 // 判断是否发送时间与上一条信息的发送时间相同,若是则不用显示了
2 MessageFrame *lastMessageFrame = [mdictArray lastObject];
3 if (lastMessageFrame && [message.time isEqualToString:lastMessageFrame.message.time]) {
4      message.hideTime = YES;
5 }

(3)只有hideTime ==  NO,计算frame的时候,长期需要计算发送时间的frame

1     // 1.发送时间
2     if (NO == message.hideTime) {
3         CGFloat timeWidth = [UIScreen mainScreen].bounds.size.width;
4         CGFloat timeHeight = 40;
5         CGFloat timeX = 0;
6         CGFloat timeY = 0;
7         _timeFrame = CGRectMake(timeX, timeY, timeWidth, timeHeight);
8     }

8.响应键盘呼出缩回事件,上移或下移恢复整个版面(聊天区和功能区)

(1)设置控制器为键盘监听器

// 设置虚拟键盘监听器

1     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];

(2)编写监听方法

 1 /** 点击拖曳聊天区的时候,缩回键盘 */
 2 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
 3     // 1.缩回键盘
 4     [self.view endEditing:YES];
 5 }
 6
 7
 8 #pragma mark - 监听事件
 9 - (void) keyboardWillChangeFrame:(NSNotification *) note {
10     // 1.取得弹出后的键盘frame
11     CGRect keyboardFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
12
13     // 2.键盘弹出的耗时时间
14     CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
15
16     // 3.键盘变化时,view的位移,包括了上移/恢复下移
17     CGFloat transformY = keyboardFrame.origin.y - self.view.frame.size.height;
18
19     [UIView animateWithDuration:duration animations:^{
20         self.view.transform = CGAffineTransformMakeTranslation(0, transformY);
21     }];
22
23
24 }

9.设置数据框TextField,改变键盘

(1)当没有文字的时候禁用回车,设置回车样式

(2)调不出中文键盘

虽然在设置里面添加了中文键盘,但是依然找不到进入中文键盘的按钮

10.发送消息

(1)拖入信息输入框到控制器,设置控制器为输入框TextField的代理

1     // 设置信息输入框的代理
2     self.inputView.delegate = self;

(2)响应回车事件

 1 #pragma mark - TextField 代理方法
 2 /** 回车响应事件 */
 3 - (BOOL)textFieldShouldReturn:(UITextField *)textField {
 4     // 我方发出信息
 5     [self sendMessageWithContent:textField.text andType:MessageTypeMe];
 6
 7     // 自动回复
 8     [self sendMessageWithContent:[NSString stringWithFormat:@"%@\n%@", textField.text, @"你妹!!!"] andType:MessageTypeOhter];
 9
10     // 消除消息框内容
11     self.inputView.text = nil;
12
13     [self.tableView reloadData];
14
15     // 滚动到最新的消息
16     NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:self.messages.count - 1 inSection:0];
17     [self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
18
19     return YES; // 返回值意义不明
20 }
21
22 // 发送消息
23 - (void) sendMessageWithContent:(NSString *) text andType:(MessageType) type {
24     // 获取当前时间
25     NSDate *date = [NSDate date];
26     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
27     formatter.dateFormat = @"yyyy-MMM-dd hh:mm:ss";
28     NSString *dateStr = [formatter stringFromDate:date];
29
30     // 我方发出信息
31     NSDictionary *dict = @{@"text":text,
32                            @"time":dateStr,
33                            @"type":[NSString stringWithFormat:@"%d", type]};
34
35     Message *message = [[Message alloc] init];
36     [message setValuesForKeysWithDictionary:dict];
37     MessageFrame *messageFrame = [[MessageFrame alloc] init];
38     messageFrame.message = message;
39
40     [self.messages addObject:messageFrame];
41 }
42  

(3)自动滚动在最底端

1     // 滚动到最新的消息
2     NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:self.messages.count - 1 inSection:0];
3     [self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

时间: 2024-10-08 20:21:07

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

iOS基础控件UINavigationController中的传值

iOS基础控件UINavigationController中的传值,代理传值,正向传值,反向传值 #import <UIKit/UIKit.h> //声明一个协议 @protocol SendValue<NSObject> //定义一个方法 - (void)sendBtnTitle:(NSString *)title; @end @interface FirstViewController : UIViewController // 定义代理 @property (nonatomi

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

A.概述 在"[iOS基础控件 - 4.4] APP列表 进一步封装,初见MVC模式”上进一步改进,给“下载”按钮加上效果.功能 1.按钮点击后,显示为“已下载”,并且不可以再按 2.在屏幕中间弹出一个消息框,通知消息“xx已经被安装”,慢慢消失 3.消息框样式为圆角半透明 B.不使用代理模式,使用app空间组和主View之间的父子View关系 1.在主View中创建一个消息框 主View控制器:ViewController.m 1 // 创建下载成功消息框 2 CGFloat labelWid

ios基础控件之开关按钮(UISwitch)

UISwitch控件是iOS开发的基础控件,是非常简单的一个控件,因为它的方法比较少.UISwitch继承于UIControl基类,因此可以当成活动控件使用. 注意:开关状态通过它的on属性进行读取,该属性是一个BOOL属性 创建: UISwitch* mySwitch = [[ UISwitch alloc]initWithFrame:CGRectMake(0.150.0f,100.0f,0.0f,0.0f)]; 可能你会疑问为什么它的大小都设置为0?没错,它的大小你设置是无效的,系统会为你分

【iOS基础控件 - 11】【Demo】模仿qq ,微信 UI 聊天界面

A.需求 做出一个类似于QQ.微信的聊天界面 1.每个cell包含发送时间.发送人(头像).发送信息 2.使用对方头像放在左边,我方头像在右边 3.对方信息使用白色背景对话框,我方信息使用蓝色背景对话框 4.隐藏相同的发送时间 5.底部功能按钮:语音按钮.消息输入框.表情按钮.附加按钮 6.响应键盘事件,呼出键盘.隐藏键盘时对上述的视图作出上移操作 7.键盘的发送事件处理 Code Source: B.实现点 1.底层视图搭建 上部分聊天信息框:UITableView 下部分功能区:UIButt

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

框架: 所有代码文件: Model: 1 // 2 // Message.h 3 // QQChatDemo 4 // 5 // Created by hellovoidworld on 14/12/8. 6 // Copyright (c) 2014年 hellovoidworld. All rights reserved. 7 // 8 // message信息模型,存储聊天记录 9 10 #import <Foundation/Foundation.h> 11 12 typedef en

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

A.storyboard 控件版 1.label 2.textfield a.Keyboard Type 账号:Number Pad 密码:Number and Punctuation b.Placeholder:提示文字 c.Clear Button: Appears with editing 清除按钮 d.Secure Text Entry:密码格式输入 3.button 1 @interface ViewController () 2 @property (weak, nonatomic)

iOS 基础控件(下)

上篇介绍了UIButton.UILabel.UIImageView和UITextField,这篇就简短一点介绍UIScrollView和UIAlertView. UIScrollView 顾名思义也知道这个是和滚动相关的控件,在Android开发时遇到过ScrollView,当内容的尺寸超出了屏幕范围之后,用ScrollView则可以通过滚动的方式使得超出屏幕的那部分内容通过滚动的方式显示出来,在Android里面有水平的ScrollView和垂直的ScrollView,在iOS里面就只有一个S

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

A.需求 1.搭建一个“私人通讯录”Demo 2.模拟登陆界面 账号 密码 记住密码开关 自动登陆开关 登陆按钮 3.退出注销 4.增删改查 5.恢复数据(取消修改) B.基本架构 1. 5个控制器 (1)导航控制器 NavigationController (2)登陆 UIViewController 输入账号密码 记住密码.自动登录开关 登陆跳转按钮 (3)联系人列表 TableViewController 注销功能 添加联系人跳转按钮 (4)添加联系人 UIView (5)查看.编辑 UI

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

A.需求 1.自定义一个UIView和xib,包含国家名和国旗显示 2.学习row的重用 B.实现步骤 1.准备plist文件和国旗图片 2.创建模型 1 // 2 // Flag.h 3 // CountriesSelection 4 // 5 // Created by hellovoidworld on 14/12/16. 6 // Copyright (c) 2014年 hellovoidworld. All rights reserved. 7 // 8 9 #import <Foun