031-UITableView(2)-iOS笔记
学习目标
1.【掌握】xib自定义cell之团购案例
2.【掌握】代码自定义cell之微博案例
一、xib自定义cell之团购案例
UITableViewCell提供了四种Cell样式,但是自带的样式往往不能满足我们的开发需求。当系统自带cell样式不能满足我们开发需求时,就应该自己手动创建cell。
手动创建cell的两种方式:
当cell的布局样式相同,只是数据不同我们就可以使用xib快速创建cell。
当布局样式不同的时候,我们就不能使用xib来创建cell,而是需要重写初始化cell的方法代码创建cell。
先了解下简单的xib快速创建cell案例,我们需要做一个团购商品展示页面,如下图所示:点击这里查看动态图
需求:顶部有图片轮播,中间是商品列表,底部有加载更多按钮并且点击后能加载新的cell。
分析:从截图可以看出每个cell的布局都是相同的,有商品图片、名 称、价格、购买数量。所以我们可以使用xib来创建,头部轮播器以前以后做过了,这里就不重点解释。今日、明日、后日推荐按钮和图片轮播是一个view。 底部加载更多也是一个单独View,不过他们都随着tableview一起滚动,所以他们都是在tableview中。
首先不考虑界面,先创建项目导入素材和plist文件,并封装模型对象。
JFGoods.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#import <Foundation/Foundation.h>
@interface JFGoods : NSObject
@property(copy,nonatomic)NSString*buyCount;
@property(copy,nonatomic)NSString*icon;
@property(copy,nonatomic)NSString*price;
@property(copy,nonatomic)NSString*title;
//快速创建模型对象的对象方法
-(instancetype)initWithDictionary:(NSDictionary*)dict;
//快速创建模型对象的类方法
+(instancetype)goodsWithDictionary:(NSDictionary*)dict;
//返回一个模型数组
+(NSMutableArray*)goodss;
@end
|
JFGoods.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#import "JFGoods.h"
@implementationJFGoods
//快速创建模型对象的对象方法
-(instancetype)initWithDictionary:(NSDictionary*)dict{
if(self=[superinit]){
[self setValuesForKeysWithDictionary:dict];
}
returnself;
}
//快速创建模型对象的类方法
+(instancetype)goodsWithDictionary:(NSDictionary*)dict{
return[[selfalloc] initWithDictionary:dict];
}
//返回一个模型数组
+(NSMutableArray*)goodss{
NSArray*array=[NSArray arrayWithContentsOfFile:[[NSBundlemainBundle] pathForResource:@"tgs.plist" ofType:nil]];
NSMutableArray*arrayM=[NSMutableArrayarray];
for(NSDictionary*dictinarray){
JFGoods*goods=[JFGoods goodsWithDictionary:dict];
[arrayM addObject:goods];
}
returnarrayM;
}
@end
|
封装好模型后,创建xib文件。注意这里是UITableViewCell控件而不是UIView,因为只有UITableViewCell才能设置cell重用标识符。
创建好xib后就创建封装cell类,注意这个类是继承自UITableViewCell。然后再xib的Custom Class选项中制定Class为我们创建的类,再进行控件属性拖线。然后定义对应方法,加载xib并创建创建cell,再对其进行子控件赋值数据。
JFGoodsCell.h
1
2
3
4
5
6
7
8
9
10
11
|
#import <UIKit/UIKit.h>
#import "JFGoods.h"
@interface JFGoodsCell : UITableViewCell
@property(strong,nonatomic)JFGoods*goods;
//快速创建cell
+(instancetype)goodsCell:(UITableView*)tableView;
@end
|
JFGoodsCell.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
#import "JFGoodsCell.h"
@interfaceJFGoodsCell()
//商品图片View
@property(weak,nonatomic)IBOutletUIImageView*iconView;
//商品标题View
@property(weak,nonatomic)IBOutletUILabel*titleView;
//商品价格View
@property(weak,nonatomic)IBOutletUILabel*priceView;
//商品购买次数View
@property(weak,nonatomic)IBOutletUILabel*buyCountView;
@end
@implementationJFGoodsCell
//快速创建cell
+(instancetype)goodsCell:(UITableView*)tableView{
//唯一标识符
staticNSString*ID=@"goods";
//从缓存中创建cell
JFGoodsCell*cell=[tableView dequeueReusableCellWithIdentifier:ID];
//如果缓存中没有就创建
if(cell==nil){
//从xib创建cell,xib已经指定了唯一标识符为goods。所以这里实现了cell重用
cell=[[[NSBundlemainBundle] loadNibNamed:@"JFGoodsCell" owner:nil options:nil] lastObject];
}
//设置单元行高度
tableView.rowHeight=80;
returncell;
}
//重写set方法为控件设置数据
-(void)setGoods:(JFGoods*)goods{
_goods=goods;
//为控件加载数据
self.iconView.image=[UIImage imageNamed:goods.icon];
self.titleView.text=goods.title;
self.priceView.text=[NSString stringWithFormat:@"¥ %@",goods.price];
self.buyCountView.text=[NSString stringWithFormat:@"%@ 人已购买",goods.buyCount];
}
@end
|
顶部的UIView我使用了网上下载的类库快速创建,类库地址:https://github.com/gsdios/SDCycleScrollView 。下载类库,并导入我们的工程,再使用xib来创建一个UIView,在UIView中再创建一个用于展示轮播的UIView并进行控件属性连线,然后将创建好的轮播器添加到这个UIView中。
提供一个快速创建顶部View的类方法,并实现。
JFHeaderView.h
1
2
3
4
5
6
7
8
|
#import <UIKit/UIKit.h>
@interface JFHeaderView : UIView
//快速创建头部View
+(instancetype)headerView;
@end
|
JFHeaderView.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
#import "JFHeaderView.h"
#import "SDCycleScrollView.h"
@interfaceJFHeaderView()
//展示轮播的UIView
@property(weak,nonatomic)IBOutletUIView*imgView;
@end
@implementationJFHeaderView
//快速创建头部View
+(instancetype)headerView{
return[[[NSBundlemainBundle] loadNibNamed:@"JFHeaderView" owner:self options:nil] lastObject];
}
//当加载xib到界面之后,会调用此方法进行xib文件的初始化操作
-(void)awakeFromNib{
//一定要调用父类的awakeFromNib方法
[superawakeFromNib];
//封装图片对象
NSArray*images=@[[UIImage imageNamed:@"ad_00"],
[UIImage imageNamed:@"ad_01"],
[UIImage imageNamed:@"ad_02"],
[UIImage imageNamed:@"ad_03"],
[UIImage imageNamed:@"ad_04"]
];
CGFloatwidth=self.imgView.bounds.size.width;
//创建不带标题的图片轮播器
SDCycleScrollView*cycleScrollView=[SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0,0,width,120) imagesGroup:images];
//轮播是否循环
cycleScrollView.infiniteLoop=YES;
//设置页码样式
cycleScrollView.pageControlStyle=SDCycleScrollViewPageContolStyleAnimated;
//设置轮播时间间隔,默认1.0秒
cycleScrollView.autoScrollTimeInterval=2.0;
//清除缓存
[cycleScrollView clearCache];
//添加轮播View到父容器中
[self.imgView addSubview:cycleScrollView];
}
@end
|
效果如图所示:
底部也是一个单独的UIView,也可以使用xib来创建。默认能点击,说明有一个Button,点击后Button隐藏Button并显示一个菊花转的控件和Label。拖好控件然后再进行LoadingView和LoadingBtn的属性连线。
加载更多数据的思路分析:
点击按钮后不仅仅是能隐藏按钮、显示加载View,还能实现真正的加载数据功能。要实现这个功能,我们必须要用到当前的tableview和模型数组,并能修改模型数组。
如果将控制器作为方法参数传递过来也是可以实现的,但是这不符合MVC设计思想,所以我们将控制器设为代理来帮我们完成控制器才能完成的事情。
给按钮添加单击事件,调用代理方法。在点击了按钮后,会触发单击事件,调用self中的方法来完成数据添加功能。
JFFooterView.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#import <UIKit/UIKit.h>
@classJFFooterView;
//定义一个代理协议
@protocolJFFooterViewDelegate<NSObject>
//必须实现的代理方法
@required
-(void)footerViewDidClicking:(JFFooterView*)footerView;
@end
@interface JFFooterView : UIView
//快速创建底部View
+(instancetype)footerView;
//底部View的代理属性
@property(weak,nonatomic)id<JFFooterViewDelegate>delegate;
@end
|
JFFooterView.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
#import "JFFooterView.h"
@interfaceJFFooterView()
@property(weak,nonatomic)IBOutletUIView*loadingView;//加载View
@property(weak,nonatomic)IBOutletUIButton*loadingBtn;//加载Button
//点击加载按钮事件
-(IBAction)loadingClick;
@end
@implementationJFFooterView
//快速创建底部View
+(instancetype)footerView{
return[[[NSBundlemainBundle] loadNibNamed:@"JFFooterView" owner:self options:nil] lastObject];
}
//点击加载按钮事件
-(IBAction)loadingClick{
//点击后让按钮隐藏,加载view取消隐藏
self.loadingBtn.hidden=YES;
self.loadingView.hidden=NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(1*NSEC_PER_SEC)),dispatch_get_main_queue(),^{
//让按钮隐藏取消隐藏,加载view隐藏
self.loadingBtn.hidden=NO;
self.loadingView.hidden=YES;
//加载数据,判断是否实现了代理方法
if([self.delegate respondsToSelector:@selector(footerViewDidClicking:)]){
//调用代理方法,将加载更多数据功能交给代理来完成
[self.delegate footerViewDidClicking:self];
}else{
NSLog(@"没有实现代理方法");
}
});
}
@end
|
完成了封装数据模型、封装Cell、创建顶部和底部UIView后,最后来控制器进行各种调用。先在Main.storyboard中拖拽一个满屏的UITableView控件并进行属性连线操作。最后再控制器中加载数据、创建cell并实现加载更多数据的代理方法。
ViewController.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
#import "ViewController.h"
#import "JFGoods.h"
#import "JFGoodsCell.h"
#import "JFHeaderView.h"
#import "JFFooterView.h"
@interfaceViewController()<UITableViewDataSource,JFFooterViewDelegate>
@property(weak,nonatomic)IBOutletUITableView*tableView;
@property(strong,nonatomic)NSMutableArray*goodss;
@end
@implementationViewController
//隐藏状态栏
-(BOOL)prefersStatusBarHidden{
returnYES;
}
-(void)viewDidLoad{
[superviewDidLoad];
//指定数据源对象
self.tableView.dataSource=self;
//设置头部view
JFHeaderView*headerView=[JFHeaderViewheaderView];
self.tableView.tableHeaderView=headerView;
//设置底部view
JFFooterView*footerView=[JFFooterViewfooterView];
self.tableView.tableFooterView=footerView;
//让当前控制器作为底部View的代理对象
footerView.delegate=self;
}
-(void)didReceiveMemoryWarning{
[superdidReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//懒加载模型数组
-(NSArray*)goodss{
if(_goodss==nil){
_goodss=[JFGoodsgoodss];
}
return_goodss;
}
//每组多少行
-(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section{
returnself.goodss.count;
}
//创建cell
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{
//取出商品模型
JFGoods*goods=self.goodss[indexPath.row];
//创建cell
JFGoodsCell*cell=[JFGoodsCell goodsCell:tableView];
//为cell赋值数据
cell.goods=goods;
returncell;
}
//当点击了底部View后触发的代理方法
-(void)footerViewDidClicking:(JFFooterView*)footerView{
//创建商品信息字典
NSDictionary*dict=@{@"title":@"镀金翔",@"icon":@"5ee372ff039073317a49af5442748071",@"price":@"10",@"buyCount":@"1009"};
//创建商品模型
JFGoods*newGoods=[JFGoods goodsWithDictionary:dict];
//添加到模型数组
[self.goodss addObject:newGoods];
//刷新所有数据
[self.tableView reloadData];
//刷新单行数据,实现添加商品动画效果
NSIndexPath*path=[NSIndexPath indexPathForRow:self.goodss.count-1 inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationBottom];
//重新设置偏移值
self.tableView.contentOffset=CGPointMake(0,CGRectGetMaxY(footerView.frame)-self.tableView.frame.size.height);
}
@end
|
案例易错总结:
1.通过plist加载模型数据,Controller中懒加载数据,设置tableview的数据源和代理对象。
2.默认情况xib和视图类是不能进行连线操作的,需要在xib中指定自定义类。
3.要给xib中的控件赋值,就必须把模型对象传递给cell,可以设置方法或直接定义模型属性。
4.加载xib创建cell时要注意xib不能加后缀名,并且是返回数组最后一个元素。
5.整个案例以MVC模式来设计,设置MVC逻辑group分类管理。
6.底部View加载更多数据用代理来完成,不要把控制器传递给底部View类。
二、代码自定义cell之微博案例
当每个cell的结构样式不同的时候,我们就不能使用xib来快速创建cell,而是通过代码创建控件来创建cell。我们通过一个微博案例来演示代码创建cell的方法,下图就是微博案例完成后的截图:点击这里查看动态图
需求:实现微博消息界面,每个人都有头像且尺寸一样,每个人都有名字字数不一定相同,有vip就显示皇冠没有就不显示,正文自动换行,有配图就显示配图没有就不显示。
分析:从上图可以看出,cell中应该有头像、昵称、vip、正文、 配图控件。且每个控件之间有个固定间距,vip的frame根据昵称的x坐标来决定,正文的高度决定了配图的坐标,配图尺寸可以定死。由此可得知,昵称、 正文需要计算宽、高,并且是根据昵称的字数、字体来计算。
导入素材和plist文件,并封装模型。注意这里字典中国键值对个数不是统一的,有些没有picture,但我们封装的时候按照最多键值对的字典来定义属性。因为属性都有默认值,如果是NSString则默认为nil,将nil赋值给控件并不会对程序造成影响。
JFWeibo.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#import <Foundation/Foundation.h>
@interface JFWeibo : NSObject
@property(copy,nonatomic)NSString*icon;
@property(copy,nonatomic)NSString*name;
@property(assign,nonatomic,getter=isVip)BOOLvip;
@property(copy,nonatomic)NSString*text;
@property(copy,nonatomic)NSString*picture;
//快速创建模型对象的对象方法
-(instancetype)initWithDictionary:(NSDictionary*)dict;
//快速创建模型对象的类方法
+(instancetype)weiboWithDictionary:(NSDictionary*)dict;
//返回模型数组
+(NSArray*)weibos;
@end
|
JFWeibo.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#import "JFWeibo.h"
@implementationJFWeibo
//快速创建模型对象的对象方法
-(instancetype)initWithDictionary:(NSDictionary*)dict{
if(self=[superinit]){
[self setValuesForKeysWithDictionary:dict];
}
returnself;
}
//快速创建模型对象的类方法
+(instancetype)weiboWithDictionary:(NSDictionary*)dict{
return[[selfalloc] initWithDictionary:dict];
}
//返回模型数组
+(NSArray*)weibos{
NSArray*array=[NSArray arrayWithContentsOfFile:[[NSBundlemainBundle] pathForResource:@"statuses.plist" ofType:nil]];
NSMutableArray*arrayM=[NSMutableArrayarray];
for(NSDictionary*dictinarray){
JFWeibo*weibo=[JFWeibo weiboWithDictionary:dict];
[arrayM addObject:weibo];
}
returnarrayM;
}
@end
|
代码自定义cell的高度每行都不同,所以一般不能通过tableView.rowHeight属性来定义。我们需要根据每个cell子控件的frame计算每个cell的高度,并且需要通过UITableView的代理方法来指定每个cell的高度。
但是:指定cell高度的代理方法是在创建调用创建cell的方法之前调用的,也就是我们需要在创建cell之前就计算出cell子控件的frame和cell的高度。
这个UITableView的代理方法返回每个cell的高度,执行优先级高于下面返回cell的方法。
1
|
-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath;
|
这个UITableView的数据源方法返回cell,但是他的调用优先级比上面的代理方法优先级低,也就是后执行这个方法。
1
|
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath;
|
通过上面的分析得之,我们需要在创建cell之前计算好cell子控件的frame和cell的高度。所以我们封装一个cell的frame模型类,通过模型数据来计算cell子控件的frame和cell的高度。
JFWeiboFrame.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@classJFWeibo;
//在这里定于字体宏,当其他类导入头文件后也能使用
#define nameFont [UIFont systemFontOfSize:18]
#define textFont [UIFont systemFontOfSize:16]
@interface JFWeiboFrame : NSObject
//数据模型对象
@property(strong,nonatomic)JFWeibo*weibo;
//cell高度
@property(assign,nonatomic,readonly)CGFloatcellHeight;
//各个子控件的frame
@property(assign,nonatomic,readonly)CGRecticonFrame;
@property(assign,nonatomic,readonly)CGRectnameFrame;
@property(assign,nonatomic,readonly)CGRectvipFrame;
@property(assign,nonatomic,readonly)CGRecttextFrame;
@property(assign,nonatomic,readonly)CGRectpictureFrame;
//返回frame模型数组
+(NSArray*)weiboFrames;
@end
|
JFWeiboFram.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
#import "JFWeiboFrame.h"
#import "JFWeibo.h"
//每个子控件之间的间距
#define margin 10
@implementationJFWeiboFrame
//返回frame模型数组
+(NSArray*)weiboFrames{
NSArray*array=[JFWeiboweibos];
NSMutableArray*arrayM=[NSMutableArrayarray];
for(JFWeibo*weiboinarray){
//创建frame模型
JFWeiboFrame*weiboFrame=[[JFWeiboFramealloc] init];
//为frame模型赋值模型数据,因为重写了set方法。所以会在赋值后计算cell子控件的frame和行高
weiboFrame.weibo=weibo;
//将计算好cell子控件的frame和行高的frame模型存到数组
[arrayM addObject:weiboFrame];
}
returnarrayM;
}
//重写set方法,初始化frame和行高
-(void)setWeibo:(JFWeibo*)weibo{
_weibo=weibo;
//初始化frame
[selfloadingFrame];
//初始化行高
[selfloadingRowHeight];
}
//初始化frame
-(void)loadingFrame{
//头像
CGFloaticonW=30;
CGFloaticonH=iconW;
CGFloaticonX=margin;
CGFloaticonY=iconX;
_iconFrame=CGRectMake(iconX,iconY,iconW,iconH);
//名字
CGSizenameSize=[self.weibo.name boundingRectWithSize:CGSizeMake(MAXFLOAT,MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: nameFont} context:nil].size;
CGFloatnameW=nameSize.width;
CGFloatnameH=nameSize.height;
CGFloatnameX=CGRectGetMaxX(_iconFrame)+margin;
CGFloatnameY=(iconH-nameH)/2+margin;
_nameFrame=CGRectMake(nameX,nameY,nameW,nameH);
//vip
CGFloatvipW=14;
CGFloatvipH=14;
CGFloatvipX=CGRectGetMaxX(_nameFrame)+margin;
CGFloatvipY=(iconH-vipH)/2+margin;
_vipFrame=CGRectMake(vipX,vipY,vipW,vipH);
//正文
CGSizetextSize=[self.weibo.text boundingRectWithSize:CGSizeMake(355,MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: textFont} context:nil].size;
CGFloattextW=textSize.width;
CGFloattextH=textSize.height;
CGFloattextX=margin;
CGFloattextY=CGRectGetMaxY(_iconFrame)+margin;
_textFrame=CGRectMake(textX,textY,textW,textH);
//配图
CGFloatpictureW=100;
CGFloatpictureH=100;
CGFloatpictureX=margin;
CGFloatpictureY=CGRectGetMaxY(_textFrame)+margin;
_pictureFrame=CGRectMake(pictureX,pictureY,pictureW,pictureH);
}
//初始化行高
-(void)loadingRowHeight{
if(self.weibo.picture){
_cellHeight=CGRectGetMaxY(_pictureFrame)+margin;
}else{
_cellHeight=CGRectGetMaxY(_textFrame)+margin;
}
}
@end
|
封装好cell的frame后就开始封装cell,因为我们每个cell的布局样式不同,所以只能使用代码创建子控件自定义cell。封装cell的类中不需要模型属性,直接定于frame模型属性就行了,因为frame模型属性中就有模型属性。
JFWeiboFrame.h
1
2
3
4
5
6
7
8
9
10
11
12
|
#import <UIKit/UIKit.h>
@classJFWeiboFrame;
@interface JFWeiboCell : UITableViewCell
//frame模型对象属性
@property(strong,nonatomic)JFWeiboFrame*weiboFrame;
//快速创建cell
+(instancetype)weiboCell:(UITableView*)tableView;
@end
|
注意我们需要重写initWithStyle:reuseIdentifier:方法创建cell的子控件,因为系统自带的cell样式和xib都不能使用,只能代码创建cell的子控件来展示数据。
JFWeiboFrame.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
#import "JFWeiboCell.h"
#import "JFWeibo.h"
#import "JFWeiboFrame.h"
@interfaceJFWeiboCell()
@property(weak,nonatomic)UIImageView*iconView; //头像
@property(weak,nonatomic)UILabel*nameView; //名字
@property(weak,nonatomic)UIImageView*vipView; //vip
@property(weak,nonatomic)UILabel*textView; //文本
@property(weak,nonatomic)UIImageView*pictureView; //配图
@end
@implementationJFWeiboCell
//快速创建cell
+(instancetype)weiboCell:(UITableView*)tableView{
staticNSString*ID=@"weibo";
//从缓存中创建cell
JFWeiboCell*cell=[tableView dequeueReusableCellWithIdentifier:ID];
if(cell==nil){
//缓存中么有cell就新创建,这里的默认初始化方法不能满足需求,需重写
cell=[[JFWeiboCellalloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
returncell;
}
//重写initWithStyle:reuseIdentifier:方法创建cell的子控件
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)reuseIdentifier{
//重写父类初始化方法,需先调用父类初始化方法初始化父类成员
if(self=[super initWithStyle:style reuseIdentifier:reuseIdentifier]){
//头像
UIImageView*iconView=[[UIImageViewalloc] init];
[self addSubview:iconView];
self.iconView=iconView;
//名字
UILabel*nameView=[[UILabelalloc] init];
nameView.font=nameFont; //设置字体
[self addSubview:nameView];
self.nameView=nameView;
//vip
UIImageView*vipView=[[UIImageViewalloc] init];
[self addSubview:vipView];
self.vipView=vipView;
//正文
UILabel*textView=[[UILabelalloc] init];
textView.font=textFont; //设置字体
textView.numberOfLines=0; //自动换行
[self addSubview:textView];
self.textView=textView;
//配图
UIImageView*pictureView=[[UIImageViewalloc] init];
[self addSubview:pictureView];
self.pictureView=pictureView;
}
returnself;
}
//重写set方法为cell的子控件赋值
-(void)setWeiboFrame:(JFWeiboFrame*)weiboFrame{
_weiboFrame=weiboFrame;
//加载子控件数据
[selfloadingData];
//设置子控件frame
[selfsetWeiboFrame];
}
-(void)loadingData{
//取出数据模型
JFWeibo*weibo=self.weiboFrame.weibo;
//头像
self.iconView.image=[UIImage imageNamed:weibo.icon];
//名字
self.nameView.text=weibo.name;
//vip
if(weibo.isVip){
//如果是vip就取消隐藏vip图标
self.vipView.hidden=NO;
self.vipView.image=[UIImage imageNamed:@"vip"];
}else{
//不是vip就隐藏vip图标
self.vipView.hidden=YES;
}
//正文
self.textView.text=weibo.text;
//配图
self.pictureView.image=[UIImage imageNamed:weibo.picture];
}
-(void)setWeiboFrame{
//头像
self.iconView.frame=self.weiboFrame.iconFrame;
//名字
self.nameView.frame=self.weiboFrame.nameFrame;
//vip
self.vipView.frame=self.weiboFrame.vipFrame;
//正文
self.textView.frame=self.weiboFrame.textFrame;
//配图
self.pictureView.frame=self.weiboFrame.pictureFrame;
}
@end
|
封装好cell后,我们就可以开始在控制台调用了。首先先在Main.storyboard中删除原有的控制器,拖拽一个新的控制器控件UITableViewController。这样做的好处是我们不需要手动添加UITableView数据源、代理协议,手动指定数据源、代理对象,会默认帮我们设置好这一切。
特别注意的是:必须勾选Is initial View Controller选项,让当前控制器设为默认视图控制器,否则看不到界面。
最后在控制器中定义一个frame模型数组属性并懒加载数据,然后实现数据源方法和代理方法,创建cell并为cell赋值数据。
ViewController.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
#import "ViewController.h"
#import "JFWeiboFrame.h"
#import "JFWeiboCell.h"
@interfaceViewController()
//frame模型数组
@property(strong,nonatomic)NSArray*weiboFrames;
@end
@implementationViewController
-(void)viewDidLoad{
[superviewDidLoad];
}
-(void)didReceiveMemoryWarning{
[superdidReceiveMemoryWarning];
}
//隐藏状态栏
-(BOOL)prefersStatusBarHidden{
returnYES;
}
//懒加载frame模型数组
-(NSArray*)weiboFrames{
if(_weiboFrames==nil){
//返回一个创建好的frame模型数组
_weiboFrames=[JFWeiboFrameweiboFrames];
}
return_weiboFrames;
}
//每组多少行
-(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section{
//模型数组有多少个元素就有多少行
returnself.weiboFrames.count;
}
//创建cell
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{
//获取模型对象
JFWeiboFrame*weiboFrame=self.weiboFrames[indexPath.row];
//创建cell
JFWeiboCell*cell=[JFWeiboCell weiboCell:tableView];
//为cell赋值
cell.weiboFrame=weiboFrame;
returncell;
}
//返回cell行高
-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{
//获取模型对象
JFWeiboFrame*weiboFrame=self.weiboFrames[indexPath.row];
returnweiboFrame.cellHeight;
}
@end
|
案例易错总结:
1.创建cell的子控件是在重写initWithStyle:reuseIdentifier:方法中创建。
2.封装frame模型的意义就是为了计算cell的高度,因为设置高度的代理方法优先级高于创建cell的数据源方法。
3.使用UITableViewController控制器,可以节省我们遵循协议、指定代理的步骤。