UItableView在开发中使用的频率非常高 所以今天本人通过tableView用纯代码的方式来模仿以下这么个界面
首先看这个界面的信息是一条一条显示的 所以选择用tabView实现 既然是tableView就需要数据源加载数据 这时创建数据模型XCmessage(注:这里数据模型中的数据是通过plist来加载的) 用来存放数据 数据模型代码如下所示
typedef enum{
Other = 0,
me
}userType;
@interface XCmessage : NSObject
/** 时间 */
@property (copy,nonatomic) NSString *time;
/** 正文 */
@property (copy,nonatomic) NSString *text;
/*
这里需要定义的属性 虽然是数字 虽然只有两种状态 但是如果定义为BOOL或者NSinteger类型的话 阅读性较差
所以这里考虑定义成枚举类型 用来保存用户的类型 究竟是自己 还是别人
*/
@property (assign,nonatomic) userType type;
@property (assign,nonatomic,getter=isHidden) BOOL timeHid;
//初始化方法
- (instancetype) initWithDict:(NSDictionary *)dict;
+ (instancetype) messageWithDict:(NSDictionary *)dict;
@implementation XCmessage
- (instancetype) initWithDict:(NSDictionary *)dict{
self = [super init];
if(self){
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
+ (instancetype) messageWithDict:(NSDictionary *)dict{
return [[self alloc]initWithDict:dict];
}
@end
数据模型建立完成之后,发现如果用系统提供的cell满足不了我对界面的需求,所以就要自定义cell,在cell的初始化方法中,添加子控件。而且cell中还应该有一个数据模型,用来给cell的各个控件赋值以及计算控件的frame,如果用XCmessage作为cell的模型,就要将cell中控件的frame计算放在这个模型中,这时,每次显示cell的时候都会计算一遍控件的frame,这样会消耗性能。这样就考虑能不能有什么方法 让我的frame只计算一次就可以了呢?原来想到懒加载中的内容只加载一次就可以了!所以又考虑能不能将cell中控件的frame放到懒加载中计算呢?这样的话frame就只计算一次的,当要显示cell的时候,就像XCmessage提供数据一样,直接给控件的frame提供计算好的frame并赋值就好了。因此,我的方法是,再提供一个XCmessageFrame模型,而且我希望在显示cell的时候,只提供一个XCMessageFrame模型就完成对控件的数据以及frame赋值,所以,这个模型中包含XCmessage模型,用来提供数据,以及计算好的frame,用来提供frame。
代码如下
@class XCmessage;
@interface XCmessageFrame : NSObject
//数据模型
@property (strong,nonatomic) XCmessage * message;
/** 时间frame */
@property (assign,nonatomic,readonly) CGRect timeF;
/** 头像frame */
@property (assign,nonatomic,readonly) CGRect iconF;
/** 正文frame */
@property (assign,nonatomic,readonly) CGRect textF;
/** 行高 */
@property (assign,nonatomic,readonly) CGFloat cellHeight;
@end
//重写message的set方法 以便为cell赋值
- (void)setMessage:(XCmessage *)message{
//将值赋值给 self.message
_message = message;
//计算frame
[self settingFrame];
}
//计算frame方法
- (void) settingFrame{
//获取屏幕的宽
CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
//计算时间
if(self.message.timeHid == NO){
CGFloat timeX = 0;
CGFloat timeY = 0;
CGFloat timeW =screenW;
CGFloat timeH = 21;
_timeF = CGRectMake(timeX, timeY, timeW, timeH);
}
//计算头像
CGFloat magin = 10;
CGFloat iconX = 0;
CGFloat timeMaxY = CGRectGetMaxY(_timeF);
CGFloat iconY = timeMaxY + magin;
CGFloat iconW = 50;
CGFloat iconH = iconW;
if(self.message.type == Other){
iconX = magin;
_iconF = CGRectMake(iconX, iconY, iconW, iconH);
}else{
iconX = screenW - magin - iconW;
_iconF = CGRectMake(iconX, iconY, iconW, iconH);
}
//计算文本框
//计算文本框的时候 一般只计算宽 不计算高
CGSize textMaxSize = CGSizeMake(180 , MAXFLOAT);
CGSize textSize = [self sizeWithText:self.message.text font:textFont maxSize:textMaxSize];
//分为自己发得消息 和别人发得消息两种情况
//因为是分两种情况 所以先定义一个textX 并初始化为0 用来分情况储存正文的x
CGFloat textX = 0;
CGFloat textY = iconY;
//设置按钮的fram
CGSize btnSize = CGSizeMake(textSize.width + 50, textSize.height + 40);
if(self.message.type == Other){
textX = CGRectGetMaxX(_iconF) + magin;
}else{
textX = iconX - magin - btnSize.width;
}
_textF = (CGRect){{textX,textY},btnSize};
//计算行高
CGFloat iconMaxY = CGRectGetMaxY(_iconF);
CGFloat textMaxY = CGRectGetMaxY(_textF);
_cellHeight = MAX(iconMaxY, textMaxY) + magin;
}
//计算文字尺寸
- (CGSize) sizeWithText:(NSString *)text font:(UIFont *)font maxSize:(CGSize)size{
return [text boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil].size;
}
@end
数据模型定义完成之后 创建自定的cell
代码如下
@class XCmessageFrame;
@interface XCmassageCell : UITableViewCell
//引入 frame模型作为属性来设置数据
@property (strong,nonatomic) XCmessageFrame * messageFrame;
//重写初始化方法
- (instancetype) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
//快速创建cell的类方法
+ (instancetype) cell:(UITableView *)tableView;
@end
//cell中一初始化时就应具备的属性
@interface XCmassageCell ()
/** 时间 */
@property (weak,nonatomic) UILabel *timeView;
/** 头像 */
@property (weak,nonatomic) UIImageView *iconView;
/** 正文 */
@property (weak,nonatomic) UIButton *textView;
@end
@implementation XCmassageCell
+ (instancetype) cell:(UITableView *)tableView{
//定义重用标示符
static NSString * ID = @"message";
//判断tableView缓存池中是否有所需cell
XCmassageCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
//如果没有 创建cell
if(cell == nil){
cell = [[XCmassageCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
{
//返回cell
return cell;
}
//重写UItableViewCell的初始化方法 初始化自定义的cell
- (instancetype) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if(self){
//设置cell的背景
self.backgroundColor = [UIColor clearColor];
//初始化控件
/** 时间 */
UILabel * timeView = [[UILabel alloc]init];
[self.contentView addSubview:timeView];
timeView.textAlignment = NSTextAlignmentCenter;
timeView.font = textFont;
self.timeView = timeView;
/** 头像 */
UIImageView * iconView = [[UIImageView alloc]init];
[self.contentView addSubview:iconView];
_iconView = iconView;
/** 正文 */
UIButton * textView = [[UIButton alloc]init];
[self.contentView addSubview:textView];
[textView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
textView.titleLabel.font = textFont;
textView.titleLabel.numberOfLines = 0;
_textView = textView;
//设置内边距
self.textView.contentEdgeInsets = UIEdgeInsetsMake(20, 20,20, 20);
self.clipsToBounds = YES;
}
return self;
}
//重写messageFrame的set方法 为cell设置数据
- (void)setMessageFrame:(XCmessageFrame *)messageFrame{
_messageFrame = messageFrame;
//设置数据
[self settingDate];
//设置frame
[self settingFrame];
}
//设置数据方法
- (void) settingDate{
XCmessage * message = self.messageFrame.message;
//设置时间
self.timeView.text = message.time;
//设置头像 这里要设置自己的头像和其他人的头像
if(message.type == Other){
self.iconView.image = [UIImage imageNamed:@"Other"];
//设置聊天气泡
UIImage * image = [UIImage imageNamed:@"chat_recive_nor"];
//保证图片拉伸不变形
UIImage * newimage = [image stretchableImageWithLeftCapWidth:image.size.width*0.5 topCapHeight:image.size.height*0.5];
[self.textView setBackgroundImage:newimage forState:UIControlStateNormal];
}else{
self.iconView.image = [UIImage imageNamed:@"me"];
UIImage * image = [UIImage imageNamed:@"chat_send_nor"];
UIImage * newimage = [image stretchableImageWithLeftCapWidth:image.size.width*0.5 topCapHeight:image.size.height*0.5];
//设置聊天气泡
[self.textView setBackgroundImage:newimage forState:UIControlStateNormal];
}
//设置正文
[self.textView setTitle:message.text forState:UIControlStateNormal]; }
- (void) settingFrame{
self.iconView.frame = self.messageFrame.iconF;
self.timeView.frame = self.messageFrame.timeF;
self.textView.frame = self.messageFrame.textF;
}
@end
再控制器中实现数据源方法
代码如下
@interface ViewController () <UITableViewDataSource,UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
/*
文本框不应设置在tableView上 否则滑动的时候 文本框也会跟着滑动
所以定义一个transView用来添加文本框 并将transView添加到控制器的View上
*/
@property (weak, nonatomic) IBOutlet UIView *transView;
@property (strong,nonatomic) NSMutableArray * messageFrameModle;
@property (weak, nonatomic) IBOutlet UITextField *inputView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//设置数据
self.tableView.dataSource = self;
self.tableView.delegate = self;
self.tableView.allowsSelection = NO;
self.tableView.backgroundColor = [UIColor lightGrayColor];
//隐藏cell边线
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.messageFrameModle.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
XCmassageCell * cell = [XCmassageCell cell:tableView];
cell.messageFrame = self.messageFrameModle[indexPath.row];
return cell;
}
//设置多个行高
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
XCmessageFrame * messageFrame = self.messageFrameModle[indexPath.row];
return messageFrame.cellHeight;
}
//隐藏状态栏
-(BOOL)prefersStatusBarHidden{
return YES;
}
//懒加载
- (NSMutableArray *)messageFrameModle{
if(_messageFrameModle == nil){
_messageFrameModle = [NSMutableArray array];
NSString * path = [[NSBundle mainBundle]pathForResource:@"messages.plist" ofType:nil];
NSArray * dicArr = [NSArray arrayWithContentsOfFile:path];
NSMutableArray * arrM = [[NSMutableArray alloc]init];
for (NSDictionary * dict in dicArr) {
XCmessage * message = [XCmessage messageWithDict:dict];
XCmessageFrame * messageFrame = [[XCmessageFrame alloc] init];
XCmessageFrame * lastFrame = [arrM lastObject];
message.timeHid = [message.time isEqualToString:lastFrame.message.time];
messageFrame.message = message;
[arrM addObject:messageFrame];
}
_messageFrameModle = arrM;
}
return _messageFrameModle;
}
@end
至此 界面搭建完毕