iOS 自定義tableView Cell、高度自適應
1.xib方式建立
每個cell的顯示的內容都是固定的,也就是cell的高度都是相同的
載入資料
有plist檔案資料結構如下
建立資料模型
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
懶載入資料
PageFirstTableViewController.m
//用來儲存所有團購商品的資料
@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];
//字典轉模型
NSMutableArray *arrayModels = [NSMutableArray array];
for (NSDictionary *dict in arrayDict) {
Product *model = [Product goodsWithDict:dict];
[arrayModels addObject:model];
}
_goods = arrayModels;
}
return _goods;
}
實現資料來源協議
通過xib方式實現自定義cell
建立以一個.xib檔案。在xib中拖一個UITableViewCell,設定高寬。向UITableViewCell中拖子控制元件。
建立一個繼承自UITableViewCell的類ProductCell與xib檔案的cell相關聯。通過拖線的方式將cell的子控制元件拖線到ProductCell的屬性上。
ProductCell.m
@property (weak, nonatomic) IBOutlet UILabel *name;
@property (weak, nonatomic) IBOutlet UILabel *price;
@property (weak, nonatomic) IBOutlet UIImageView *icon;
....略
實現資料來源協議
PageFirstTableViewController.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.goods.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//1.獲取模型資料
Product *model = self.goods[indexPath.row];
//2.建立單元格
//通過xib建立單元格
//由於此方法呼叫十分頻繁,cell的標示宣告成靜態變數有利於效能優化
static NSString *ID = @"goods_cell"; //要在xib中設定這個id
ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
//載入xib檔案,loadNibName:方法返回的是一個陣列,因為xib中可能有不止一個控制元件
cell = [[[NSBundle mainBundle]loadNibNamed:@"ProductCell" owner:nil options:nil] firstObject];//不能加xib字尾
}
//3.把模型資料設定給單元格
cell.name.text = model.name;
cell.price.text = [NSString stringWithFormat:@"¥%@", model.price];
cell.icon.image = [UIImage imageNamed: model.icon];
...賦值,略
//4.返回單元格
return cell;
}
在控制器中直接為cell重的每個子控制元件賦值資料造成的問題:
1.控制器強依賴於cell,一旦cell內部子控制元件發生了變化,那麼控制器中的程式碼也得改(緊耦合)。控制器完全依賴於單元格里面的屬性。
2.cell的封裝不夠完整,凡是用到cell的地方,每次都要編寫為cell的子控制元件依次賦值的語句,比如:cell.xxx = model.xxx。如果有10個控制器,每個控制器裡都需要用到單元格進行賦值,如果一個單元格里有10個子控制元件,那麼上面這樣的程式碼就要寫10次。
對自定義cell進行封裝,把模型資料設定給單元格,形如:cell.goods = model;由cell物件內部自己來解析模型資料,並把資料設定到對應的子控制元件中。在cell中建立一個模型型別的屬性,重寫該屬性的set方法,在set方法中將資料賦值給控制元件。
ProductCell.h
#import "Product.h"
@interface ProductCell : UITableViewCell
@property (nonatomic , strong) Product *goods;
//封裝一個建立自定義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:@"最低批發量:%@",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;
}
修改後的資料來源方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Product *model = self.goods[indexPath.row];
ProductCell *cell = [ProductCell productCellWithTableView:tableView];
cell.goods = model;
return cell;
}
注意:要設定tableView.rowHeight = xib中的cell的高度。不然會報警告
2.純程式碼方式建立(frameLayout,自適應高度)
每個cell顯示的內容不固定,cell的高度需要根據內容的多少自適應高度(例如微博,朋友圈):
iOS開發UI篇—使用純程式碼自定義UItableviewcell實現一個簡單的微博介面佈局
使用純程式碼自定義一個tableview的步驟
1.新建一個繼承自UITableViewCell的類
2.重寫initWithStyle:reuseIdentifier:方法
新增所有需要顯示的子控制元件(不需要設定子控制元件的資料和frame, 子控制元件要新增到contentView中)
進行子控制元件一次性的屬性設定(有些屬性只需要設定一次, 比如字型\固定的圖片)
3.提供2個模型
資料模型: 存放文字資料\圖片資料
frame模型: 存放資料模型\所有子控制元件的frame\cell的高度
4.cell擁有一個frame模型(不要直接擁有資料模型)
5.重寫frame模型屬性的setter方法: 在這個方法中設定子控制元件的顯示資料和frame
6.frame模型資料的初始化已經採取懶載入的方式(每一個cell對應的frame模型資料只載入一次)
原文例子裡自定義cell自適應高度是通過加減乘除運算來計算出來的,沒有使用autolayout
結合連結原文裡面的例子,講一下自己的個人理解。
步驟2:NJWeiboCell.m檔案,在重寫的
initWithStyle:reuseIdentifier:
方法裡建立並新增子控制元件到contentView上,注意建立的子控制元件屬性要宣告為weak。另外,像微博vip皇冠圖示這種固定的內容的控制元件,進行一次性資料設定即可。步驟3、4:除了資料模型(NJWeibo.m)以外還需要frame模型,自定義cell持有frame模型,frame模型持有資料模型。
為什麼還需要frame模型?如果沒有frame模型,那麼自定義cell中直接持有資料模型,即- (void)setWeiboFrame:(NJWeiboFrame *)weiboFrame
替換為- (void)setWeibo:(NJWeibo *)weibo
,然後在- (void)settingFrame
方法中詳細設定控制元件frame,得出cell的高度。每個cell的高度是隨子控制元件內容而變化的。
但是在tableView中,- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
設定行高的代理方法要比- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
方法先呼叫。如果沒有提供frame模型,那麼要直到執行資料來源方法,把模型資料設定給單元格(形如:cell.weibo = model;
)時,才能獲取得到cell的行高。
如果獨立出frame模型,就可以在frame模型中詳細設定控制元件frame,得出cell高度,然後在設定行高的代理方法中取出對應的frame模型中的行高。獲取文字lable的寬和高:
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context ;
引數1是計算大小的指定範圍;如果將來計算的文字的範圍超出了指定的範圍,返回的就是指定的範圍;如果沒有超出那麼返回的就是真實的範圍;不限制範圍的話設定CGSizeMake(MAXFLOAT, MAXFLOAT)就可以了。要注意的是:如果是獲取多行文字的話,在此之前需要設定文字控制元件的numberOfLines屬性為0。
另外,如果是獲取單行文字的size :可以用sizeWithAttributes:方法
更新:使用xib建立自適應高度的tableViewCell
UITableViewCell高度自適應探索這篇文章寫得挺細緻的,沒有其他要補充,大致上和用程式碼建立自適應高度cell的實現原理上是一樣的。比起使用frameLayout建立cell要簡單一點,不需要另外設定frame模型來存放所有子控制元件的frame、cell的高度。
現在有個第三方框架可以很方便地建立高度自適應的tableView cell:
優化UITableViewCell高度計算的那些事
UITableView+FDTemplateLayoutCell 原始碼閱讀
相關文章
- iOS初級開發學習筆記:一個頁面中自動計算cell的高度來自適應tableView的高度iOS筆記View
- iOS 精準獲取webView內容高度並自適應高度iOSWebView
- IOS多型別Cell的tableView實現iOS多型型別View
- textarea 高度自適應
- Swift iOS : 如果Cell內部有webview怎麼自適應呢SwiftiOSWebView
- html iframe高度自適應HTML
- iOS自定義控制元件:自定義TableView、CollectionView空資料佔點陣圖iOS控制元件View
- IOS 動態改變cell的高度iOS
- 短視訊平臺原始碼,彈性佈局實現自適應高度cell原始碼
- textarea文域高度自適應
- textarea高度自適應詳解
- 小程式Swiper高度自適應
- iframe 跨域高度自適應跨域
- jQuery textarea框高度自適應jQuery
- display:table-cell自適應佈局
- iframe自適應高度的外掛
- Widget小元件如何自適應高度元件
- 自動載入的iframe高度自適應
- textarea實現高度自適應的理解
- 微信輪播圖自適應高度
- 巢狀UITextView的UITableViewCell高度自適應巢狀UITextView
- 微信小程式Swiper高度自適應微信小程式
- easyui-layout佈局高度自適應UI
- 高度自定義的STDPickerViewView
- 真正解決iframe高度自適應問題
- React Native踩坑指南:WebView高度自適應React NativeWebView
- UITableViewCell含有WebView的自適應高度新解決方案UIWebView
- textarea文字框高度自適應程式碼例項
- 微信小程式swiper高度自適應,swiper的子元素高度不固定微信小程式
- 好程式設計師web前端分享高度自適應程式設計師Web前端
- 怎麼讓body高度自適應螢幕?為什麼?
- 前端頁面高度和寬度自適應怎麼做?前端
- CSS 圖片固定長寬比實現高度自適應CSS
- iOS 一個可高度自定義化的評分控制元件、打分、打星iOS控制元件
- 67,表格中單元格td自適應高度,最大高度後滾動條顯示
- css--常見左右盒子寬度高度自適應佈局CSS
- Iframe嵌入跨域頁面高度自適應實現詳解跨域
- 移動端:對高度自適應的輸入框說不~
- Android XML靈活佈局之 EditText實現自適應高度同時限制最小和最大高度AndroidXML