iOS Swift5從0到1系列(十二):走入 UICollectionView(一):了解篇

一、前言

我面過(guò)許多 iOS 開(kāi)發(fā)者,其中有個(gè)問(wèn)題,我偶爾會(huì)問(wèn)到(如果與候選者聊的愉快,我會(huì)問(wèn)這個(gè)小問(wèn)題,回答的不好不會(huì)影響最終的結(jié)果,回答的好可以加分):

大家可能或多或少有點(diǎn)了解:UITableView 能干的事,UICollectionView 幾乎都能干(總有干不了的,除非自定義,例如:刪除手勢(shì)),那么,你認(rèn)為 UITableView 與 UICollectionView 分別應(yīng)該在哪些場(chǎng)景用到?

但實(shí)際上,并沒(méi)有標(biāo)準(zhǔn)的答案,而我也只有個(gè)人建議給出,主要取決于需求(嗯,我也知道是廢話):

  • 如果是瀑布流,那么,老老實(shí)實(shí)的使用 UICollectionView;
  • 如果是簡(jiǎn)單的單行列表,那么首選肯定是 UITableView;

總的來(lái)說(shuō)就是:?jiǎn)瘟杏?UITableView,多列或復(fù)雜邏輯就用 UICollectionView;

那啥,有人會(huì)和我扛,UITableView 單行也能多列!嗯,那你就違背了『用合適的控件使合適的事情,即復(fù)雜問(wèn)題簡(jiǎn)單化』。

簡(jiǎn)單看一下,兩者的 UI 區(qū)別:

52f43bb813ff4dbf9f1827fe04976df0_tplv-k3u1fbpfcp-watermark.png

從上圖可以看出:

  • UITableView 默認(rèn)含有表頭、表尾;而 UICollectionView 默認(rèn)沒(méi)有;
  • UITableView 默認(rèn)是 Section + (Header + UITableViewCell + Footer);
  • UICollectionView 默認(rèn)是 Section +(Header + UICollectionViewCell + Footer);

二、UICollectionView 官方流程圖

2.png

上圖是官方的流程圖:

  • 最上面是一個(gè) N行M列 的 Cell 單元格,我們叫作: UICollectionViewCell;
  • 類(lèi)似 UITableView,UICollectionView 是 UICollectionViewCell 的容器;
  • UICollectionViewCell 的展示方式是由左側(cè)的 layout(attributes) 來(lái)決定,官方提供了一個(gè)已經(jīng)定義好的 layout:UICollectionViewFlowLayout (流式布局),我們也可以自定義;
  • 同樣,類(lèi)似 UITableView,cell 的數(shù)據(jù)來(lái)源由 dataSource 負(fù)責(zé),cell 的操作由 delegate 負(fù)責(zé);

對(duì)于 layout ,你可以類(lèi)比 delegate 或者 data source,即 cell 的展示由 layout 來(lái)決定。

三、UICollectionViewFlowLayout 核心屬性源碼分析

@available(iOS 6.0, *)
open class UICollectionViewFlowLayout : UICollectionViewLayout {
    // 同一組當(dāng)中:
    // 垂直方向:行與行之間的間距 or 水平方向:列與列之間的間距
    open var minimumLineSpacing: CGFloat
    // 垂直方向:同一行中,cell之間的間距 or 水平方向:同一列中,cell與cell之間的間距
    open var minimumInteritemSpacing: CGFloat
    // 每個(gè) cell 的尺寸
    open var itemSize: CGSize
    // 滑動(dòng)方向:默認(rèn)滑動(dòng)方向是垂直方向滑動(dòng)
    open var scrollDirection: UICollectionView.ScrollDirection
    // 每一組頭視圖的尺寸。如果是垂直方向滑動(dòng),則只有高起作用 or 如果是水平方向滑動(dòng),則只有寬起作用
    open var headerReferenceSize: CGSize
    // 同上,作用于尾視圖
    open var footerReferenceSize: CGSize
    // 每一組的內(nèi)容縮進(jìn)
    open var sectionInset: UIEdgeInsets

    // 下面兩屬性,類(lèi)似 UITableView,就是我們常說(shuō)的,是否:吸頂 / 吸底
    @available(iOS 9.0, *)
    open var sectionHeadersPinToVisibleBounds: Bool
    @available(iOS 9.0, *)
    open var sectionFootersPinToVisibleBounds: Bool
}

上面的源碼注釋已經(jīng)寫(xiě)的很詳細(xì)了,后續(xù)的組件開(kāi)發(fā)中會(huì)用到,這里大家只需要了解即可。

四、UICollectionView 源碼簡(jiǎn)要分析

@available(iOS 6.0, *)
open class UICollectionView : UIScrollView, UIDataSourceTranslating {
    // 給定 frame 大小 && layout(布局)
    public init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout)
    ......
    // 布局(可以使用默認(rèn)的 UICollectionViewFlowLayout,也可以自定義)
    open var collectionViewLayout: UICollectionViewLayout
    // 委托:操作 UICollectionViewCell(item)
    weak open var delegate: UICollectionViewDelegate?
    // 數(shù)據(jù)源
    weak open var dataSource: UICollectionViewDataSource?

    // 注冊(cè) cell 的不同方式:手寫(xiě)代碼 or XIB    
    open func register(_ cellClass: AnyClass?, forCellWithReuseIdentifier identifier: String)
    open func register(_ nib: UINib?, forCellWithReuseIdentifier identifier: String)

    // 同上,注冊(cè) header / footer 的不同方式
    open func register(_ viewClass: AnyClass?, forSupplementaryViewOfKind elementKind: String, withReuseIdentifier identifier: String)
    open func register(_ nib: UINib?, forSupplementaryViewOfKind kind: String, withReuseIdentifier identifier: String)

    // 重用 cell、header / footer(根據(jù)傳入不同的 identifier)
    open func dequeueReusableCell(withReuseIdentifier identifier: String, for indexPath: IndexPath) -> UICollectionViewCell
    open func dequeueReusableSupplementaryView(ofKind elementKind: String, withReuseIdentifier identifier: String, for indexPath: IndexPath) -> UICollectionReusableView
}

五、UICollectionViewDataSource 源碼必要方法分析

public protocol UICollectionViewDataSource : NSObjectProtocol {
    // 我們可能有多個(gè) Section,每個(gè) Section 又可以有不同個(gè)數(shù)的 Items,即我們的數(shù)據(jù)結(jié)構(gòu)是二維:[Section][Item]
    @available(iOS 6.0, *)
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int

    // 必需調(diào)用 -dequeueReusableCellWithReuseIdentifier:forIndexPath: 來(lái)獲取可重用的 cell,
    // 然后根據(jù) indexPath 來(lái)重新賦值,更新顯示,最后返回該 cell
    @available(iOS 6.0, *)
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

    // 可選方法,如果只有 1 個(gè) section,可以不用實(shí)現(xiàn)(官方文檔:默認(rèn)返回值是 1)
    @available(iOS 6.0, *)
    optional func numberOfSections(in collectionView: UICollectionView) -> Int

    // 可選方法,類(lèi)同上面第二個(gè)方法
    // 必需調(diào)用 -dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: 來(lái)獲取可重用的 header / footer
    @available(iOS 6.0, *)
    optional func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容