理解 UICollection Flow Layout 流式布局

UICollectionView 因?yàn)?流式布局 (flow layout)而成了為一個(gè)非常強(qiáng)大的 UI 組件,流式布局是一種動(dòng)態(tài)網(wǎng)格鲁纠,提供了 table view 所不具備的功能。

flow layout 實(shí)際上是 layout 的子類。(普通的)layout 要更強(qiáng)大一點(diǎn),因?yàn)槟憧梢匀我獠季謫卧窈舱。…h(huán)形布局?沒問題讽坏!

但在本文中锭魔,我們只討論垂直的流式布局。

有兩種實(shí)現(xiàn)方式:

  1. 自定義 flow layout 對象(簡單方法)
  2. 實(shí)現(xiàn) delegate(高級方法)

單元格是如何被布局的

在討論如何實(shí)現(xiàn)上面的方法之前路呜,先搞懂單元格是如何被布局的迷捧。

用垂直的流式布局作為例子(水平的很相似)织咧。

  • 一行中最高的單元格決定了行高
  • 一行中所有單元格都是垂直居中對齊的
  • Minimum spacing 是單元格之間的最小距離,但實(shí)際上的間距由 collection view 的寬度決定
  • 流式布局對象會(huì)用最小間距添加單元格漠秋,直到加不下了為止笙蒙,然后增加實(shí)際間距,以使它們間隔均勻
  • 每個(gè) section 都有自己的行/單元格間距
  • 在一個(gè) section 中庆锦,行/單元格間距是固定的捅位;同一個(gè) section 中不能有兩種行/單元格間距
  • 每個(gè) section 有自己的 inset

1. 簡單方法

如果單元格具有固定的大小,只要使用 layout 對象即可——[UICollectionViewFlowLayout](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewFlowLayout_class/index.html#//apple_ref/occ/instp/UICollectionViewFlowLayout/)

這是單元格為 100x100 的例子肥荔,相隔至少 8pt绿渣,section inset 也為 8pt朝群。

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: 100, height: 100)
    layout.minimumInteritemSpacing = 8
    layout.minimumLineSpacing = 8
    layout.headerReferenceSize = CGSize(width: 0, height: 40)
    layout.sectionInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
    collectionView.collectionViewLayout = layout
}

如果單元格很簡單燕耿,布局就這么簡單。

2. 高級方法

如果單元格有不同的尺寸姜胖,就需要用高級方法了誉帅,要實(shí)現(xiàn) [UICollectionViewDelegateFlowLayout](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewDelegateFlowLayout_protocol/#//apple_ref/occ/intfm/UICollectionViewDelegateFlowLayout/)

使用了相同的 UICollectionViewFlowLayout 對象右莱,但會(huì)實(shí)現(xiàn)它的代理方法以定制更高級的功能蚜锨。

舉個(gè)例子,如果每個(gè)單元格尺寸不同慢蜓,會(huì)實(shí)現(xiàn)如下方法:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
      // 返回單元格尺寸
  }

對于最小行間距亚再、單元格間距等等都有對應(yīng)的代理方法。

這些都是可選的晨抡,如果不實(shí)現(xiàn)它們氛悬,就會(huì)直接使用流式布局對象的屬性。

附加:如何讓單元格有固定的間距耘柱?

一個(gè) 常見問題 是要讓單元格有固定的間距如捅。

然而,只能設(shè)置 minimumInteritemSpacing调煎,實(shí)際單元格間距由 collection view 的寬度決定镜遣。

UICollectionViewFlowLayout 會(huì)在應(yīng)用 section inset 后排列中間的單元格,每個(gè)單元格之間的間距都相同士袄。

如果想要固定的間距悲关,這么做 可以實(shí)現(xiàn),通過修改 section 的左右 inset:

private let minItemSpacing: CGFloat = 8
private let itemWidth: CGFloat      = 100
private let headerHeight: CGFloat   = 32

override func viewDidLayoutSubviews() {
      super.viewDidLayoutSubviews()

      // 創(chuàng)建自定義流式布局娄柳,將單元格均勻分布寓辱,并將它們放在中間
      let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
    layout.minimumInteritemSpacing = minItemSpacing
    layout.minimumLineSpacing = minItemSpacing
    layout.headerReferenceSize = CGSize(width: 0, height: headerHeight)

      // 求 n,n 是 collection view 可以容納的單元格數(shù)量
      var n: CGFloat = 1
    let containerWidth = collectionView.bounds.width
    while true {
        let nextN = n + 1
        let totalWidth = (nextN*itemWidth) + (nextN-1)*minItemSpacing
        if totalWidth > containerWidth {
            break
        } else {
            n = nextN
        }
    }

      // 計(jì)算 section 的左右 inset
      // 設(shè)置 section 的 inset 會(huì)影響單元格西土,以將它們水平居中對齊
      let inset = max(minItemSpacing, floor( (containerWidth - (n*itemWidth) - (n-1)*minItemSpacing) / 2 ) )
    layout.sectionInset = UIEdgeInsets(top: minItemSpacing, left: inset, bottom: minItemSpacing, right: inset)

    collectionView.collectionViewLayout = layout
}

附加:自定義布局

流式布局是開箱即用的讶舰。易于使用,對于大多數(shù) UI 都足夠了。

但也可以創(chuàng)建自己的 自定義布局 跳昼。

布局類的核心方法是 layoutAttributesForElementsInRect:般甲。可以讀一下 來自 objc.io 的教程鹅颊,寫的很好敷存。這是更高級別的主題。

注意:通常我們會(huì)使用 autolayout 約束堪伍,但對于單元格(cell)锚烦,需要用傳統(tǒng)的方式設(shè)置 frame。只有 cell 要這么做帝雇。單元格里面的視圖仍然可以使用自動(dòng)布局涮俄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尸闸,隨后出現(xiàn)的幾起案子彻亲,更是在濱河造成了極大的恐慌,老刑警劉巖吮廉,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苞尝,死亡現(xiàn)場離奇詭異,居然都是意外死亡宦芦,警方通過查閱死者的電腦和手機(jī)宙址,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來调卑,“玉大人抡砂,你說我怎么就攤上這事×钜埃” “怎么了舀患?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長气破。 經(jīng)常有香客問我聊浅,道長,這世上最難降的妖魔是什么现使? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任低匙,我火速辦了婚禮,結(jié)果婚禮上碳锈,老公的妹妹穿的比我還像新娘顽冶。我一直安慰自己,他們只是感情好售碳,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布强重。 她就那樣靜靜地躺著绞呈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪间景。 梳的紋絲不亂的頭發(fā)上佃声,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音倘要,去河邊找鬼圾亏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛封拧,可吹牛的內(nèi)容都是我干的志鹃。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼泽西,長吁一口氣:“原來是場噩夢啊……” “哼曹铃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尝苇,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤铛只,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后糠溜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡直撤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年非竿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谋竖。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡红柱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蓖乘,到底是詐尸還是另有隱情锤悄,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布嘉抒,位于F島的核電站零聚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏些侍。R本人自食惡果不足惜隶症,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岗宣。 院中可真熱鬧蚂会,春花似錦、人聲如沸耗式。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至彪见,卻和暖如春躲叼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背企巢。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工枫慷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浪规。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓或听,卻偏偏與公主長得像,于是被迫代替她去往敵國和親笋婿。 傳聞我的和親對象是個(gè)殘疾皇子誉裆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容