iOS開發之窺探UICollectionViewController(三) :使用UICollectionView自定義瀑布流

青玉伏案發表於2015-12-30

上篇部落格的例項是自帶的UICollectionViewDelegateFlowLayout佈局基礎上來做的Demo, 詳情請看iOS開發之窺探UICollectionViewController(二) –詳解CollectionView各種回撥。UICollectionView之所以強大,是因為其具有自定義功能,這一自定義就不得了啦,自由度非常大,定製的高,所以功能也是灰常強大的。本篇部落格就不使用自帶的流式佈局了,我們要自定義一個瀑布流。自定義的瀑布流可以配置其引數: 每個Cell的邊距,共有多少列,Cell的最大以及最小高度是多少等。

一.先入為主

先來看一下不同配置引數下執行後的效果吧,每張截圖的列數和Cell之間的邊距都有所不同,瀑布流的列數依次為2,3,8。有密集恐懼證的童鞋就不要看這些執行效果圖了,真的會看暈的。下面這些執行效果就是修改不同的配置引數來進行佈局的。看圖吧,關於瀑布流的效果就不囉嗦了。以下的效果就是使用自定義佈局做的,接下來將會介紹一下其實現原理。

二. UICollectionViewLayout

在介紹上述效果實現原理之前,需要介紹一下UICollectionViewLayout。UICollectionView的自定義功能就是自己去實現UICollectionViewLayout的子類,然後重寫相應的方法來實現Cell的佈局,先介紹一下需要重寫的方法,然後再此方法上進行應用實現上述瀑布流。好,廢話少說,幹活走起。

1.佈局預載入函式

當佈局首次被載入時會呼叫prepareLayout函式,見名知意,就是預先載入佈局,在該方法中可以去初始化佈局相關的資料。該方法類似於檢視控制器的ViewDidLoad方法,稍後回用到該方法。

2.內容滾動範圍

下方是定義ContentSize的方法。該方法會返回CollectionView的大小,這個方法也是自定義佈局中必須實現的方法。說白了,就是設定ScrollView的ContentSize,即滾動區域。

3. 下方四個方法是確定佈局屬性的,下方第一個方法返回一個陣列,該陣列中存放的是為每個Cell繫結的UICollectionViewLayoutAttributes屬性,便於在下面第二個方法中去定製每個Cell的屬性。第三個方法就是根據indexPath來獲取Cell所繫結的layoutAtrributes, 然後去更改UICollectionViewLayoutAttributes物件的一些屬性並返回,第四個是為Header View或者FooterView來定製其對應的UICollectionViewLayoutAttributes,然後返回。

4.UICollectionViewLayoutAttributes

下方是UICollectionViewLayoutAttributes常用的屬性,你可以在上面第二個方法中去為下方這些屬性賦值,為Cell定製屬於自己的Attributes。由下方的屬性就對自定義佈局的的強大,在本篇部落格中只用到了下方的一個屬性,那就是frame。

三. UICollectionViewLayout的應用

經過上面的簡單介紹,想必對UICollectionViewLayout有一定的瞭解吧,UICollectionViewLayout中還有好多方法,以後用到的時候在給大家介紹。接下來要使用自定義佈局來實現瀑布流。我們需要在UICollectionViewLayout的子類中實現相應的佈局方法,因為UICollectionViewLayout是虛基類,是不能直接被例項化的,所以我們需要新建一個佈局類,這個佈局類繼承自UICollectionViewLayout。然後去實現上述方法,給每個Cell定製不同的UICollectionViewLayoutAttributes。好了還是拿程式碼說話吧。

1.重寫prepareLayout方法去初始化一些資料,該方法在CollectionView重新載入時只會呼叫一次,所以把一些引數的配置,計算每個Cell的寬度,每個Cell的高度等程式碼放在預處理函式中。在該函式中具體呼叫的函式如下所示:

2.返回內容的範圍,即為CollectionView設定ContentSize。ContentSize的Width就是螢幕的寬度,而ContentSize的高度是一列中最後一個Cell的Y座標加上其自身高度的最大值。在此函式中會呼叫求CellY陣列中的最大值。具體實現程式碼如下:

3.下面的方法是為每個Cell去繫結一個UICollectionViewLayoutAttributes物件,並且以陣列的形式返回,在我們的自定義瀑布流中,我們只自定義了Cell的frame,就可以實現我們的瀑布流,UICollectionViewLayoutAttributes的其他屬性我們沒有用到,由此可以看出自定義Cell佈局功能的強大。

4. 通過下述方法設定每個Cell的UICollectionViewLayoutAttributes物件的引數,為了實現瀑布流所以我們只需要設定每個Cell的frame即可。每個cell的frame的確定是以列來定的,有所在列的上個Cell的Y座標來確定下個cell的位置。瀑布流實現關鍵點如下:

(1)Cell寬度計算:如果瀑布流的列數和Cell的Padding確定了,那麼每個Cell的寬度再通過螢幕的寬度就可以計算出來了。

(2)Cell高度計算:通過隨機數生成的高度

(3)Cell的X軸座標計算:通過列數,和Padding,以及每個Cell的寬度很容易就可以計算出每個Cell的X座標。

(4)Cell的Y軸座標計算:通過Cell所在列的上一個Cell的Y軸座標,Padding, 和 上一個Cell的高度就可以計算下一個Cell的Y座標,並記錄在Y座標的陣列中了。

5. initData方法主要是對資料進行初始化,在本篇部落格中為了先實現效果,我們暫且把資料給寫死。下篇部落格會在本篇部落格中的基礎上進行優化和改進,這些配置引數都會在Delegate中提供,便於靈活的去定製屬於你自己的瀑布流。本篇部落格中Demo的配置項先寫死就OK了,還是那句話,下篇部落格中會給出一些相應的代理,來定製我們的瀑布流。

6.下方的方法是根據Cell的列數來求出Cell的寬度。因為Cell的寬度都是一樣的,每個Cell的間隔也是一定的。例如有5列Cell, 那麼Cell中間的間隔就有4(5-1)個,那麼每個Cell的寬度就是螢幕的寬度減去所有間隔的寬度,再除以列數就是Cell的寬度。如果沒聽我囉嗦明白的話,直接看程式碼吧,並不複雜。每個Cell的寬度和間隔確定了,那麼每個Cell的X軸座標也就確定了。程式碼如下:

7. 根據Cell的最小高度和最大高度來利用隨機數計算每個Cell的高度,把每個Cell的高度記錄在陣列中,便於Cell載入時使用。具體程式碼如下:

8.初始化Cell的Y軸座標陣列,因為是瀑布流,瀑布流的特點是每列中Cell的X軸座標是相同的,我們只需要根據本列上一個Cell的Y軸座標來確定本列中將要插入Cell的Y軸座標,所有我們需要維護一個每列當前Cell的Y軸座標陣列。其初始化方法如下:

9.下面的方法是求Cell的Y軸座標陣列的最大值,因為在Cell都載入完畢後,Cell陣列中最大值就是CollectionView的ContentSize的Height的值。

10.下方程式碼是求CellY陣列中的第一個最小值的索引,因為求出這個CellY陣列中的第一個Cell最新值得索引就是Cell應該插入的列。

自定義集合檢視控制器佈局第一階段就先到這,下篇部落格會在此基礎上進一步開發。把上述寫死的配置引數,通過Delegate提供,使其在UICollectionView可進行配置,其配置方式類似於UICollectionViewDelegateFlowLayout的代理方法。

上述程式碼gitHub分享地址:https://github.com/lizelu/CustomCollectionViewLayout

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

iOS開發之窺探UICollectionViewController(三) :使用UICollectionView自定義瀑布流

相關文章