1. 程式人生 > >UICollectionViewCell重用注意要點(解決檢視重疊問題)

UICollectionViewCell重用注意要點(解決檢視重疊問題)

剛接觸√,總會遇到很多莫名其妙的困難,

最容易造成bug的是,我總以為UICollectionView和UITableView是差不多的,就先入為主基本就照抄uitableview的東西去做,以為只是把幾個資料來源方法和代理方法改個名字而已,這就是bug不斷的根源!!

第一個問題:起碼要顯示點東西吧:

    建立UICollectionViewFlowLayout並註冊! 跟uitableview第一個不同就在這裡,UICollectionView需要一個layout和為cell註冊一個類才能顯示出東西。。。。

UICollectionViewFlowLayout *flowLayout= [[

UICollectionViewFlowLayoutalloc]init];

self.myUICollectionView = [[UICollectionViewalloc]initWithFrame:CGRectMake(0,45,self.view.frame.size.width,self.view.frame.size.height-44collectionViewLayout:flowLayout];

self.myUICollectionView.backgroundColor = [UIColorwhiteColor];

    [self.myUICollectionView

registerClass:[ClassifiListCell1 class] forCellWithReuseIdentifier:@"myUICollectionViewCell"];

    self.myUICollectionView.delegate = self;

    self.myUICollectionView.dataSource = self;

    [self.viewaddSubview:self.myUICollectionView];

這樣,起碼終於可以顯示東西了,不容易。。。要注意的是,ClassifiListCell1是自定義的UICollectionViewCell;

第二個問題:重點!!cell重用時檢視重疊!!!

   資料表較多的話,當然,必須得重用cell,正常實現其他資料來源方法,最重要的是以下方法:

 NSUInteger row = [indexPathrow];

ClassifiListCell1 * cell = (ClassifiListCell1 *)[collectionViewdequeueReusableCellWithReuseIdentifier:@"myUICollectionViewCell"forIndexPath:indexPath];

   if (!cell) {

        cell = [[ClassifiListCell1alloc] initWithFrame:CGRectMake(0,0, 145, 200)];

    }

   if ([_commodity_listcount]>=row) {

       NSDictionary *dict =[_commodity_listobjectAtIndex:row];

       Classifi *merc = [[Classifialloc] init];

        [mercsetJson:dict];

        [cellsetCellContent:merc];

    }

   return cell;

}

其中[cell setCellContent:merc];是設定cell的檢視,上面有圖片和uitextfield等,如此倒弄一番,終於看到自己自定義的正常檢視了,雖然位置還沒調好,但畢竟這不是重點!!

file:///Users/maobai/Desktop/1234.png
額。。。。不能插入圖片,就算了,,,意思就是正常看到檢視了! 但當我上下滑動檢視的時候,問題來了,cell裡面的東西一層層的重疊,慘不忍睹! 在網上找了很多方法和注意的要點: 其中,註冊,自定義上面已經做了,還有一個方法是,重用cell之前,先把cell裡面的所有subviews removefromsuperview,不過這個方法很挫有木有!! 再者就是重寫init方法,在裡面新增自己的檢視,但這方法根本行不通,真實測試過,自定義的uicollectionviewcell重用時,甚至建立的時候,根本不走init方法!! 最終解決辦法:重寫initWithFrame:方法,把自定義的檢視放進去,然後在setCellContent:方法裡就可以直接填東西,再不會出現重疊的情況了 其實很簡單有木有。。。。。 目前還搞不懂,為什麼自定義的cell例項化的時候不走init方法,但會走initWithFrame:方法???

