用UICollectionViewFlowLayout類來管理collectionView中的布局, 它是一個具體的類, 可以直接使用. flow layout實現(xiàn)了一種基于線性的斷裂式布局, 也就是說collectionView中的item是按照線性順序排列的, 該布局會盡量適配item的間距, 使得item排列在同一行, 如果超出一行會接著進行下一行的排列. 圖3-1展示了水平方向上的flow layout.

使用flow layout除了可以實現(xiàn)一個網(wǎng)格狀的collectionView, 還可以實現(xiàn)更多樣式. 比如, 你可以調(diào)整間距來實現(xiàn)一個單行滾動的collectionView. 如果item的size可以不同, 這樣可以產(chǎn)生非對稱效果. 你可以通過代碼和xib來配置你的flow layout對象, 步驟如下:
- 創(chuàng)建一個flow layout對象, 將其賦值給collectionView
- 設置cell的width和height
- 設置行間距, item的間距
- 如果你想要section header和footer, 給footer和header設置size
- 設置flow layout滾動的方向(scroll layout)
注意:配置layout時, 你至少需要配置cell的寬高, 不然item默認的寬高為0, 導致item不可見
自定義Flow Layout Attributes
flow layout對象暴露了幾個屬性用于配置item的外觀, 當你設置這些屬性時, 該屬性對所有的item都有起作用. 比如你通過屬性itemSize來配置item的大小后, 所有的item大小都和屬性中配置的一樣.
如果你想item的size和spacing都不一樣, 那么你需要實現(xiàn)flow layout的委托UICollectionViewDelegateFlowLayout. 你可以將collectionView的delegate和flow layout的delegate設置成同一個. 當flow layout的delegate實現(xiàn)了相應的方法后, layout對象根據(jù)你實現(xiàn)的方法中返回的值來設置item的size和spacing.
設置item的size
- 如果collectionView中的cell大小相同, 那么flow layout中的item的size大小可以通過
itemSize來確定. - 如果item的size要不同, 那么需要實現(xiàn)委托方法
collectionView:layout:sizeForItemAtIndexPath:來確定不同item的size. 在委托方法中, 你可以根據(jù)index path來確定不同item, 從而確定特定item的size. 如圖3-2展示了不同item的不同size

注意:如果item的size是不同的, 那么會導致不同行的item的數(shù)量不同
設置行間距和itme的間距
- 使用flow layout布局時, 你可以設置item間的最小間距, 以及最小行間距. 需要注意, 是最小間距, 而不是具體的間距, 也就是說實際顯示的間距可能和你設置最小間距不同, 有可能大于最小間距.
- 在布局時, flow layout往一行中加item, 直到一行顯示不下, 才開始第二行. 如果一行剛好可以顯示整數(shù)個item沒有額外的空間, 那么此時item的間距為最小間距. 若果還有額外的空間, 那么layout對象會將item的間距均勻地調(diào)大以剛好適配屏幕. 如圖3-3所展示調(diào)整itme的間距來避免最后留下大塊空間

-
對于行間距, flow layout使用同樣原理來管理. 而且當item的size不同時, 導致行間距也不同. 行間距的實際距離和相鄰兩行中最大的兩個item有關, 如圖3-4所示
圖3-4 不同itemSize時的行間距 flow layout提供兩個屬性
minimumLineSpacing和minimumInterItemSpacing來設置靜態(tài)的行間距和item間距, 如果想動態(tài)提供行間距和item間距可以使用委托方法collectionView:layout:minimumLineSpacingForSectionAtIndex:和collectionView:layout:minimumInteritemSpacingForSectionAtIndex:來提供動態(tài)的間距.
使用Section Insets來調(diào)節(jié)section的margin
section inset是調(diào)節(jié)cells使用空間的一種手段. 你可以在section的左右, header/footer上下之間插入間距達到調(diào)節(jié)cell布局的空間大小. 如圖3-5所示, 展示section inset的作用.

什么時候使用子類化的Flow Layout
在有的情況下, 使用UICollectionViewFlowLayout滿足不了需求, 需要繼承UICollectionViewFlowLayout做一些定制化的操作才行. 下面列舉了使用子類化UICollectionViewFlowLayout的幾種情況:
-
給你的布局添加新的補充視圖(supplementary)和裝飾視圖(decoration)
標準的flow layout只提供了header, footer沒有decoration view, 為了支持更多的supplementary和decoration view你需要重寫下面的一些方法:
-
layoutAttributesForElementsInRect:(required) -
layoutAttributesForItemAtIndexPath:(required) -
layoutAttributesForSupplementaryViewOfKind:atIndexPath:(為了支持新的supplementary視圖) -
layoutAttributesForDecorationViewOfKind:atIndexPath:(為了支持新的decoration視圖)
在layoutAttributesForElementsInRect:方法中, 需要調(diào)用父類方法來獲得cell的layout attribute對象, 然后再添加supplementary或者decoration的layout attribute對象. 再使用其他方法來提供其他attribute. 關于如何創(chuàng)建layout attribute請看Creating Layout Attributes
-
-
給flow layout返回的layout attribute對象做點調(diào)整
重寫
layoutAttributesForElementsInRect:方法以及其他返回layout attribute對象的方法. 在這些方法中你應該調(diào)用super來獲取attribute對象, 在方法中修改調(diào)用super獲取layout attribute對象, 然后返回. -
給cell和其他view替換新的layout attribute對象
- 通過集成
UICollectionViewLayoutAttributes類, 自定義layout attribute對象, 你可以往layout attribute中添加新的特性(屬性). - 然后在
UICollectionViewFlowLayout的子類重寫layoutAttributesClass方法返回你自定義的UICollectionViewLayoutAttributes的子類. - 你還應該重寫
layoutAttributesForElementsInRect:,layoutAttributesForItemAtIndexPath:方法, 在這些方法中你要給新的layout attribute對象中屬性賦值.
- 通過集成
-
插入/刪除的item, 確定item的initial/final位置
默認情況下, 當你插入/刪除cell時, 只有一個fade動畫, 如果你想要自定義動畫, 可以重寫下面的方法:
initialLayoutAttributesForAppearingItemAtIndexPath:initialLayoutAttributesForAppearingSupplementaryElementOfKind:atIndexPath:initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:finalLayoutAttributesForDisappearingItemAtIndexPath:finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:-
finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:
在cell被插入/刪除前, 你可以通過上面的方法來設置特定的layout attribute對象, 然后flow layout會使用這些attribute來進行動畫. 如果你重寫了這些方法, Apple推薦你也重寫prepareForCollectionViewUpdates:和finalizeCollectionViewUpdates這兩個方法. 你可以使用者兩個方法來跟蹤當前周期內(nèi)那個item被插入/刪除. 想知道關于刪除/插入的工作原理, 請看Making Insertion and Deletion Animations More Interesting
上面的幾種情況和操作也適用于創(chuàng)建自定義的layout, 不過在自定義layout之前考慮一下是否可以使用子類化的flow layout, 如果可以的話, 建議優(yōu)先使用flow layout的子類, 因為flow layout是經(jīng)過蘋果精心設計的, 效果更好. 事實上, 需要用到自定義layout的情況很少, 因為使用flow layout或繼承flow layout可以滿足大多數(shù)需求. 只有少數(shù)情況下才需要自定義layout, 比如, 你的布局不是基于線性的, 或者你的內(nèi)容不局限在屏幕兩個方向上滾動, 你布局中的item經(jīng)常移動, 在這些情況下用自定義layout更好點.
