需求:类似于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