以下關於UICollectionView的使用是另一個博主的文章:(連線:http://blog.csdn.net/majiakun1/article/details/17204693)

什麼是UICollectionView

UICollectionView是一種新的資料展示方式,簡單來說可以把他理解成多列的UITableView(請一定注意這是UICollectionView的最最簡單的形式)。如果你用過iBooks的話,可能你還對書架佈局有一定印象:一個虛擬書架上放著你下載和購買的各類圖書,整齊排列。其實這就是一個UICollectionView的表現形式,或者iPadiOS6中的原生時鐘應用中的各個時鐘,也是UICollectionView的最簡單的一個佈局,如圖:

 

最簡單的UICollectionView就是一個GridView,可以以多列的方式將資料進行展示。標準的UICollectionView包含三個部分,它們都是UIView的子類:

•  Cells 用於展示內容的主體,對於不同的cell可以指定不同尺寸和不同的內容,這個稍後再說

•  Supplementary Views追加檢視如果你對UITableView比較熟悉的話,可以理解為每個SectionHeader或者Footer,用來標記每個sectionview

•  Decoration Views裝飾檢視這是每個section的背景,比如iBooks中的書架就是這個

 


實現一個簡單的UICollectionView

先從最簡單的開始,UITableViewiOS開發中的非常非常非常重要的一個類,相信如果你是開發者的話應該是對這個類非常熟悉了。實現一個UICollectionView和實現一個UITableView基本沒有什麼大區別,它們都同樣是datasourcedelegate設計模式的:datasourceview提供資料來源,告訴view要顯示些什麼東西以及如何顯示它們,delegate提供一些樣式的小細節以及使用者互動的相應。因此在本節裡會大量對比collectionviewtableview來進行說明,如果您還不太熟悉tableview的話,也是個對照著複習的好機會。

UICollectionViewDataSource

•  section的數量-numberOfSectionsInCollection:

•  某個section裡有多少個item-collectionView:numberOfItemsInSection:

•  對於某個位置應該顯示什麼樣的cell-collectionView:cellForItemAtIndexPath:

實現以上三個委託方法,基本上就可以保證CollectionView工作正常了。當然,還有提供SupplementaryView的方法

•  collectionView:viewForSupplementaryElementOfKind:atIndexPath:

對於Decoration Views,提供方法並不在UICollectionViewDataSource中,而是直接在UICollectionViewLayout類中的(因為它僅僅是檢視相關,而與資料無關),放到稍後再說。

關於重用

為了得到高效的View,對於cell的重用是必須的,避免了不斷生成和銷燬物件的操作,這與在UITableView中的情況是一致的。但值得注意的時,在UICollectionView中,不僅cell可以重用,SupplementaryViewDecorationView也是可以並且應當被重用的。在iOS5中,AppleUITableView的重用做了簡化,以往要寫類似這樣的程式碼:

UITableViewCell*cell =[tableViewdequeueReusableCellWithIdentifier:@"MY_CELL_ID"];

if(!cell)//如果沒有可重用的cell,那麼生成一個 {

cell =[[UITableViewCellalloc]init];

}

//配置cellblablabla

returncell; 

而如果我們在TableView向資料來源請求資料之前使用-registerNib:forCellReuseIdentifier:方法為@“MY_CELL_ID”註冊過nib的話,就可以省下每次判斷並初始化cell的程式碼,要是在重用佇列裡沒有可用的cell的話,runtime將自動幫我們生成並初始化一個可用的cell

這個特性很受歡迎,因此在UICollectionViewApple繼承使用了這個特性,並且把其進行了一些擴充套件。使用以下方法進行註冊:

•  -registerClass:forCellWithReuseIdentifier:

•  -registerClass:forSupplementaryViewOfKind:withReuseIdentifier:

•  -registerNib:forCellWithReuseIdentifier:

•  -registerNib:forSupplementaryViewOfKind:withReuseIdentifier:

相比UITableView有兩個主要變化:一是加入了對某個Class的註冊,這樣即使不用提供nib而是用程式碼生成的view也可以被接受為cell了;二是不僅只是cellSupplementary View也可以用註冊的方法繫結初始化了。在對collectionview的重用ID註冊後,就可以像UITableView那樣簡單的寫cell配置了:

