無論是初學者,又或者是老鳥,只要是學習ios的人都知道,TableView對於開發有多重要,然而我們在使用TableView,可能會遇到各種各樣的問題,例如今天我想要說的 TableViewCell的重用的問題:
我們都知道,在TableView返回每一行cell的數據源方法中,我們一般會通過重用cell來達到節省內存的目的:通過為每個cell指定一個重用標識符(reuseIdentifier),即指定了單元格的種類,當cell滾出屏幕時,會將滾出屏幕的單元格放入重用的緩存池中,當某個未在屏幕上的單元格要顯示的時候,就從這個緩存池中取出單元格進行重用。
但對於多變的自定義cell,有時這種重用機制會出錯。我舉個簡單的例子,看下面的兩張圖片
<1>正常顯示的圖片
<2>界面錯亂的圖片
通過比較以上兩張圖片 明顯可以看出 第二張圖片的界面顯示錯亂,原因很簡單 ,那就是在重用cell的時候出現了問題,那么我們該怎么解決界面錯亂或者其他的問題呢?下面我一一為大家來介紹:
方法1 :
將獲得cell的方法從- (UITableViewCell*)dequeueReusableCellWithIdentifier:(NSString*)identifier 換為-(UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
重用機制調用的就是dequeueReusableCellWithIdentifier這個方法,方法的意思就是“出列可重用的cell”,因而只要將它換為cellForRowAtIndexPath(只從要更新的cell的那一行取出cell),就可以不使用重用機制,因而問題就可以得到解決,雖然可能會浪費一些空間。
//下面是示例代碼:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //改為以下的方法
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; //根據indexPath准確地取出一行,而不是從cell重用隊列中取出
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}......
上面的代碼,無疑是能夠解決界面錯亂的問題,但如果數據很多,那就會浪費相當多的空間,不推薦使用。
方法2:
為每個cell指定不同的重用標識符(reuseIdentifier)來解決。
重用機制是根據相同的標識符來重用cell的,標識符不同的cell不能彼此重用。於是我們將每個cell的標識符都設置為不同,就可以避免不同cell重用的問題了。
怎么為每個cell都設置不同的標示符呢?其實很簡單 在返回每一行cell的數據源方法中 通過為每一個cell的標示符綁定indexPath.section--indexPath.row就可以了,下面是示例代碼:
//實現建立cell的具體內容
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * ID = [NSString stringWithFormat:@"cell-ld%-ld%",[indexPath section ],[indexPath row]];
//2.到緩存池中去查找可重用標示符
HMWeiboCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[HMWeiboCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
//給cell中得屬性framemodel賦值
HMWeiboFrameModel * frameModel = self.weiboFrameArray[indexPath.row];
cell.weiboFrameModel = frameModel;
return cell;
}
//上面的這種方式 雖然說比第一種方式有了一定的改進,通過為每一個cell都綁定了一個不同的標識符能夠使得cell與cell之間不會重用,解決了界面錯亂的問題,但是從根本上來說還是沒有太大的內存優化,同樣的如果數據比較多還是比較浪費空間。
方法3:
第三種方式其實很簡單就是刪除重用cell的所有子視圖,這句話什么意思呢?當我們從緩存池中取得重用的cell后,通過刪除重用的cell的所有子視圖,從而得到一個沒有特殊格式的cell,供其他cell重用。是不是還是沒懂什么意思,那我們就接着來看代碼:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; //出列可重用的cell
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
else
{
//刪除cell的所有子視圖
while ([cell.contentView.subviews lastObject] != nil)
{
[(UIView*)[cell.contentView.subviews lastObject] removeFromSuperview];//強制裝換為UIView類型 ,移除所有子視圖
}
}
return cell;
}
看完上面的代碼是不是感覺這種方式很新穎,這種方式相比前兩種方式來說 是比較完美的,既達到了重用的目的,又不會導致界面的錯亂等問題,如果你也遇到了 自定義cell的時候 界面錯亂不堪,不妨試試這種方式吧,你會覺得很神奇的...
方法4:
看完了前面三種方式,你是不是覺得就沒有后文了呢,你錯了,下面我再來說說第四種方式:
這第四種方式其實是比較笨的方法,為什么這么說呢,我們可以這么來想,既然重用cell導致界面錯亂的原因是,重用cell的時候 數據沒有更換,將重用cell的數據一起重用了,那么我們為什么想不到在給自定義cell設置數據的時候 通過判斷每一個cell該使用什么樣的數據呢 ,如果每一個cell使用的數據是不同的,那么就不會導致界面的數據或者是frame出現問題了,下面我就用簡單的的例子來為大家演示:
//如果是某一個軟件的會員 那么會員圖標的frame設置 如果不是會員那么重新設置 這樣就能夠解決會員圖標的數據和frame問題
if (weiboModel.vip) {//如果是會員
CGFloat vipX = CGRectGetMaxX(_nameF) + kMargin;
_vipF = CGRectMake(vipX, kMargin, kVipWidth, kVipWidth);
} else{
_vipF = CGRectZero;
}
再比如
//設置picture的frame 注意也要判斷是否有picture
if (weiboModel.picture.length>0) {//有圖片
CGFloat pictureY = CGRectGetMaxY(_textF) + kMargin;
_pictureF = CGRectMake(kMargin, pictureY, kPictureWidth, kPictureWidth);
//如果有圖片行高就是最大的圖片的y值加一個邊距
self.cellHeight = CGRectGetMaxY(_pictureF) + kMargin;
}else {
_pictureF = CGRectZero;
//如果沒有圖片 最大的行高就是正文的最大高度加一個高度
self.cellHeight = CGRectGetMaxY(_textF) + kMargin;
} 上面這句代碼的目的是 當有圖片的時候 給圖片設置frame 如果沒有圖片那么我們就設置frame為0,這樣就能輕松的解決frame導致的界面錯亂的問題了。
以上四種方式,有自己的拙見,也有借鑒網上各位大牛的方法,希望這篇文章能夠對大家有幫助,有什么問題可以留言,我定將知無不言,言無不盡....