1.xib方式創(chuàng)建
每個cell的顯示的內(nèi)容都是固定的,也就是cell的高度都是相同的
加載數(shù)據(jù)
有plist文件數(shù)據(jù)結(jié)構(gòu)如下
創(chuàng)建數(shù)據(jù)模型
Product.h
@interface Product : NSObject
@property (nonatomic , copy) NSString *name;
@property (nonatomic , copy) NSString *location;
@property (nonatomic , copy) NSString *count;
@property (nonatomic , copy) NSString *price;
@property (nonatomic , copy) NSString *icon;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)goodsWithDict:(NSDictionary *)dict;
@end
Product.m
@implementation Product
- (instancetype)initWithDict:(NSDictionary *)dict{
if (self = [super init]) {
self.name = dict[@"name"];
self.location = dict[@"location"];
self.count = dict[@"minproduct"];
self.price = dict[@"price"];
self.icon = dict[@"icon"];
// [self setValuesForKeysWithDictionary:dict];
}
return self;
}
+ (instancetype)goodsWithDict:(NSDictionary *)dict{
return [[self alloc]initWithDict:dict];
}
@end
懶加載數(shù)據(jù)
PageFirstTableViewController.m
//用來存儲所有團購商品的數(shù)據(jù)
@property (nonatomic , strong) NSMutableArray *goods;
#pragma mark -lazyload
- (NSMutableArray *)goods{
if (_goods ==nil) {
NSString *path = [[NSBundle mainBundle]pathForResource:@"dataSource.plist" ofType:nil];
NSArray *arrayDict = [NSArray arrayWithContentsOfFile:path];
//字典轉(zhuǎn)模型
NSMutableArray *arrayModels = [NSMutableArray array];
for (NSDictionary *dict in arrayDict) {
Product *model = [Product goodsWithDict:dict];
[arrayModels addObject:model];
}
_goods = arrayModels;
}
return _goods;
}
實現(xiàn)數(shù)據(jù)源協(xié)議
通過xib方式實現(xiàn)自定義cell
創(chuàng)建以一個.xib文件。在xib中拖一個UITableViewCell,設(shè)置高寬。向UITableViewCell中拖子控件。
創(chuàng)建一個繼承自UITableViewCell的類ProductCell與xib文件的cell相關(guān)聯(lián)。通過拖線的方式將cell的子控件拖線到ProductCell的屬性上。
ProductCell.m
@property (weak, nonatomic) IBOutlet UILabel *name;
@property (weak, nonatomic) IBOutlet UILabel *price;
@property (weak, nonatomic) IBOutlet UIImageView *icon;
....略
實現(xiàn)數(shù)據(jù)源協(xié)議
PageFirstTableViewController.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.goods.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//1.獲取模型數(shù)據(jù)
Product *model = self.goods[indexPath.row];
//2.創(chuàng)建單元格
//通過xib創(chuàng)建單元格
//由于此方法調(diào)用十分頻繁,cell的標示聲明成靜態(tài)變量有利于性能優(yōu)化
static NSString *ID = @"goods_cell"; //要在xib中設(shè)置這個id
ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
//加載xib文件,loadNibName:方法返回的是一個數(shù)組,因為xib中可能有不止一個控件
cell = [[[NSBundle mainBundle]loadNibNamed:@"ProductCell" owner:nil options:nil] firstObject];//不能加xib后綴
}
//3.把模型數(shù)據(jù)設(shè)置給單元格
cell.name.text = model.name;
cell.price.text = [NSString stringWithFormat:@"¥%@", model.price];
cell.icon.image = [UIImage imageNamed: model.icon];
...賦值,略
//4.返回單元格
return cell;
}
在控制器中直接為cell重的每個子控件賦值數(shù)據(jù)造成的問題:
1.控制器強依賴于cell,一旦cell內(nèi)部子控件發(fā)生了變化,那么控制器中的代碼也得改(緊耦合)。控制器完全依賴于單元格里面的屬性。
2.cell的封裝不夠完整,凡是用到cell的地方,每次都要編寫為cell的子控件依次賦值的語句,比如:cell.xxx = model.xxx。如果有10個控制器,每個控制器里都需要用到單元格進行賦值,如果一個單元格里有10個子控件,那么上面這樣的代碼就要寫10次。
對自定義cell進行封裝,把模型數(shù)據(jù)設(shè)置給單元格,形如:cell.goods = model;由cell對象內(nèi)部自己來解析模型數(shù)據(jù),并把數(shù)據(jù)設(shè)置到對應(yīng)的子控件中。在cell中創(chuàng)建一個模型類型的屬性,重寫該屬性的set方法,在set方法中將數(shù)據(jù)賦值給控件。
ProductCell.h
#import "Product.h"
@interface ProductCell : UITableViewCell
@property (nonatomic , strong) Product *goods;
//封裝一個創(chuàng)建自定義cell的方法
+ (instancetype)productCellWithTableView:(UITableView *)tableView;
@end
ProductCell.m
//重寫set方法
- (void)setGoods:(Product *)goods{
_goods =goods;
self.name.text = goods.name;
self.price.text = [NSString stringWithFormat:@"¥%@",goods.price];
self.icon.image = [UIImage imageNamed:goods.icon];
self.location.text = goods.location;
self.count.text = [NSString stringWithFormat:@"最低批發(fā)量:%@",goods.count];
}
+ (instancetype)productCellWithTableView:(UITableView *)tableView{
static NSString *ID = @"goods_cell";
ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[[NSBundle mainBundle]loadNibNamed:@"ProductCell" owner:nil options:nil] firstObject];//不能加xib后綴
}
return cell;
}
修改后的數(shù)據(jù)源方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Product *model = self.goods[indexPath.row];
ProductCell *cell = [ProductCell productCellWithTableView:tableView];
cell.goods = model;
return cell;
}
注意:要設(shè)置tableView.rowHeight = xib中的cell的高度。不然會報警告
2.純代碼方式創(chuàng)建(frameLayout,自適應(yīng)高度)
每個cell顯示的內(nèi)容不固定,cell的高度需要根據(jù)內(nèi)容的多少自適應(yīng)高度(例如微博,朋友圈):
iOS開發(fā)UI篇—使用純代碼自定義UItableviewcell實現(xiàn)一個簡單的微博界面布局
使用純代碼自定義一個tableview的步驟
1.新建一個繼承自UITableViewCell的類
2.重寫initWithStyle:reuseIdentifier:方法
添加所有需要顯示的子控件(不需要設(shè)置子控件的數(shù)據(jù)和frame, 子控件要添加到contentView中)
進行子控件一次性的屬性設(shè)置(有些屬性只需要設(shè)置一次, 比如字體\固定的圖片)
3.提供2個模型
數(shù)據(jù)模型: 存放文字數(shù)據(jù)\圖片數(shù)據(jù)
frame模型: 存放數(shù)據(jù)模型\所有子控件的frame\cell的高度
4.cell擁有一個frame模型(不要直接擁有數(shù)據(jù)模型)
5.重寫frame模型屬性的setter方法: 在這個方法中設(shè)置子控件的顯示數(shù)據(jù)和frame
6.frame模型數(shù)據(jù)的初始化已經(jīng)采取懶加載的方式(每一個cell對應(yīng)的frame模型數(shù)據(jù)只加載一次)
原文例子里自定義cell自適應(yīng)高度是通過加減乘除運算來計算出來的,沒有使用autolayout
結(jié)合鏈接原文里面的例子,講一下自己的個人理解。
步驟2:NJWeiboCell.m文件,在重寫的
initWithStyle:reuseIdentifier:
方法里創(chuàng)建并添加子控件到contentView上,注意創(chuàng)建的子控件屬性要聲明為weak。另外,像微博vip皇冠圖標這種固定的內(nèi)容的控件,進行一次性數(shù)據(jù)設(shè)置即可。步驟3、4:除了數(shù)據(jù)模型(NJWeibo.m)以外還需要frame模型,自定義cell持有frame模型,frame模型持有數(shù)據(jù)模型。
為什么還需要frame模型?如果沒有frame模型,那么自定義cell中直接持有數(shù)據(jù)模型,即- (void)setWeiboFrame:(NJWeiboFrame *)weiboFrame
替換為- (void)setWeibo:(NJWeibo *)weibo
,然后在- (void)settingFrame
方法中詳細設(shè)置控件frame,得出cell的高度。每個cell的高度是隨子控件內(nèi)容而變化的。
但是在tableView中,- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
設(shè)置行高的代理方法要比- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
方法先調(diào)用。如果沒有提供frame模型,那么要直到執(zhí)行數(shù)據(jù)源方法,把模型數(shù)據(jù)設(shè)置給單元格(形如:cell.weibo = model;
)時,才能獲取得到cell的行高。
如果獨立出frame模型,就可以在frame模型中詳細設(shè)置控件frame,得出cell高度,然后在設(shè)置行高的代理方法中取出對應(yīng)的frame模型中的行高。獲取文本lable的寬和高:
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context ;
參數(shù)1是計算大小的指定范圍;如果將來計算的文字的范圍超出了指定的范圍,返回的就是指定的范圍;如果沒有超出那么返回的就是真實的范圍;不限制范圍的話設(shè)置CGSizeMake(MAXFLOAT, MAXFLOAT)就可以了。要注意的是:如果是獲取多行文本的話,在此之前需要設(shè)置文本控件的numberOfLines屬性為0。
另外,如果是獲取單行文本的size :可以用sizeWithAttributes:方法
更新:使用xib創(chuàng)建自適應(yīng)高度的tableViewCell
UITableViewCell高度自適應(yīng)探索這篇文章寫得挺細致的,沒有其他要補充,大致上和用代碼創(chuàng)建自適應(yīng)高度cell的實現(xiàn)原理上是一樣的。比起使用frameLayout創(chuàng)建cell要簡單一點,不需要另外設(shè)置frame模型來存放所有子控件的frame、cell的高度。
現(xiàn)在有個第三方框架可以很方便地創(chuàng)建高度自適應(yīng)的tableView cell:
優(yōu)化UITableViewCell高度計算的那些事
UITableView+FDTemplateLayoutCell 源碼閱讀