需求:类似于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的像素来平铺图片,直到平铺满整个图片为止。