-(UICollectionView*)collectionView:(UICollectionView*)cvcellForItemAtIndexPath:(NSIndexPath*)indexPath{ MyCell*cell=[cvdequeueReusableCellWithReuseIdentifier:@MY_CELL_ID]; // Configure the cell's content cell.imageView.image=... returncell; }

需要吐槽的是,對collection view,取重用佇列的方法的名字和UITableView裡面不一樣了,在Identifier前面多加了Reuse五個字母,語義上要比以前清晰,命名規則也比以前嚴謹了..不知道Apple會不會為了追求完美而把UITableView中的命名不那麼好的方法deprecate掉。

UICollectionViewDelegate

資料無關的view的外形啊,使用者互動啊什麼的,由UICollectionViewDelegate來負責:

•  cell的高亮

•  cell的選中狀態

•  可以支援長按後的選單

關於使用者互動,UICollectionView也做了改進。每個cell現在有獨立的高亮事件和選中事件的delegate,使用者點選cell的時候,現在會按照以下流程向delegate進行詢問:

-collectionView:shouldHighlightItemAtIndexPath:是否應該高亮?

-collectionView:didHighlightItemAtIndexPath:如果1回答為是,那麼高亮

-collectionView:shouldSelectItemAtIndexPath:無論1結果如何,都詢問是否可以被選中?

-collectionView:didUnhighlightItemAtIndexPath:如果1回答為是,那麼現在取消高亮

-collectionView:didSelectItemAtIndexPath:如果3回答為是,那麼選中cell

狀態控制要比以前靈活一些,對應的高亮和選中狀態分別由highlightedselected兩個屬性表示。

關於Cell

相對於UITableViewCell來說,UICollectionViewCell沒有這麼多花頭。首先UICollectionViewCell不存在各式各樣的預設的style,這主要是由於展示物件的性質決定的,因為UICollectionView所用來展示的物件相比UITableView來說要來得靈活,大部分情況下更偏向於影象而非文字,因此需求將會千奇百怪。因此SDK提供給我們的預設的UICollectionViewCell結構上相對比較簡單,由下至上:

•  首先是cell本身作為容器view

•  然後是一個大小自動適應整個cellbackgroundView,用作cell平時的背景

•  再其上是selectedBackgroundView,是cell被選中時的背景

•  最後是一個contentView,自定義內容應被加在這個view

這次Apple給我們帶來的好康是被選中cell的自動變化,所有的cell中的子view,也包括contentView中的子view,在當cell被選中時,會自動去查詢view是否有被選中狀態下的改變。比如在contentView里加了一個normalselected指定了不同圖片的imageView,那麼選中這個cell的同時這張圖片也會從normal變成selected,而不需要額外的任何程式碼。

UICollectionViewLayout

終於到UICollectionView的精髓了這也是UICollectionViewUITableView最大的不同。UICollectionViewLayout可以說是UICollectionView的大腦和中樞,它負責了將各個cellSupplementary ViewDecoration Views進行組織,為它們設定各自的屬性,包括但不限於:

•  位置

•  尺寸

•  透明度

•  層級關係

•  形狀

•  等等等等

Layout決定了UICollectionView是如何顯示在介面上的。在展示之前,一般需要生成合適的UICollectionViewLayout子類物件,並將其賦予CollectionViewcollectionViewLayout屬性。關於詳細的自定義UICollectionViewLayout和一些細節,我將寫在之後一篇筆記中。

Apple為我們提供了一個最簡單可能也是最常用的預設layout物件,UICollectionViewFlowLayoutFlow Layout簡單說是一個直線對齊的layout,最常見的GridView形式即為一種FlowLayout配置。上面的照片架介面就是一個典型的FlowLayout

•  首先一個重要的屬性是itemSize,它定義了每一個item的大小。通過設定itemSize可以全域性地改變所有cell的尺寸,如果想要對某個cell制定尺寸,可以使用-collectionView:layout:sizeForItemAtIndexPath:方法。

•  間隔可以指定item之間的間隔和每一行之間的間隔,和size類似,有全域性屬性,也可以對每一個item和每一個section做出設定:

◦                    @property (CGSize)minimumInteritemSpacing

◦                    @property (CGSize)minimumLineSpacing

◦                    -collectionView:layout:minimumInteritemSpacingForSectionAtIndex:

◦                    -collectionView:layout:minimumLineSpacingForSectionAtIndex:

•  滾動方向由屬性scrollDirection確定scroll view的方向,將影響FlowLayout的基本方向和由headerfooter確定的section之間的寬度

◦                    UICollectionViewScrollDirectionVertical

◦                    UICollectionViewScrollDirectionHorizontal

•  HeaderFooter尺寸同樣地分為全域性和部分。需要注意根據滾動方向不同,headerfooter的高和寬中只有一個會起作用。垂直滾動時section間寬度為該尺寸的高,而水平滾動時為寬度起作用,如圖。

◦                    @property (CGSize)headerReferenceSize

◦                    @property (CGSize)footerReferenceSize

◦                    -collectionView:layout:referenceSizeForHeaderInSection:

◦                    -collectionView:layout:referenceSizeForFooterInSection:

•  縮排

◦                    @property UIEdgeInsetssectionInset;

◦                    -collectionView:layout:insetForSectionAtIndex:

總結

一個UICollectionView的實現包括兩個必要部分:UICollectionViewDataSourceUICollectionViewLayout,和一個互動部分:UICollectionViewDelegate。而Apple給出的UICollectionViewFlowLayout已經是一個很強力的layout方案了。

幾個自定義的Layout

但是光是UICollectionViewFlowLayout的話,顯然是不夠用的,而且如果單單是這樣的話,就和現有的開源各類GridView沒有區別了…UICollectionView的強大之處,就在於各種layout的自定義實現,以及它們之間的切換。先看幾個相當exiciting的例子吧~

比如,堆疊佈局:


圓形佈局:


Cover Flow佈局:


所有這些佈局都採用了同樣的資料來源和委託方法,因此完全實現了modelview的解耦。但是如果僅這樣,那開源社群也已經有很多相應的解決方案了。Apple的強大和開源社群不能比擬的地方在於對SDK的全域性掌控,CollectionView提供了非常簡單的API可以令開發者只需要一次簡單呼叫,就可以使用CoreAnimation在不同的layout之間進行動畫切換,這種切換必定將大幅增加使用者體驗,代價只是幾十行程式碼就能完成的佈局實現,以及簡單的一句API呼叫,不得不說現在所有的開原始碼與之相比,都是相形見拙了不得不佩服和感謝UIKit團隊的努力。