你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 詳細分享UICollectionView的自定義布局(瀑布流, 線性, 圓形...)

詳細分享UICollectionView的自定義布局(瀑布流, 線性, 圓形...)

編輯:IOS開發基礎

本文授權轉載,作者:ZeroJ(Github)

前言

本篇文章不是分享collectionView的詳細使用教程, 而是屬於比較'高級'的collectionView使用技巧, 閱讀之前, 我想你已經很熟悉collectionView的基本使用, 如果不是很熟悉, 建議在以後熟悉一下。那麼在本篇結束後, 你也能夠很輕松的使用collectionView來實現, 當下比較流行和比較炫酷的效果以及你想要自己實現的其他的效果。這裡就實現三種比較常用的效果: 線性布局, 瀑布流布局, 圓形布局, 其他的各種自定義的布局你將是會有能力自己實現的(Demo地址)。

最終效果

1466504483384965.gif

1466504496304949.gif

1466504510241206.gif

一、首先了解下UICollectionViewLayoutAttributes

  • 當你看這些屬性的時候, 有沒有感覺到是一個view應該有的屬性, 實際上, collectionVIew裡面的所有的cell我們並沒有給它直接設置過他在collectionView上面的frame等屬性, 它的相關的設置都是由UICollectionViewLayoutAttributes來完成的。

  • 每一個cell都對應的有一個UICollectionViewLayoutAttributes來設置他的一些屬性, 當我們在修改他對應的UICollectionViewLayoutAttributes的相關的屬性的時候, 就間接的實現了對響應的cell的相關屬性的修改。

  • 所以我們對collectionViewCell的很多自定義就落在了對相應的UICollectionViewLayoutAttributes上面

public var frame: CGRect
public var center: CGPoint
public var size: CGSize
// 用於實現一些3D效果
public var transform3D: CATransform3D
@available(iOS 7.0, *)
public var bounds: CGRect
@available(iOS 7.0, *)
// 更改transform可以方便的實現一些縮放, 旋轉效果
public var transform: CGAffineTransform
public var alpha: CGFloat
public var zIndex: Int // default is 0
// 初始化方法, 分別對應為collectionView裡面的幾種reusebleView
//cell
public convenience init(forCellWithIndexPath indexPath: NSIndexPath)
//SupplementaryView
public convenience init(forSupplementaryViewOfKind elementKind: String, withIndexPath indexPath: NSIndexPath)
// DecorationView
public convenience init(forDecorationViewOfKind decorationViewKind: String, withIndexPath indexPath: NSIndexPath)

二、了解UICollectionViewFlowLayout

要完成collectionView的布局是需要設置他的屬性 collectionViewLayout, 當使用storyboard的時候是默認為UICollectionViewFlowLayout。

UICollectionViewFlowLayout是系統提供的一種網格布局, 通常情況下你只需要設置一下下面列舉的一些屬性, 就可以達到普通的collectionView的使用效果---網格效果, 同時你可以使用UICollectionViewDelegateFlowLayout 來實現一些比較簡單的動態更改cell的布局的效果。

// 最小的行距
public var minimumLineSpacing: CGFloat
// 最小的列距
public var minimumInteritemSpacing: CGFloat
// cell的大小
public var itemSize: CGSize
// collectionView的滾動方向
public var scrollDirection: UICollectionViewScrollDirection // default is UICollectionViewScrollDirectionVertical
// headersize
public var headerReferenceSize: CGSize
public var footerReferenceSize: CGSize
public var sectionInset: UIEdgeInsets

三、強大的UICollectionViewLayout

  • 要完成collectionView的布局是需要設置他的屬性 collectionViewLayout, 當使用storyboard的時候是默認為UICollectionViewFlowLayout, 實際上UICollectionViewFlowLayout是繼承自UICollectionViewLayout, 由系統實現的一種collectionView的布局。

  • 所以我們可以繼承UICollectionViewLayout來自定義我們想要的布局。

  • 實現自定義的布局並不是很復雜, 官方文檔中已經說明了相關的方法, 這裡直接分享給大家。

下面是自定義UICollectionViewLayout時比較常用到的一些方法:

  • collectionView每次需要重新布局(初始, layout 被設置為invalidated ...)的時候會首先調用這個方法prepareLayout()。所以Apple建議我們可以重寫這個方法來為自定義布局做一些准備的操作,在cell比較少的情況下, 我們一般都可以在這個方法裡面計算好所有的cell布局,並且緩存下來, 在需要的時候直接取相應的值即可, 以提高效率。

