今天在看iphone開發(fā)秘籍的時(shí)候,遇到這個(gè)問題,就仔細(xì)的深入了一下,通過測(cè)試,獲取了一些自認(rèn)為還不錯(cuò)的結(jié)論,希望對(duì)大家在cell復(fù)用方面遇到的一些問題會(huì)有所幫助。
本篇文章只講原理,對(duì)于如果對(duì)cell做界面,不深入講述。鑒于我的表達(dá)能力有限,可能會(huì)有我自己清楚,但是卻說不清楚的地方,如有問題,留言給我。
UITableView在界面的編程用的甚多,iphone開發(fā)也三月有余了,每次用到cellForRowAtIndexPath的委托方法的時(shí)候,都是直接copy代碼,自己略加一些界面的修改,對(duì)于cell的標(biāo)示符都是static NSString* identifier = @"cell";然后調(diào)用dequeueReusableCellWithIdentifier方法獲取cell,如果cell為空,再調(diào)用[[[UITableViewCellalloc]initWithStyle方法新創(chuàng)建一個(gè),根本沒有考慮過更深一些的東西。為了講解清楚,現(xiàn)放上一段代碼,代碼copy自iphone開發(fā)秘籍,本人為了講解,略加修改。以下所有講解均依照此代碼進(jìn)行,因此,如果您希望能夠透徹的了解cell的復(fù)用機(jī)制,建議實(shí)際運(yùn)行以下,跟著講解,查看效果。代碼只有tableVIew的委托方法,因此您需要自己創(chuàng)建工程,把這些委托方法加進(jìn)去。
上文一共四個(gè)委托方法,表明tableView一個(gè)section,有32行,高度為58.關(guān)于tableView的高度那個(gè)委托方法的返回的高度值,建議最好自己運(yùn)行程序查看以下,最終達(dá)到一頁的界面顯示8個(gè)cell,第8個(gè)cell顯示一半也行,但是不能顯示9和7個(gè)cell。我這里之所以為58,由于(480 - 44)/ 58 為7.5 的樣子,44為navigationBar的高度,480為屏幕的高度。總之,需要達(dá)到的效果是一頁顯示7個(gè)多的cell。還有那個(gè)icon.png需要自己準(zhǔn)備了,要把它顯示出來。是不是很麻煩呀?沒辦法,誰讓我們?cè)讷@得知識(shí)呢,知識(shí)總是需要點(diǎn)功夫的。
[cpp] view plain copy
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section
{
return 32;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 58;
}
- (UITableViewCell *)tableView:(UITableView *)tView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCellStyle style;
NSString *cellType;
switch (indexPath.row % 4)
{
case 0:
style = UITableViewCellStyleDefault;
cellType = @"Default Style";
//有標(biāo)題沒有正文(沒有細(xì)節(jié)文字)。可選的圖片
break;
case 1:
style = UITableViewCellStyleSubtitle;
cellType = @"Subtitle Style";
//標(biāo)題和正文方式,上下排布??蛇x的圖片
break;
case 2:
style = UITableViewCellStyleValue1;
cellType = @"Value1 Style";
//左邊文字左對(duì)齊,右邊文字右對(duì)齊。可選的圖片
break;
case 3:
style = UITableViewCellStyleValue2;
cellType = @"Value2 Style";
//左邊文字右對(duì)齊,藍(lán)色;右邊文字左對(duì)齊,黑色。沒有圖片
break;
}
static int i = 0;
UITableViewCell *cell = [tView dequeueReusableCellWithIdentifier:cellType];
if (!cell)
{
cell = [[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease];
++i;
NSLog(@"cell created %d times", i);
}
if (indexPath.row > 3)
cell.imageView.image = [UIImage imageNamed:@"icon.png"];
cell.textLabel.text = cellType;
cell.detailTextLabel.text = @"Subtitle text";
return cell;
}
首先講解一下復(fù)用隊(duì)列:
復(fù)用隊(duì)列的元素增加:只有在cell被滑動(dòng)出界面的時(shí)候,此cell才會(huì)被加入到復(fù)用隊(duì)列中。每次在創(chuàng)建cell的時(shí)候,程序會(huì)首先通過調(diào)用dequeueReusableCellWithIdentifier:cellType方法,到復(fù)用隊(duì)列中去尋找標(biāo)示符為“cellType”的cell,如果找不到,返回nil,然后程序去通過調(diào)用[[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease]來創(chuàng)建標(biāo)示符為“cellType”的cell。
先運(yùn)行一次程序,不要滑動(dòng)cell,查看打印日志,會(huì)有打印"cell create 1 times",,,"cell create 8 times",一共有8個(gè)日志,表明cell創(chuàng)建了8次,這個(gè)日志打印是在cellForRowAtIndexPath中的創(chuàng)建cell的時(shí)候打印的。然后慢慢向下(向下的意思,實(shí)際上是手向上滑動(dòng),讓界面顯示下一個(gè)cell,向上與之相反)滑動(dòng)cell,到顯示第九個(gè)cell的時(shí)候,會(huì)看到打印一條日志"cell create 9 times",然后繼續(xù)慢慢向下滑動(dòng),會(huì)有"cell create 10 times",直到滑動(dòng)到第12個(gè)cell以后,cell被創(chuàng)建了12個(gè)之后,以后再怎么滑動(dòng),cell都不會(huì)被創(chuàng)建了。也就是說本tableView要完整的工作,一共創(chuàng)建了12個(gè)cell。
開始解釋原因了:
第一頁的界面一共需要展示8個(gè)cell,故而cell需要?jiǎng)?chuàng)建8次,每一個(gè)cell負(fù)責(zé)自己的數(shù)據(jù)顯示。
此8個(gè)創(chuàng)建以后,復(fù)用隊(duì)列依然為空(因?yàn)槟愦藭r(shí)還沒有滑動(dòng)cell呢,復(fù)用隊(duì)列的元素不會(huì)增加)。
注解
- 首先根據(jù)屏幕的高度和cell的高度計(jì)算出,在屏幕上可見的cell的個(gè)數(shù)。在內(nèi)存中創(chuàng)建可見個(gè)數(shù)的cell的內(nèi)存空間。根據(jù)以上計(jì)算得出的cell的個(gè)數(shù)為8
- 必須明白一點(diǎn),8個(gè)cell創(chuàng)建后,當(dāng)你滾動(dòng)tableView,顯示第9個(gè)cell后,此時(shí)的復(fù)用隊(duì)列依然是空的,因?yàn)?,第一個(gè)cell還處在屏幕的可見范圍內(nèi),當(dāng)?shù)谝粋€(gè)cell徹底從屏幕消失的時(shí)候,復(fù)用隊(duì)列中才添加了第一個(gè)從屏幕消失的cell
然后在向下滑動(dòng)顯示出第9個(gè)cell的時(shí)候,還會(huì)調(diào)用cellForRowAtIndexPath方法,在此方法中,它首先到可復(fù)用隊(duì)列中去找,由于此時(shí)隊(duì)列為空,它創(chuàng)建了一個(gè)cell,打印日志,同時(shí)當(dāng)?shù)?個(gè)cell滑動(dòng)出界面之外,第一個(gè)cell進(jìn)入到復(fù)用隊(duì)列中,隊(duì)列中有一個(gè)元素,此元素的表示符為@"Default Style"。然后繼續(xù)向下滑動(dòng)cell,開始顯示第10個(gè)cell,它同樣到復(fù)用隊(duì)列中去找,第10個(gè)cell的標(biāo)示符為@"Subtitle Style",但是隊(duì)列中唯一的cell的標(biāo)示符為@"Default Style",根據(jù)標(biāo)示符尋找,沒有找到,故而再次創(chuàng)建一個(gè)新的cell,同時(shí)將滑動(dòng)出界面的第2個(gè)cell進(jìn)入復(fù)用隊(duì)列。此時(shí)復(fù)用隊(duì)列有兩個(gè)元素,標(biāo)示符為@"Default Style",@"Subtitle Style"。同樣的道理,滑動(dòng)到第11個(gè)cell的時(shí)候,第3個(gè)cell入隊(duì),第12個(gè)cell的時(shí)候,第4個(gè)cell入隊(duì)。此時(shí)復(fù)用隊(duì)列的元素個(gè)數(shù)為四個(gè),標(biāo)示符分別為:@"Default Style",@"Subtitle Style",@"Value1 Style",@"Value2 Style".然后繼續(xù)滑動(dòng)cell,當(dāng)滑動(dòng)到第13個(gè)cell的時(shí)候,它的標(biāo)示符為@"Default Style",它到復(fù)用隊(duì)列中去找,可以找到。故而,這個(gè)cell就不會(huì)被創(chuàng)建了,同理,再次向下滑動(dòng),以下的所有cell都可以根據(jù)標(biāo)示符找到對(duì)應(yīng)的cell,不會(huì)有被創(chuàng)建的。在向下滑動(dòng)的過程中,滑動(dòng)出去的cell會(huì)被入隊(duì),不過只入隊(duì)創(chuàng)建了的cell,也就是說最終隊(duì)列中會(huì)有12個(gè)cell。對(duì)于每次取對(duì)應(yīng)標(biāo)示符的元素,到底取的是哪一個(gè)?采用的什么策略?這個(gè)我不知道,我通過打印查看的是在向下滑動(dòng)的過程中,一直順著隊(duì)列找,隊(duì)列是一個(gè)循環(huán)隊(duì)列。向上滑動(dòng)的時(shí)候,逆著隊(duì)列向上找,由于是循環(huán)隊(duì)列,一直在轉(zhuǎn)圈。
如果你仔細(xì)看的話,你會(huì)發(fā)現(xiàn)第一個(gè)cell本來沒有圖片,為什么一劃下去再次滑動(dòng)(如果滑動(dòng)的距離遠(yuǎn),一次搞定,如果滑動(dòng)的距離近,多上下滑動(dòng)幾次)上來又有圖片了呢?這個(gè)就要思考一個(gè):第一個(gè)cell在滑出界面又劃入界面的時(shí)候,是從復(fù)用隊(duì)列拿到的。復(fù)用隊(duì)列有12個(gè)元素,第1,5,9還第一個(gè)cell有相同的標(biāo)示符,第1個(gè)沒有圖片,第5個(gè)和第9個(gè)有圖片。當(dāng)用戶復(fù)用的是第一個(gè)cell的時(shí)候,它是沒有圖片,當(dāng)用戶復(fù)用第5,9個(gè)cell的時(shí)候,它是有圖片的。因此,當(dāng)你上下滑動(dòng),查看第一個(gè)cell的時(shí)候,你會(huì)看到它一會(huì)有圖片,一會(huì)沒有圖片。這個(gè)跟復(fù)用時(shí)候的隊(duì)列查找規(guī)則有關(guān)。
實(shí)用篇:
說了那么多,全是關(guān)于原理的。現(xiàn)在說點(diǎn)實(shí)用的。如果你想在所有的cell中添加一個(gè)按鈕,你是應(yīng)該在if中添加,還是應(yīng)該在if之外添加呢?毫無疑問,應(yīng)該在if中,如果你是在if的外面添加的,那會(huì)導(dǎo)致,你在向下滑動(dòng)cell的過程中,取出來的cell本來已經(jīng)帶有button了,而你還在addSubview,按鈕越來越多?;蛘吣憧梢圆捎迷趇f外面添加,前提是每次先cell remove掉其所有的子視圖。這樣太消耗cpu,麻煩了。如果你想一行隔著一行有按鈕和沒有按鈕,你該怎么做呢?稍微思考一下,這個(gè)可是兩種風(fēng)格的cell,故而在滑出界面進(jìn)行重用的時(shí)候,它們應(yīng)該屬于不同的標(biāo)示符。于是你在創(chuàng)建cell的時(shí)候,應(yīng)該去指定兩種標(biāo)示符,創(chuàng)建兩種cell。當(dāng)然,也許你聰明了,我是不是可以在if之外先remove掉cell的所有子視圖,然后根據(jù)row % 2 == 0或者!=0 來進(jìn)行addSubView:button嗎?答案當(dāng)然是肯定的,但是這樣還是同樣的問題,太消耗cpu了。平常我們給每一個(gè)cell添加了一個(gè)button,肯定要添加事件的。對(duì)于不同的button,響應(yīng)不同的事件?那么我是不是通過在if語句中給button設(shè)置它的tag標(biāo)記(例如button.tag = indexpath.row)來實(shí)現(xiàn)呢?哈哈,你應(yīng)該足夠聰明了吧,當(dāng)然不行。你可以這樣想,if語句是用來創(chuàng)建的,它只被執(zhí)行了(例如上面的例子:12次),但是你可能有幾百行的cell,當(dāng)然你的tag也就只有12個(gè)了,明顯不對(duì)應(yīng)。像這樣的,應(yīng)該怎么處理呢?答案是:放在if的外面。你在if外面設(shè)置了tag標(biāo)記,當(dāng)然,在某一個(gè)具體的時(shí)間點(diǎn)上,仍然只有12個(gè)標(biāo)記,但是這12個(gè)標(biāo)記是可變的,例如當(dāng)前界面顯示第100-111號(hào)的cell,那么此時(shí)的button的tag就會(huì)是100-111了,仍然是12個(gè)按鈕,但是它們會(huì)根據(jù)用戶的滑動(dòng),進(jìn)行不同的tag切換,相當(dāng)于擁有了很多個(gè)按鈕。如果你沒有被我說的話給弄暈,腦袋又足夠清醒的話,
你應(yīng)該可以得出以下的結(jié)論:
對(duì)于界面的定制,放在if中比較好,一個(gè)cell中只創(chuàng)建一次;
對(duì)于數(shù)據(jù)的定制,放在if外面比較好,對(duì)于不同的cell,表示不同的內(nèi)容,雖然只有12個(gè)cell,但是cell中存放的數(shù)據(jù)我可以任意的映射。
如果你得出了這個(gè)結(jié)論,那么如果在加上textField,label等等,你應(yīng)該可以輕松搞定。不僅僅是表面上,更重要的是,你理解了原理,掌握了機(jī)制,萬變都不怕,即使有新的需求,腦袋想想,或者拿著這篇文章看看,希望能給你一些啟示。