[轉]UItableViewCell的複用機制

轉自:http://www.2cto.com/kf/201207/144337.htmlhtml

今天在看iphone開發祕籍的時候,遇到這個問題,就仔細的深刻了一下,經過測試,獲取了一些自認爲還不錯的結論,但願對你們在cell複用方面遇到的一些問題會有所幫助。編程

本篇文章只講原理,對於若是對cell作界面,不深刻講述。鑑於個人表達能力有限,可能會有我本身清楚,可是卻說不清楚的地方,若有問題,留言給我。iphone


UITableView在界面的編程用的甚多,iphone開發也三月有餘了,每次用到cellForRowAtIndexPath的委託方法的時候,都是直接copy代碼,本身略加一些界面的修改,對於cell的標示符都是static NSString* identifier = @"cell";而後調用dequeueReusableCellWithIdentifier方法獲取cell,若是cell爲空,再調用[[[UITableViewCellalloc]initWithStyle方法新建立一個,根本沒有考慮過更深一些的東西。爲了講解清楚,現放上一段代碼,代碼copy自iphone開發祕籍,本人爲了講解,略加修改。如下全部講解均依照此代碼進行,所以,若是您但願可以透徹的瞭解cell的複用機制,建議實際運行如下,跟着講解,查看效果。代碼只有tableVIew的委託方法,所以您須要本身建立工程,把這些委託方法加進去。ide


上文一共四個委託方法,代表tableView一個section,有32行,高度爲58.關於tableView的高度那個委託方法的返回的高度值,建議最好本身運行程序查看如下,最終達到一頁的界面顯示8個cell,第8個cell顯示一半也行,可是不能顯示9和7個cell。我這裏之因此爲58,因爲(480 - 44)/ 58 爲7.5 的樣子,44爲navigationBar的高度,480爲屏幕的高度。總之,須要達到的效果是一頁顯示7個多的cell。還有那個icon.png須要本身準備了,要把它顯示出來。是否是很麻煩呀?沒辦法,誰讓咱們在得到知識呢,知識老是須要點功夫的。測試


[cpp] 
- (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"; 
            //有標題沒有正文(沒有細節文字)。可選的圖片 
            break; 
        case 1: 
            style = UITableViewCellStyleSubtitle; 
            cellType = @"Subtitle Style"; 
            //標題和正文方式,上下排布。可選的圖片 
            break; 
        case 2: 
            style = UITableViewCellStyleValue1; 
            cellType = @"Value1 Style"; 
            //左邊文字左對齊,右邊文字右對齊。可選的圖片 
            break; 
        case 3: 
            style =  UITableViewCellStyleValue2; 
            cellType =  @"Value2 Style"; 
            //左邊文字右對齊,藍色;右邊文字左對齊,黑色。沒有圖片 
            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; 
日誌


首先講解一下複用隊列:htm

複用隊列的元素增長:只有在cell被滑動出界面的時候,此cell纔會被加入到複用隊列中。每次在建立cell的時候,程序會首先經過調用dequeueReusableCellWithIdentifier:cellType方法,到複用隊列中去尋找標示符爲「cellType」的cell,若是找不到,返回nil,而後程序去經過調用[[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease]來建立標示符爲「cellType」的cell。隊列

先運行一次程序,不要滑動cell,查看打印日誌,會有打印"cell create 1 times",,,"cell create 8 times",一共有8個日誌,代表cell建立了8次,這個日誌打印是在cellForRowAtIndexPath中的建立cell的時候打印的。而後慢慢向下(向下的意思,其實是手向上滑動,讓界面顯示下一個cell,向上與之相反)滑動cell,到顯示第九個cell的時候,會看到打印一條日誌"cell create 9 times",而後繼續慢慢向下滑動,會有"cell create 10 times",直到滑動到第12個cell之後,cell被建立了12個以後,之後再怎麼滑動,cell都不會被建立了。也就是說本tableView要完整的工做,一共建立了12個cell。事件

開始解釋緣由了:圖片

第一頁的界面一共須要展現8個cell,故而cell須要建立8次,每個cell負責本身的數據顯示。此8個建立之後,複用隊列依然爲空(由於你此時尚未滑動cell呢,複用隊列的元素不會增長)。而後在向下滑動顯示出第9個cell的時候,還會調用cellForRowAtIndexPath方法,在此方法中,它首先到可複用隊列中去找,因爲此時隊列爲空,它建立了一個cell,打印日誌,同時當第1個cell滑動出界面以外,第一個cell進入到複用隊列中,隊列中有一個元素,此元素的表示符爲@"Default Style"。而後繼續向下滑動cell,開始顯示第10個cell,它一樣到複用隊列中去找,第10個cell的標示符爲@"Subtitle Style",可是隊列中惟一的cell的標示符爲@"Default Style",根據標示符尋找,沒有找到,故而再次建立一個新的cell,同時將滑動出界面的第2個cell進入複用隊列。此時複用隊列有兩個元素,標示符爲@"Default Style",@"Subtitle Style"。一樣的道理,滑動到第11個cell的時候,第3個cell入隊,第12個cell的時候,第4個cell入隊。此時複用隊列的元素個數爲四個,標示符分別爲:@"Default Style",@"Subtitle Style",@"Value1 Style",@"Value2 Style".而後繼續滑動cell,當滑動到第13個cell的時候,它的標示符爲@"Default Style",它到複用隊列中去找,能夠找到。故而,這個cell就不會被建立了,同理,再次向下滑動,如下的全部cell均可以根據標示符找到對應的cell,不會有被建立的。在向下滑動的過程當中,滑動出去的cell會被入隊,不過只入隊建立了的cell,也就是說最終隊列中會有12個cell。對於每次取對應標示符的元素,到底取的是哪個?採用的什麼策略?這個我不知道,我經過打印查看的是在向下滑動的過程當中,一直順着隊列找,隊列是一個循環隊列。向上滑動的時候,逆着隊列向上找,因爲是循環隊列,一直在轉圈。

若是你仔細看的話,你會發現第一個cell原本沒有圖片,爲何一劃下去再次滑動(若是滑動的距離遠,一次搞定,若是滑動的距離近,多上下滑動幾回)上來又有圖片了呢?這個就要思考一個:第一個cell在滑出界面又劃入界面的時候,是從複用隊列拿到的。複用隊列有12個元素,第1,5,9還第一個cell有相同的標示符,第1個沒有圖片,第5個和第9個有圖片。當用戶複用的是第一個cell的時候,它是沒有圖片,當用戶複用第5,9個cell的時候,它是有圖片的。所以,當你上下滑動,查看第一個cell的時候,你會看到它一會有圖片,一會沒有圖片。這個跟複用時候的隊列查找規則有關。


實用篇:

說了那麼多,全是關於原理的。如今說點實用的。若是你想在全部的cell中添加一個按鈕,你是應該在if中添加,仍是應該在if以外添加呢?毫無疑問,應該在if中,若是你是在if的外面添加的,那會致使,你在向下滑動cell的過程當中,取出來的cell原本已經帶有button了,而你還在addSubview,按鈕愈來愈多。或者你能夠採用在if外面添加,前提是每次先cell remove掉其全部的子視圖。這樣太消耗cpu,麻煩了。若是你想一行隔着一行有按鈕和沒有按鈕,你該怎麼作呢?稍微思考一下,這個但是兩種風格的cell,故而在滑出界面進行重用的時候,它們應該屬於不一樣的標示符。因而你在建立cell的時候,應該去指定兩種標示符,建立兩種cell。固然,也許你聰明瞭,我是否是能夠在if以外先remove掉cell的全部子視圖,而後根據row % 2 == 0或者!=0 來進行addSubView:button嗎?答案固然是確定的,可是這樣仍是一樣的問題,太消耗cpu了。日常咱們給每個cell添加了一個button,確定要添加事件的。對於不一樣的button,響應不一樣的事件?那麼我是否是經過在if語句中給button設置它的tag標記(例如button.tag = indexpath.row)來實現呢?哈哈,你應該足夠聰明瞭吧,固然不行。你能夠這樣想,if語句是用來建立的,它只被執行了(例如上面的例子:12次),可是你可能有幾百行的cell,固然你的tag也就只有12個了,明顯不對應。像這樣的,應該怎麼處理呢?答案是:放在if的外面。你在if外面設置了tag標記,固然,在某一個具體的時間點上,仍然只有12個標記,可是這12個標記是可變的,例如當前界面顯示第100-111號的cell,那麼此時的button的tag就會是100-111了,仍然是12個按鈕,可是它們會根據用戶的滑動,進行不一樣的tag切換,至關於擁有了不少個按鈕。若是你沒有被我說的話給弄暈,腦殼又足夠清醒的話,你應該能夠得出如下的結論:對於界面的定製,放在if中比較好,一個cell中只建立一次;對於數據的定製,放在if外面比較好,對於不一樣的cell,表示不一樣的內容,雖然只有12個cell,可是cell中存放的數據我能夠任意的映射。若是你得出了這個結論,那麼若是在加上textField,label等等,你應該能夠輕鬆搞定。不只僅是表面上,更重要的是,你理解了原理,掌握了機制,萬變都不怕,即便有新的需求,腦殼想一想,或者拿着這篇文章看看,但願能給你一些啓示。

相關文章
相關標籤/搜索