iOS開發-寫給萌新們的UITableView的Cell高度自適應方法
Emmmm…. 時間過得好快,一晃眼一年就過去了,開啟塵封已久的CSDN部落格,發現自己墮落了無數時光,但人活著總是要混口飯吃的嘛,所以再度起航,鍵盤敲起來,文章寫起來。希望大家能支援我這個垃圾猿,好啦切入正題。
皮一下很開心
上週在技術群裡看萌新們在問UITableView的Cell怎麼自適應寬高(xib和storyboard的我就不說了,關東昇老哥的書裡全都是),有裝大佬的人就給他們甩一臉名詞過去—-“動態計算高度”,萌新們頓時“哇”,“大佬求帶”,“大佬求教” and so on,看得我一臉黑線,我當時就去網上搜所謂的“動態計算高度”,其實然並卵,很多都是互相抄襲、標題黨、說的玄乎罷了。
然而事實上,其實都是一樣獲取到了需要計算的文字、圖片等資訊,來進行計算,為了提高關注度,加上一個所謂的“動態計算”。而且互相抄襲的實質就是很多人把註釋和關鍵屬性、方法的使用方式隱藏了或者抹掉了,這樣讓那些萌新們覺得:“這就是大佬啊!”—呵呵
進入正題
今天介紹的是UITableview的Cell高度計算方法,這裡沒有高階的說辭,大佬勿看,萌新請進,不喜勿噴,謝謝合作~
1.首先咱們先來建立一個UITableview
- (void)CreateUI{
UITableView * tableView = [[UITableView alloc]initWithFrame:CGRectMake(0 , 20, self.view.frame.size.width, self.view.frame.size.height - 20)];
tableView.backgroundColor = [UIColor whiteColor];
tableView.dataSource = self;
tableView.delegate = self;
tableView.separatorColor = [UIColor clearColor];
[self.view addSubview:tableView];
}
2.建立資料來源,我們假設資料來源為【文字+圖片】,以一種很簡單的方式呈現出來。
(資料來源在本地,有8張圖片,文字如以下方法裡面的文字)
- (NSMutableArray *)CreateData{
// 資料來源
NSMutableArray * array = [[NSMutableArray alloc]init];
// 在獲取資料來源的時候,一般都會的到圖片的寬高,如果沒有,我們就用CGSize來獲取
for (int i = 0; i < 8; i++) {
NSMutableDictionary * dic = [[NSMutableDictionary alloc]init];
CGSize size = [UIImage imageNamed:[NSString stringWithFormat:@"%d.jpg", i + 1]].size;
// 獲取圖片寬高,根據寬高比例計算新的寬高
CGFloat imgheight = [self getImagWidth:size.width imageHeight:size.height];
// 根據label寬度、文字字數、文字大小計算新的寬高
CGFloat labheight = [self getWidth:self.view.frame.size.width title:@"按實際佛is南方路口is女離開誰知道呢是;你溫柔看;否那我可;老婦女卡洛斯的女警卡拉水泥大V看劉女士的烏克蘭那我可老婦女為快樂風男考慮對方是你的快樂發是哪裡看的真是女分類看是否離開 薩瓦迪卡嫁女記初中生的的女離開SZN SKNJD距中山東路東南角WE;失地農民了;你WAE法芙娜看來是在哪讀離開WWNSE弗蘭克SND克魯蘇NDvjklnwaKlfnawkerljnslkvcn只是看了你就開了瓦恩積分klwaNJE lknkldvnklszdnvkldzasn klandgk lnazelkr " font:[UIFont systemFontOfSize:15]];
// 寬度的存入,需要存入目標tableview的cell的寬度
[dic setValue:[NSString stringWithFormat:@"%f",self.view.frame.size.width] forKey:@"cellwidth"];
// 圖片、文字高度
[dic setValue:[NSString stringWithFormat:@"%f",imgheight] forKey:@"imageheight"];
[dic setValue:[NSString stringWithFormat:@"%f",labheight] forKey:@"labheight"];
// cell總高度
[dic setValue:[NSString stringWithFormat:@"%f",labheight + imgheight] forKey:@"cellheight"];
/*
一般在cell高度在呼叫之前就要計算好cell的整體高度,所以同文章裡所說的
什麼“動態計算”,聽著都是忽悠的,很多都是互相抄襲、標題黨、說的玄乎罷了
*/
// 圖片名字
[dic setValue:[NSString stringWithFormat:@"%d.jpg", i + 1] forKey:@"imageName"];
// 文字
[dic setValue:@"按實際佛is南方路口is女離開誰知道呢是;你溫柔看;否那我可;老婦女卡洛斯的女警卡拉水泥大V看劉女士的烏克蘭那我可老婦女為快樂風男考慮對方是你的快樂發是哪裡看的真是女分類看是否離開 薩瓦迪卡嫁女記初中生的的女離開SZN SKNJD距中山東路東南角WE;失地農民了;你WAE法芙娜看來是在哪讀離開WWNSE弗蘭克SND克魯蘇NDvjklnwaKlfnawkerljnslkvcn只是看了你就開了瓦恩積分klwaNJE lknkldvnklszdnvkldzasn klandgk lnazelkr " forKey:@"info"];
// 存入資料來源
[array addObject:dic];
}
return array;
}
- 很多人都在資料來源這一步封裝成model,交給model去處理計算寬高、資料儲存,我這篇文章只是用來給萌新們解坑的,以最基本、最簡單粗暴的方式來讓讀者懂得如何使用這些東西。
- 之前親身體會去騰訊面試的過程,面試官在最後說的很清楚,“tableview什麼動態計算都是花裡胡哨的封裝方法和簡化步驟,並沒有那麼神奇”,很受用(騰訊面試基本都會問tableview的9種優化方式)。
- Tips:這裡需要提醒一下大家,因為Cell上面可能有各種控制元件,我們需要在資料來源獲取的時候就完成Cell完整高度的計算,那樣才能保證我們在TableView建立時候不會出現Cell錯位的情況。
看到這裡,大家會問具體怎麼計算呢?不要著急,慢慢道來。我們一般在使用UITableview的時候絕大多數都是作為資料展示的,所以我們計算的目標物件就是:文字、圖片。
3.UILabel 和 UIImageView 的高度計算
// 計算label高度
- (CGFloat)getWidth:(CGFloat)width title:(NSString *)title font:(UIFont *)font
{
// 建立一個label物件,給出目標label的寬度
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width, 0)];
// 獲得文字
label.text = title;
// 獲得字型大小
label.font = font;
// 自適應
label.numberOfLines = 0;
[label sizeToFit];
// 經過自適應之後的label,已經有新的高度
CGFloat height = label.frame.size.height;
// 返回高度
return height;
}
// 計算image高度
- (CGFloat)getImagWidth:(CGFloat)width imageHeight:(CGFloat)height
{
// 獲得寬高比
CGFloat scale = width / height;
// 當前tableview或者cell的寬度 除以 比例 = 高度
CGFloat newHeight = self.view.frame.size.width/* 當前寬度 */ / scale/*比例*/;
// 返回新的高度
return newHeight;
}
- 計算高度所需要的元素:
UILabel的高度是需要三個要素 | UIImageView的高度需要三個要素 |
---|---|
目標寬度 | 圖片本身的大小 |
文字內容 | 圖片的目標寬度 |
字型大小 | 圖片寬高比列 |
這樣我們就可以計算好目標高度,然後儲存到資料來源裡面,在之後所需要的地方再取出來。
好啦,那現在我們建立好了TableView和資料來源,然後該做什麼呢?實現TableView的協議:
4. UITableView協議
這裡呢?老套路,返回1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
這裡肯定是返回咱們的資料來源的個數啦(資料來源結構 Array -> Dictionary)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return dataArray.count;
}
重要的就是在這裡,我們把計算好的Cell高度,在這個方法裡面返回
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return [[[dataArray objectAtIndex:indexPath.row]objectForKey:@"cellheight"]floatValue];
}
到了另一個重要的地方,複用池,我們需要在這裡把所存的資料塞進Cell裡面,怎麼放置呢?我們建立自定義的Cell然後寫一個get方法,在這裡一一對應的獲取資料
- Tips:一般不要在複用池內建立UI類的東西,會影響Tableview的效能
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * cellID = @"Cell";
TableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[TableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
}
if (indexPath.row < dataArray.count) {
[cell getImageName:[[dataArray objectAtIndex:indexPath.row]objectForKey:@"imageName"] getLabelInfo:[[dataArray objectAtIndex:indexPath.row]objectForKey:@"info"] getImageHeight:[[dataArray objectAtIndex:indexPath.row]objectForKey:@"imageheight"] getLabelHeight:[[dataArray objectAtIndex:indexPath.row]objectForKey:@"labheight"]];
return cell;
}
return nil;
}
好啦,外部已經看到了怎麼實現的,我們再來看看自定義的UITableviewCell內部怎麼實現的
5. 自定義Cell
在TableViewCell.h檔案裡面,定義好我們需要的控制元件和用於接收資料的成員變數
@interface TableViewCell : UITableViewCell{
CGFloat imageHeight;
CGFloat labelHeight;
}
// 屬性名
@property (nonatomic, strong) UIImageView * cellImageView;
// 屬性型別
@property (nonatomic, strong) UILabel * cellLabel;
- (void)getImageName:(NSString *)name getLabelInfo:(NSString *)info getImageHeight:(NSString *)imageheight getLabelHeight:(NSString *)labelheight;
@end
讓我們來TableViewCell.m檔案裡面實際的看看我們是如何建立的:
// 初始化方法
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.cellLabel = [[UILabel alloc]initWithFrame:CGRectZero];
self.cellImageView = [[UIImageView alloc]initWithFrame:CGRectZero];
[self.contentView addSubview:self.cellLabel];
[self.contentView addSubview:self.cellImageView];
}
return self;
}
// get方法
- (void)getImageName:(NSString *)name getLabelInfo:(NSString *)info getImageHeight:(NSString *)imageheight getLabelHeight:(NSString *)labelheight{
[self.cellImageView setImage:[UIImage imageNamed:name]];
self.cellLabel.text = info;
self.cellLabel.numberOfLines = 0;
[self.cellLabel sizeToFit];
imageHeight = [imageheight floatValue];
labelHeight = [labelheight floatValue];
}
// 檢視方法
- (void)layoutSubviews{
[super layoutSubviews];
// 文字
self.cellLabel.frame = CGRectMake(0, 0, self.contentView.frame.size.width, labelHeight);
self.cellLabel.textColor = [UIColor blackColor];
self.cellLabel.font = [UIFont systemFontOfSize:15];
// 圖片
self.cellImageView.frame = CGRectMake(0, labelHeight, self.contentView.frame.size.width, imageHeight);
}
Tips:在執行init方法的時候我們建立檢視,並把檢視新增到Cell的contentView上面去,因為Cell的self和self.contentView所承載的業務不同,我們就按照官方指引新增檢視就在contentView。
Tips:需要知道的是,在這個檔案裡面所寫的方法都是按照順序來排列的,get方法會在layoutSubviews方法之前執行,所以我們需要在get方法裡面獲取資料,並填充,但是!切記不要在get方法裡面執行對佈局的操作,因為UITableView有一個遺留問題就是,Cell在初始化的時候,沒有經過layoutSubViews這個方法之前的大小都是固定的,其詳細的值是寬:320,高:44。所以我們才需要建立成員變數在get的時候獲取frame、寬、高,然後在layoutSubViews方法裡面對檢視做佈局操作。
好啦,收尾啦