原文地址:http://www.cocoachina.com/ios/20160622/16784.html
本文授權(quán)轉(zhuǎn)載,作者:ZeroJ(Github)
前言
本篇文章不是分享collectionView的詳細(xì)使用教程, 而是屬于比較'高級'的collectionView使用技巧, 閱讀之前, 我想你已經(jīng)很熟悉collectionView的基本使用, 如果不是很熟悉, 建議在以后熟悉一下。那么在本篇結(jié)束后, 你也能夠很輕松的使用collectionView來實現(xiàn), 當(dāng)下比較流行和比較炫酷的效果以及你想要自己實現(xiàn)的其他的效果。這里就實現(xiàn)三種比較常用的效果: 線性布局, 瀑布流布局, 圓形布局, 其他的各種自定義的布局你將是會有能力自己實現(xiàn)的(Demo地址)。
最終效果
一、首先了解下UICollectionViewLayoutAttributes
當(dāng)你看這些屬性的時候,
有沒有感覺到是一個view應(yīng)該有的屬性, 實際上,
collectionVIew里面的所有的cell我們并沒有給它直接設(shè)置過他在collectionView上面的frame等屬性,
它的相關(guān)的設(shè)置都是由UICollectionViewLayoutAttributes來完成的。
每一個cell都對應(yīng)的有一個UICollectionViewLayoutAttributes來設(shè)置他的一些屬性,
當(dāng)我們在修改他對應(yīng)的UICollectionViewLayoutAttributes的相關(guān)的屬性的時候,
就間接的實現(xiàn)了對響應(yīng)的cell的相關(guān)屬性的修改。
所以我們對collectionViewCell的很多自定義就落在了對相應(yīng)的UICollectionViewLayoutAttributes上面
publicvarframe:?CGRect
publicvarcenter:?CGPoint
publicvarsize:?CGSize
//?用于實現(xiàn)一些3D效果
publicvartransform3D:?CATransform3D
@available(iOS?7.0,?*)
publicvarbounds:?CGRect
@available(iOS?7.0,?*)
//?更改transform可以方便的實現(xiàn)一些縮放,?旋轉(zhuǎn)效果
publicvartransform:?CGAffineTransform
publicvaralpha:?CGFloat
publicvarzIndex:?Int//?default?is?0
//?初始化方法,?分別對應(yīng)為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的布局是需要設(shè)置他的屬性 collectionViewLayout, 當(dāng)使用storyboard的時候是默認(rèn)為UICollectionViewFlowLayout。
UICollectionViewFlowLayout是系統(tǒng)提供的一種網(wǎng)格布局,
通常情況下你只需要設(shè)置一下下面列舉的一些屬性, 就可以達(dá)到普通的collectionView的使用效果---網(wǎng)格效果,
同時你可以使用UICollectionViewDelegateFlowLayout 來實現(xiàn)一些比較簡單的動態(tài)更改cell的布局的效果。
//?最小的行距
publicvarminimumLineSpacing:?CGFloat
//?最小的列距
publicvarminimumInteritemSpacing:?CGFloat
//?cell的大小
publicvaritemSize:?CGSize
//?collectionView的滾動方向
publicvarscrollDirection:?UICollectionViewScrollDirection//?default?is?UICollectionViewScrollDirectionVertical
//?headersize
publicvarheaderReferenceSize:?CGSize
publicvarfooterReferenceSize:?CGSize
publicvarsectionInset:?UIEdgeInsets
三、強(qiáng)大的UICollectionViewLayout
要完成collectionView的布局是需要設(shè)置他的屬性
collectionViewLayout, 當(dāng)使用storyboard的時候是默認(rèn)為UICollectionViewFlowLayout,
實際上UICollectionViewFlowLayout是繼承自UICollectionViewLayout,
由系統(tǒng)實現(xiàn)的一種collectionView的布局。
所以我們可以繼承UICollectionViewLayout來自定義我們想要的布局。
實現(xiàn)自定義的布局并不是很復(fù)雜, 官方文檔中已經(jīng)說明了相關(guān)的方法, 這里直接分享給大家。
下面是自定義UICollectionViewLayout時比較常用到的一些方法:
collectionView每次需要重新布局(初始, layout 被設(shè)置為invalidated ...)的時候會首先調(diào)用這個方法prepareLayout()。所以Apple建議我們可以重寫這個方法來為自定義布局做一些準(zhǔn)備的操作,在cell比較少的情況下, 我們一般都可以在這個方法里面計算好所有的cell布局,并且緩存下來, 在需要的時候直接取相應(yīng)的值即可, 以提高效率。
func?prepareLayout()
然后會調(diào)用layoutAttributesForElementsInRect(rect: CGRect)方法獲取到rect范圍內(nèi)的cell的所有布局, 這個rect大家可以打印出來看下, 和collectionView的bounds不一樣, size可能比collectionView大一些, 這樣設(shè)計也許是為了緩沖。Apple要求這個方法必須重寫, 并且提供相應(yīng)rect范圍內(nèi)的cell的所有布局的UICollectionViewLayoutAttributes, 如果之前我們已經(jīng)計算好了,就可以直接返回就可以了, 當(dāng)然你可以比如只返回rect范圍內(nèi)的cell的布局,而不是所有的cell的布局, 不過這樣的話你需要設(shè)置下一個方法
func?layoutAttributesForElementsInRect(rect:?CGRect)?->?[UICollectionViewLayoutAttributes]?
當(dāng)collectionView的bounds變化的時候會調(diào)用,shouldInvalidateLayoutForBoundsChange(newBounds: CGRect)這個方法。如果我們的布局是會時刻變化的, 需要在滾動的過程中重新布局 , 那么我們需要,設(shè)置這個方法的返回值為true, 默認(rèn)為false。
* 當(dāng)返回值為true的時候會將collectionView的layout設(shè)置為invalidated,將會使collectionView重新調(diào)用上面的prepareLayout()...方法重新獲得布局
* 同時, 當(dāng)屏幕旋轉(zhuǎn)的時候collectionView的bounds也會調(diào)用這個方法,
如果設(shè)置為false, 那么將不會達(dá)到屏幕適配的效果,
* 需要注意的是, 當(dāng)collectionView執(zhí)行一些操作(delete insert reload)等的時候,
不會調(diào)用這個方法, 會直接重新調(diào)用上面的prepareLayout()...方法重新獲得布局
1
public?func?shouldInvalidateLayoutForBoundsChange(newBounds:?CGRect)?->?Bool
需要設(shè)置collectionView 的滾動范圍 collectionViewContentSize(),自定義的時候, 必須重寫這個方法, 并且返回正確的滾動范圍, collectionView才能正常的滾動
1
public?func?collectionViewContentSize()?->?CGSize
以下方法, Apple建議我們也重寫, 返回正確的自定義對象的布局。因為有時候當(dāng)collectionView執(zhí)行一些操作(delete insert reload)等系統(tǒng)會調(diào)用這些方法獲取布局, 如果沒有重寫, 可能發(fā)生意想不到的效果。
自定義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?
這個方法是當(dāng)collectionView將停止?jié)L動的時候調(diào)用, 我們可以重寫它來實現(xiàn), collectionView停在指定的位置(比如照片瀏覽的時候, 你可以通過這個實現(xiàn)居中顯示照片...)
public?func?targetContentOffsetForProposedContentOffset(proposedContentOffset:?CGPoint,?withScrollingVelocity?velocity:?CGPoint)?->?CGPoint
同時里面還有很多的方法我們可以重寫來實現(xiàn)更多的效果, 但是這里, 就先介紹這么多的方法來實現(xiàn)自定義collectionView的布局。
下面通過例子介紹下具體的使用
圓形布局的實現(xiàn)
繼承自UICollectionViewLayout, 重寫prepareLayout(),在這里面我們計算好所有的cell布局
override?func?prepareLayout()?{
//?一定要調(diào)用super
super.prepareLayout()
//?初始化需要的數(shù)據(jù)
//?總的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
varindexPath:?NSIndexPath
forindexin0..
重寫方法設(shè)置每個cell的布局
//?Apple建議要重寫這個方法,?因為某些情況下(delete?insert...)系統(tǒng)可能需要調(diào)用這個方法來布局
override?func?layoutAttributesForItemAtIndexPath(indexPath:?NSIndexPath)?->?UICollectionViewLayoutAttributes??{
let?attributes?=?UICollectionViewLayoutAttributes(forCellWithIndexPath:?indexPath)
//?設(shè)置cell的大小
attributes.size?=?CGSize(width:?60.0,?height:?60.0)
//?當(dāng)前cell的角度
//?注意類型轉(zhuǎn)換
let?angle?=?2?*?CGFloat(M_PI)?*?CGFloat(indexPath.row)?/?CGFloat(totalNum)
//?一點點數(shù)學(xué)轉(zhuǎn)換
attributes.center?=?CGPoint(x:?center.x?+?radius*cos(angle),?y:?center.y?+?radius*sin(angle))
returnattributes
}
數(shù)學(xué)計算過程(寫得不好看##<<>>##)
http://cc.cocimg.com/api/uploads/20160621/1466504858311878.jpg" title="1466504858311878.jpg" alt="1271831-1c7c180626674873.jpg">
重寫方法返回計算好的布局
override?func?layoutAttributesForElementsInRect(rect:?CGRect)?->?[UICollectionViewLayoutAttributes]?
{
returnlayoutAttributes
}
重寫方法設(shè)置collectionView的滾動范圍(這里不滾動)
override?func?collectionViewContentSize()?->?CGSize?{
returncollectionView!.bounds.size
}
這樣就實現(xiàn)了自定義的圓形布局, 還是比較簡單!!
關(guān)于瀑布流的布局, 自定義過程和這個相似, 就不貼代碼了, https://github.com/jasnig/CollectionViewLayout"target="_blank">Demo地址里面有詳細(xì)的代碼注釋, 直接大家看代碼吧, 如果對你有幫助, 歡迎關(guān)注, 歡迎star。
另外Demo里的布局大家是可以直接拿來使用的,以后你也有能力自己實現(xiàn)各種炫酷的布局效果了。