func prepareLayout()
  • 然後會調用layoutAttributesForElementsInRect(rect: CGRect)方法獲取到rect范圍內的cell的所有布局, 這個rect大家可以打印出來看下, 和collectionView的bounds不一樣, size可能比collectionView大一些, 這樣設計也許是為了緩沖。Apple要求這個方法必須重寫, 並且提供相應rect范圍內的cell的所有布局的UICollectionViewLayoutAttributes, 如果之前我們已經計算好了,就可以直接返回就可以了, 當然你可以比如只返回rect范圍內的cell的布局,而不是所有的cell的布局, 不過這樣的話你需要設置下一個方法

func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]?
  • 當collectionView的bounds變化的時候會調用,shouldInvalidateLayoutForBoundsChange(newBounds: CGRect)這個方法。如果我們的布局是會時刻變化的, 需要在滾動的過程中重新布局 , 那麼我們需要,設置這個方法的返回值為true, 默認為false。

* 當返回值為true的時候會將collectionView的layout設置為invalidated,將會使collectionView重新調用上面的prepareLayout()...方法重新獲得布局

* 同時, 當屏幕旋轉的時候collectionView的bounds也會調用這個方法,

如果設置為false, 那麼將不會達到屏幕適配的效果,

* 需要注意的是, 當collectionView執行一些操作(delete insert reload)等的時候,

不會調用這個方法, 會直接重新調用上面的prepareLayout()...方法重新獲得布局

public func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool
  • 需要設置collectionView 的滾動范圍 collectionViewContentSize(),自定義的時候, 必須重寫這個方法, 並且返回正確的滾動范圍, collectionView才能正常的滾動

public func collectionViewContentSize() -> CGSize
  • 以下方法, Apple建議我們也重寫, 返回正確的自定義對象的布局。因為有時候當collectionView執行一些操作(delete insert reload)等系統會調用這些方法獲取布局, 如果沒有重寫, 可能發生意想不到的效果。

自定義cell布局的時候重寫

public func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?

自定義SupplementaryView的時候重寫

public func layoutAttributesForSupplementaryViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?

自定義DecorationView的時候重寫

public func layoutAttributesForDecorationViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
  • 這個方法是當collectionView將停止滾動的時候調用, 我們可以重寫它來實現, collectionView停在指定的位置(比如照片浏覽的時候, 你可以通過這個實現居中顯示照片...)

public func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint

同時裡面還有很多的方法我們可以重寫來實現更多的效果, 但是這裡, 就先介紹這麼多的方法來實現自定義collectionView的布局。

下面通過例子介紹下具體的使用

圓形布局的實現

繼承自UICollectionViewLayout, 重寫prepareLayout(),在這裡面我們計算好所有的cell布局

override func prepareLayout() {
    // 一定要調用super
      super.prepareLayout()
      // 初始化需要的數據
      // 總的cell
      totalNum = collectionView!.numberOfItemsInSection(0)
      // 每次計算前需要清零
      layoutAttributes = []
      // 圓心
      center = CGPoint(x: Double(collectionView!.bounds.width * 0.5), y: Double(collectionView!.bounds.height * 0.5))
      // 圓半徑
      radius = min(collectionView!.bounds.width, collectionView!.bounds.height) / 3.0
      var indexPath: NSIndexPath
      for index in 0..

重寫方法設置每個cell的布局

// Apple建議要重寫這個方法, 因為某些情況下(delete insert...)系統可能需要調用這個方法來布局
  override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
      let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
      // 設置cell的大小
      attributes.size = CGSize(width: 60.0, height: 60.0)
      // 當前cell的角度
      // 注意類型轉換
      let angle = 2 * CGFloat(M_PI) * CGFloat(indexPath.row) / CGFloat(totalNum)
      // 一點點數學轉換
      attributes.center = CGPoint(x: center.x + radius*cos(angle), y: center.y + radius*sin(angle))
      return attributes
  }

數學計算過程(寫得不好看##<<>>##)

1271831-1c7c180626674873.jpg

重寫方法返回計算好的布局

override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? 
{
    return layoutAttributes
}

重寫方法設置collectionView的滾動范圍(這裡不滾動)

override func collectionViewContentSize() -> CGSize {
      return collectionView!.bounds.size
}

這樣就實現了自定義的圓形布局, 還是比較簡單!!

關於瀑布流的布局, 自定義過程和這個相似, 就不貼代碼了, Demo地址裡面有詳細的代碼注釋, 直接大家看代碼吧, 如果對你有幫助, 歡迎關注, 歡迎star。

另外Demo裡的布局大家是可以直接拿來使用的,以後你也有能力自己實現各種炫酷的布局效果了。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved