UI基础--UITableView实现仿QQ聊天页面

需求:类似于QQ聊天页面的展示,内容包括有头像、时间、聊天内容。相同时间发生的内容,只显示第一条内容的时间,并且点击输入框时,可以滚动到最后一条内容信息。具体效果图:

实例的文件结构:

实现的具体步骤:

1、布局界面,主要包括一个UIImageView、3个UIButton、1个UITextField;

2、自定义数据模型类,并测试数据是否能正常加载;

3、自定义cell,由于每行数据的高度都是不规则的,所以考虑先自定义好frame再来写自定义cell。属性包括frame模型以及生成可重用cell的方法,要注意的是需要重写- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier方法,写给cell各子控件赋值以及生成frame的方法;

4、自定义frame,属性包括模型数据和行高以及cell中各个子控件的frame;

5、在控制器中写UITableView的 数据显示的方法;

6、订阅键盘通知,并且弹出键盘时,改变view的frame,最后也要记得销毁通知(有订阅就要有销毁);

7、写UITextFieldDelegate方法,在界面中显示新发送的消息;

具体的代码:

Model:

 1 //
 2 //  JWMessage.h
 3 //  12-24-Message-Two
 4 //
 5 //  Created by xiaomoge on 14/12/24.
 6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
 7 //
 8
 9 #import <Foundation/Foundation.h>
10 typedef enum {
11     JWMessageTypeSelf,
12     JWMessageTypeOther
13 } JWMessageType;
14 @interface JWMessage : NSObject
15 @property (nonatomic,copy) NSString *text;
16 @property (nonatomic,copy) NSString *time;
17 @property (nonatomic,assign) JWMessageType type;
18 @property (nonatomic,assign,getter=isHiddemTime) BOOL hiddemTime;
19 - (instancetype)initWithDic:(NSDictionary *)dic;
20 + (instancetype)messageWithDic:(NSDictionary *)dic;
21 + (NSMutableArray *)messageList;
22 @end
 1 //
 2 //  JWMessage.m
 3 //  12-24-Message-Two
 4 //
 5 //  Created by xiaomoge on 14/12/24.
 6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
 7 //
 8
 9 #import "JWMessage.h"
10
11 @implementation JWMessage
12 - (instancetype)initWithDic:(NSDictionary *)dic {
13     if (self = [super init]) {
14         [self setValuesForKeysWithDictionary:dic];
15     }
16     return self;
17 }
18 + (instancetype)messageWithDic:(NSDictionary *)dic {
19     return [[self alloc] initWithDic:dic];
20 }
21
22 + (NSMutableArray *)messageList {
23     NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"messages" ofType:@"plist"]];
24
25     NSMutableArray *tempArray = [NSMutableArray array];
26     //定义一个前信息
27     JWMessage *preMessage;
28     for (NSDictionary *dic in array) {
29         JWMessage *message = [JWMessage messageWithDic:dic];
30         //判断前信息和当前信息是否相同,如相同即隐藏当前信息的时间frame
31         if ([message.time isEqualToString:preMessage.time]) {
32             message.hiddemTime = YES;
33         }
34         [tempArray addObject:message];
35         //获得前信息的数据
36         preMessage = [tempArray lastObject];
37     }
38     return tempArray;
39 }
40 @end
 1 //
 2 //  JWMessageFrame.h
 3 //  12-24-Message-Two
 4 //
 5 //  Created by xiaomoge on 14/12/24.
 6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
 7 //
 8 #define TEXTFONT 14
 9 #import <UIKit/UIKit.h>
10 @class JWMessage;
11 @interface JWMessageFrame : NSObject
12 @property (nonatomic,assign) CGFloat rowHeight;
13 @property (nonatomic,assign) CGRect timeFrame;
14 @property (nonatomic,assign) CGRect iconFrame;
15 @property (nonatomic,assign) CGRect textFrame;
16 @property (nonatomic,strong) JWMessage *message;
17 + (NSMutableArray *)messageFrameList;
18 @end
 1 //
 2 //  JWMessageFrame.m
 3 //  12-24-Message-Two
 4 //
 5 //  Created by xiaomoge on 14/12/24.
 6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
 7 //
 8
 9 #import "JWMessageFrame.h"
10 #import "JWMessage.h"
11 #import "NSString+Ext.h"
12 @implementation JWMessageFrame
13 /*
14  重写set方法,设置fram
15  */
16 - (void)setMessage:(JWMessage *)message {
17     _message = message;
18
19     //屏幕宽度
20     UIScreen *screen = [UIScreen mainScreen];
21     CGFloat screenW = screen.bounds.size.width;
22     //间距
23     CGFloat margin = 10;
24
25     //时间frame
26     if (!message.hiddemTime) {//如果时间不相同时,才设置时间的frame
27         _timeFrame = CGRectMake(0, 0, screenW, 40);
28     }
29
30     //头像frame
31     CGFloat iconW = 50;
32     CGFloat iconH = 50;
33     CGFloat iconX;
34     CGFloat iconY = CGRectGetMaxY(_timeFrame);
35     if (message.type == JWMessageTypeSelf) {
36         //自己的头像在右边,所以是屏幕的宽度减去间距,再减去头像的宽度
37         iconX = screenW - margin - iconW;
38     }else {
39         iconX = margin;
40     }
41     _iconFrame = CGRectMake(iconX, iconY, iconW, iconH);
42
43     //内容frame
44     //取得内容的大小
45     CGSize textSize = [message.text setTextSize:CGSizeMake(200, MAXFLOAT) andFontSize:TEXTFONT];
46     //取得内容按钮的大小
47     CGSize btnSize = CGSizeMake(textSize.width + 40, textSize.height + 40);
48     CGFloat textX;
49     //内容的Y值和头像的Y值是一样的
50     CGFloat textY = iconY;
51     if (message.type == JWMessageTypeSelf) {
52         //自己的内容在右边,所以是头像的X值减去按钮的宽度,再减去间距
53         textX = iconX - btnSize.width - margin;
54     }else {
55         //对方的内容在左边,所以是头像的宽度加上间距
56         textX = iconW + margin;
57     }
58     _textFrame = CGRectMake(textX, textY, btnSize.width, btnSize.height);
59
60     //行高
61     //取得内容的最大Y值
62     CGFloat textMax = CGRectGetMaxY(_textFrame);
63     //取得头像的最大Y值
64     CGFloat iconMax = CGRectGetMaxY(_iconFrame);
65     //行高的多少是根据内容多少来判断的,内容少时是头像的最大Y值,内容过多时,就是内容的最大Y值了,所以用了一个MAX函数,取最大值
66     _rowHeight = MAX(textMax, iconMax) + margin;
67
68 }
69 + (NSMutableArray *)messageFrameList {
70     NSArray *message = [JWMessage messageList];
71     NSMutableArray *tem = [NSMutableArray array];
72     for (JWMessage *msg in message) {
73         JWMessageFrame *frame = [[JWMessageFrame alloc] init];
74         frame.message = msg;
75         [tem addObject:frame];
76     }
77     return tem;
78 }
79 @end

View:

 1 //
 2 //  JWMessageCell.h
 3 //  12-24-Message-Two
 4 //
 5 //  Created by xiaomoge on 14/12/24.
 6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
 7 //
 8
 9 #import <UIKit/UIKit.h>
10 @class JWMessageFrame;
11 @interface JWMessageCell : UITableViewCell
12 @property (nonatomic,strong) JWMessageFrame *messageFrame;
13 + (instancetype)cellWithTableView:(UITableView *)tableView;
14 @end
 1 //
 2 //  JWMessageCell.m
 3 //  12-24-Message-Two
 4 //
 5 //  Created by xiaomoge on 14/12/24.
 6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
 7 //
 8
 9 #import "JWMessageCell.h"
10 #import "JWMessage.h"
11 #import "JWMessageFrame.h"
12 #import "UIImage+Ext.h"
13 @interface JWMessageCell ()
14 @property (nonatomic,weak) UILabel *timeLabel;
15 @property (nonatomic,weak) UIImageView *iconView;
16 @property (nonatomic,weak) UIButton *textBtn;
17 @end
18 @implementation JWMessageCell
19 //创建可重用的cell
20 + (instancetype)cellWithTableView:(UITableView *)tableView {
21     //创建缓存池标识
22     static NSString *resue = @"msg";
23     //当缓存池有空闲的cell时,可重用
24     JWMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:resue];
25     //当缓存池内暂时没有空闲的cell时,自动创建
26     if (!cell) {
27         cell = [[self alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:resue];
28     }
29     return cell;
30 }
31 //重写
32 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
33     if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
34         //清除背景颜色
35         self.backgroundColor = [UIColor clearColor];
36         //初始化时间子控件
37         UILabel *time = [[UILabel alloc] init];
38         [self.contentView addSubview:time];
39         self.timeLabel = time;
40         //把时间居中显示
41         time.textAlignment = NSTextAlignmentCenter;
42         //初始化头像子控件
43         UIImageView *img = [[UIImageView alloc] init];
44         [self.contentView addSubview:img];
45         self.iconView = img;
46         //设置头像的圆角
47         img.layer.cornerRadius = 25;
48         //设置是否剪裁多余的部分
49         img.layer.masksToBounds = YES;
50         //初始化内容子控件
51         UIButton *text = [UIButton buttonWithType:UIButtonTypeCustom];
52         [self.contentView addSubview:text];
53         self.textBtn = text;
54         [text setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
55         text.titleLabel.numberOfLines = 0;
56         //设置内容的间距
57         text.contentEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);
58         //设置内容的字体大小
59         text.titleLabel.font = [UIFont systemFontOfSize:TEXTFONT];
60     }
61     return self;
62 }
63 - (void)setMessageFrame:(JWMessageFrame *)messageFrame {
64     _messageFrame = messageFrame;
65     [self setSubviewsContent];
66     [self setSubviewsFrame];
67 }
68 - (void)setSubviewsContent {
69     JWMessage *msg = self.messageFrame.message;
70     //给时间子控件赋值
71     self.timeLabel.text = msg.time;
72     //给头像子控件赋值
73     self.iconView.image = [UIImage imageNamed:msg.type == JWMessageTypeSelf ? @"me" :@"other"];
74     //给内容子控件赋值
75     [self.textBtn setTitle:msg.text forState:UIControlStateNormal];
76     //设置内容子控件的背景图片
77     if (msg.type == JWMessageTypeSelf) {
78        [self.textBtn setBackgroundImage:[UIImage setImage:@"chat_send_nor"] forState:UIControlStateNormal];
79         [self.textBtn setBackgroundImage:[UIImage setImage:@"chat_send_press_pic"] forState:UIControlStateHighlighted];
80     }else {
81         [self.textBtn setBackgroundImage:[UIImage setImage:@"chat_recive_nor"] forState:UIControlStateNormal];
82         [self.textBtn setBackgroundImage:[UIImage setImage:@"chat_recive_press_pic"] forState:UIControlStateHighlighted];
83     }
84 }
85 - (void)setSubviewsFrame {
86     self.timeLabel.frame = self.messageFrame.timeFrame;
87     self.iconView.frame = self.messageFrame.iconFrame;
88     self.textBtn.frame = self.messageFrame.textFrame;
89 }
90 @end

Controller:

 1 //
 2 //  ViewController.m
 3 //  12-24-Message-Two
 4 //
 5 //  Created by xiaomoge on 14/12/24.
 6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
 7 //
 8
 9 #import "ViewController.h"
10 #import "JWMessage.h"
11 #import "JWMessageFrame.h"
12 #import "JWMessageCell.h"
13 @interface ViewController ()<UITextFieldDelegate,UITableViewDataSource,UITableViewDelegate>
14 @property (weak, nonatomic) IBOutlet UITableView *tableView;
15 @property (nonatomic,strong) NSMutableArray *messageFrame;
16 @end
17
18 @implementation ViewController
19 #pragma mark - 隐藏状态栏
20 - (BOOL)prefersStatusBarHidden {
21     return YES;
22 }
23 #pragma mark - 懒加载
24 - (NSMutableArray *)messageFrame {
25     if (!_messageFrame) {
26         _messageFrame = [JWMessageFrame messageFrameList];
27     }
28     return _messageFrame;
29 }
30 - (void)viewDidLoad {
31     [super viewDidLoad];
32     //取消分割线
33     self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
34     //设置背景颜色
35     self.tableView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
36     //订阅键盘通知
37     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clickTableView:) name:UIKeyboardWillChangeFrameNotification object:nil];
38 }
39 //键盘frame发生改变时,view也跟着改变
40 - (void)clickTableView:(NSNotification *)noti {
41     CGFloat duration = [noti.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
42     CGRect frame = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
43     CGFloat offX = frame.origin.y - self.view.frame.size.height;
44     [UIView animateWithDuration:duration animations:^{
45         self.view.transform = CGAffineTransformMakeTranslation(0, offX);
46     }];
47 }
48 //销毁订阅键盘通知
49 - (void)dealloc {
50     [[NSNotificationCenter defaultCenter] removeObserver:self];
51 }
52 #pragma mark - UITableViewDataSource方法
53 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
54     return self.messageFrame.count;
55 }
56 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
57     JWMessageCell *cell = [JWMessageCell cellWithTableView:tableView];
58     cell.messageFrame = self.messageFrame[indexPath.row];
59     return cell;
60 }
61 #pragma mark - UITableViewDelegate方法
62 - (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
63     JWMessageFrame *frame = self.messageFrame[indexPath.row];
64     return frame.rowHeight;
65 }
66 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
67     [self.view endEditing:YES];
68 }
69 #pragma mark - UITextFieldDelegate方法
70 -(BOOL)textFieldShouldReturn:(UITextField *)textField {
71     JWMessage *msg = [[JWMessage alloc] init];
72     msg.type = JWMessageTypeSelf;
73     msg.text = textField.text;
74     NSDate *date = [NSDate date];
75     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
76     formatter.dateFormat = @"HH:mm";
77     msg.time = [formatter stringFromDate:date];
78
79     //判断前一条信息和当前信息的时间是否相同
80     JWMessage *preMessage = (JWMessage *)[[self.messageFrame lastObject] message];
81     if ([preMessage.time isEqualToString:msg.time]) {
82         msg.hiddemTime = YES;
83     }
84
85     JWMessageFrame *frame = [[JWMessageFrame alloc] init];
86     frame.message = msg;
87     [self.messageFrame addObject:frame];
88
89     //重新加载数据
90     [self.tableView reloadData];
91     NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.messageFrame.count - 1 inSection:0];
92     //滚动显示最后一条数据
93     [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
94     return YES;
95 }
96 @end

Category:

 1 //
 2 //  UIImage+Ext.m
 3 //  12-24-Message-Two
 4 //
 5 //  Created by xiaomoge on 14/12/24.
 6 //  Copyright (c) 2014年 xiaomoge. All rights reserved.
 7 //
 8
 9 #import "UIImage+Ext.h"
10
11 @implementation UIImage (Ext)
12 //平铺图片,改变图片的大小
13 + (UIImage *)setImage:(NSString *)name {
14     UIImage *imageName = [UIImage imageNamed:name];
15     return [imageName stretchableImageWithLeftCapWidth:imageName.size.width * 0.5 topCapHeight:imageName.size.height * 0.5];
16 }
17 @end

Category中的NSString+Ext内容和上一篇的内容是一样的,都是获取文字的大小,可回看。http://www.cnblogs.com/xiaomoge/p/4200169.html

在这里,要注意的:

1、Category中的方法,该方法是在图片的中心,获取1X的像素来平铺图片,直到平铺满整个图片为止。

时间: 2024-08-10 00:07:24

UI基础--UITableView实现仿QQ聊天页面的相关文章

UI基础--UITableView实现仿QQ好友列表页面

需求:类似于QQ好友列表页面的显示,有好友分组,有好友数量,在线人数,vip会员.展开分组时显示分组好友,合并分组时不显示:具体效果图如下: 分析: 1.展开分组时显示分组好友,该功能可以使用显示UITableViewCell的数据即可: 2.分组头可以考虑使用一个headerView来实现: 示例文件结构: 具体实现步骤: 1.自定义数据模型类,由于plist文件中包含了2个字典,所以需要写2个数据模型: 2.自定义cell,属性包括数据模型以及生成可重用cell的方法,由于系统自带的子控件即

UI基础--UITableView实现仿微博页面

需求:类似于微博内容页面的展示,内容包括有头像.呢称.会员标志.微博内容.微博图片(可有可没有).具体效果图: 实例的文件结构: 实现的具体步骤: 1.自定义数据模型类,并测试数据是否能正常加载: 2.设置storyBoard,把UIViewController改为UITableViewController,,并更改controller文件为继承自UITableViewController的自定义文件,设置关联; 3.自定义cell,由于每行数据的高度都是不规则的,所以考虑先自定义好frame再

Android UI设计: 分享一个仿QQ聊天消息提示可以拖拉气泡

首先上效果图 功能有: 1. 可以随时拖拉 2. 拖拉超过一定距离会监听 3. 拖拉返回的时候,有来回反弹效果 此效果先是参照了网上github两个版本的效果.不过都不是自己想要那么理想. 1.其中有一个是两层,一层是textview控件和一层surfaceview.经测试效果不错,但是效率不高,在刚点击的有闪动现象.会跳帧.于是放弃这种方法.不过它能够全屏拖拉. 2另一个的实现方法很赞,全程一个view就完事,全部自绘,没有用控件.而且贝塞尔曲线就是中间那个瘦瘦的,随着距离越来越瘦的,是两条贝

仿QQ聊天软件2.0版

仿QQ聊天软件2.0版 转载请标明出处:牟尼的专栏 http://blog.csdn.net/u012027907     上次课设做了Java版的仿QQ聊天程序,这次软件实训,我们继续完善了仿QQ聊天程序,将上次未完成及不完善的地方进行完善和改进,还新加了部分功能:表情输入.气泡模式.文件传输.截屏.语音聊天.逐步向QQ的基本功能靠齐.通过这次软件实训,又有了很多收获. 一.设计内容及要求 1.1综述 A.系统概述 我们要做的就是类似QQ这样的面向企业内部的聊天软件,基本功能和QQ类似.首先,

Android特效专辑(六)——仿QQ聊天撒花特效,无形装逼,最为致命

Android特效专辑(六)--仿QQ聊天撒花特效,无形装逼,最为致命 我的关于特效的专辑已经在CSDN上申请了一个专栏--http://blog.csdn.net/column/details/liuguilin.html 日后我所写的特效专辑也会以一添加在这个专栏上,今天写的这个特效,是关于聊天的,你肯定遇到过,就是你跟人家聊天的时候,比如发送应(么么哒),然后屏幕上全部就是表情了,今天我们就是做这个,撒花的特效,国际惯例,上图 截图 实现这样的效果,你要知道贝塞尔曲线,何谓贝塞尔曲线?其实

Socket实现仿QQ聊天(可部署于广域网)附源码(1)-简介

1.前言 本次实现的这个聊天工具是我去年c#程序设计课程所写的Socket仿QQ聊天,由于当时候没有自己的服务器,只能在机房局域网内进行测试,最近在腾讯云上买了一台云主机(本人学生党,腾讯云有个学生专享活动一元一个月的云服务器,如果还是学生的可以试一下,地址http://www.qcloud.com/event/qcloudSchool),经过重新编码实现了广域网聊天的功能.下面开始介绍我的自制聊天软件啦!!! 2.功能 1. 聊天室服务器端的创建. 2. 聊天室客户端的创建. 3. 实现客户与

html打开个人QQ聊天页面

打开qq聊天页面(有权限需要添加好友) <a href="tencent://message/?uin=1578929883&Site=&Menu=yes" class="icon icon-qq first"></a> 强制打开临时会话 <a href="tencent://message/?Menu=yes&uin=这里是QQ号& Service=300&sigT=45a1e5847

mina的编码和解码以及断包的处理,发送自定义协议,仿qq聊天,发送xml或json和

最近一段时间以来,mina很火,和移动开发一样,异常的火爆.前面写了几篇移动开发的文章,都还不错,你们的鼓励就是我最大的动力.好了,废话少说.我们来看下tcp通讯吧. tcp通讯对于java来说是很简单的.就是socket,也就是大家常说的套接字.大家不要把它看的很难.说白了tcp通讯其实就是数据流的读写.一条输入流,一条输出流.分别复杂发消息和接收消息. 明白了这些,ok,我们来看看我写的例子吧.先看服务器端的测试类的源码: package com.minaqq.test; import co

仿QQ聊天布局--iOS

虽然注册博客园这么久了,但很少在这上面写些东西,一来也是觉得自己能力不够,二来怕误人子弟,所以一直秉着“多看,多做,少说”的原则混迹在各论坛之中.但日子久了,觉着这其实是一种逃避的方法.思来想去,那些牛逼的人其实是那些能把自己心中所想完全表达出来,让人看之舒服,听之认同的人.所以,除了“多看,多做,少说”外,怕要加一条“多总结”,否则恐要淹死在这信息化的浪潮中了. 最近对QQ.微信聊天布局产生兴趣,便搜索资料试着搞搞,趁着空隙,先上效果图,得空再补充说明. 大致思路就是 自定义tableView