UICollectionView(二)實現一個最簡單的UICollectionView

weixin_33860722發表於2016-03-08

UICollectionView的學習可以對比著UITableView來,因為這兩個類很相似,實現途徑也很相似。它們都是datasource和delegate設計模式的:datasource為view提供資料來源,告訴view要顯示些什麼東西以及如何顯示它們,delegate提供一些樣式的小細節以及使用者互動的相應。

  1. UICollectionViewDataSource
    numberOfSectionsInCollectionView //section的數量
    numberOfItemsInSection //每個section對應的items的數量
    cellForItemAtIndexPath //對應每個位置的item應該如何顯示
    viewForSupplementaryElementOfKind //對應每組section的Supplementary View應該如何顯示
    對於Decoration Views,提供方法並不在UICollectionViewDataSource中,而是直接UICollectionViewLayout類中的(因為它僅僅是檢視相關,而與資料無關)。

  2. 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兩個屬性表示。
  1. 關於Cell
    相對於UITableViewCell來說,UICollectionViewCell沒有這麼多花頭。首先UICollectionViewCell不存在各式各樣的預設的style,這主要是由於展示物件的性質決定的,因為UICollectionView所用來展示的物件相比UITableView來說要來得靈活,大部分情況下更偏向於影像而非文字,因此需求將會千奇百怪。因此SDK提供給我們的預設的UICollectionViewCell結構上相對比較簡單,由下至上:
  • 首先是cell本身作為容器view

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

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

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

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

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MY_CELL_ID"]; 
    if (!cell) { //如果沒有可重用的cell,那麼生成一個  
        cell = [[UITableViewCell alloc] init]; 
    } //配置cell,blablabla 
    return cell
    

而如果我們在TableView向資料來源請求資料之前使用-registerNib:forCellReuseIdentifier:方法為@“MY_CELL_ID"註冊過nib的話,就可以省下每次判斷並初始化cell的程式碼,要是在重用佇列裡沒有可用的cell的話,runtime將自動幫我們生成並初始化一個可用的cell。
這個特性很受歡迎,因此在UICollectionView中Apple繼承使用了這個特性,並且把其進行了一些擴充套件。使用以下方法進行註冊:

  • -registerClass:forCellWithReuseIdentifier:
  • -registerClass:forSupplementaryViewOfKind:withReuseIdentifier:
  • -registerNib:forCellWithReuseIdentifier:
  • -registerNib:forSupplementaryViewOfKind:withReuseIdentifier:

相比UITableView有兩個主要變化:一是加入了對某個Class的註冊,這樣即使不用提供nib而是用程式碼生成的view也可以被接受為cell了;二是不僅只是cell,Supplementary View也可以用註冊的方法繫結初始化了。在對collection view的重用ID註冊後,就可以像UITableView那樣簡單的寫cell配置了:
- (UICollectionView)collectionView:(UICollectionView)cv cellForItemAtIndexPath:(NSIndexPath*)indexPath {
MyCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@”MY_CELL_ID”]; // Configure the cell's content
cell.imageView.image = ...
return cell;
}

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

  1. UICollectionViewLayout
    終於到UICollectionView的精髓了。這也是UICollectionView和UITableView最大的不同。UICollectionViewLayout可以說是UICollectionView的大腦和中樞,它負責了將各個Cell、Supplementary View和Decoration Views進行組織,為它們設定各自的屬性,包括但不限於:
  • 位置
  • 尺寸
  • 透明度
  • 層級關係
  • 形狀
  • 等等等等…

Layout決定了UICollectionView是如何顯示在介面上的。在展示之前,一般需要生成合適的UICollectionViewLayout子類物件,並將其賦予CollectionView的collectionViewLayout屬性。
Apple為我們提供了一個最簡單可能也是最常用的預設layout物件: UICollectionViewFlowLayout。Flow Layout簡單說是一個直線對齊的layout。

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

**間隔**
可以指定item之間的間隔和每一行之間的間隔,和size類似,有全域性屬性,也可以對每一個item和每一個section做出設定:
    @property (CGSize) minimumInteritemSpacing
    @property (CGSize) minimumLineSpacing
    -collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
    -collectionView:layout:minimumLineSpacingForSectionAtIndex:

**滾動方向**
由屬性scrollDirection確定scroll view的方向,將影響Flow Layout的基本方向和由header及footer確定的section之間的寬度
    @property (nonatomic) UICollectionViewScrollDirection scrollDirection; 

**Header和Footer尺寸**
同樣地分為全域性和部分。需要注意根據滾動方向不同,header和footer的高和寬中只有一個會起作用。垂直滾動時section間寬度為該尺寸的高,而水平滾動時為寬度起作用。
   @property (CGSize) headerReferenceSize
   @property (CGSize) footerReferenceSize
   -collectionView:layout:referenceSizeForHeaderInSection:
   -collectionView:layout:referenceSizeForFooterInSection: 

**縮排**
    @property UIEdgeInsets sectionInset; 
    -collectionView:layout:insetForSectionAtIndex:
  1. 總結
    一個UICollectionView的實現包括兩個必要部分:UICollectionViewDataSource和UICollectionViewLayout,和一個互動部分:UICollectionViewDelegate。而Apple給出的UICollectionViewFlowLayout已經是一個很強力的layout方案了。

  2. 參考資料:
    WWDC 2012 Session筆記——205 Introducing Collection Views

相關文章