UICollectionView Layout自定義 Layout布局


from:   http://www.tuicool.com/articles/vuyIriN

當我們使用系統自帶的UICollectionViewFlowLayout無法實現我們的布局時,我們就可以考慮自定義layout。

所以,了解並學習一下自定義Layout是很有必要。

其實可以分三個步驟:

  1. 覆寫prepareLayout方法,並在里面事先就計算好必要的布局信息並存儲起來。
  2. 基於prepareLayout方法中的布局信息,使用collectionViewContentSize方法返回UICollectionView的內容尺寸。
  3. 使用layoutAttributesForElementsInRect:方法返回指定區域cell、Supplementary View和Decoration View的布局屬性。

了解了自定義布局的三個主要步驟,我們來通過自定義布局的方式用UICollectionView實現grideView。當然,grideView使用 UICollectionViewFlowLayout 就可以輕易實現,這里我們只是學習了解一下自定義布局的過程,所以拿grideView這個經常用的來作為例子。

我們創建一個新的工程 BGCustomLayoutCollectionViewDemo 。然后創建一個UICollectionViewLayout的子類對象 BGGrideLayout ,它就是我們自定義layout對象。

在BGGrideLayout里面,我們首先覆寫prepareLayout方法。

prepareLayout是專門用來准備布局的,在 prepareLayout 方法里面我們可以事先就計算后面要用到的布局信息並存儲起來,防止后面方法多次計算,提高性能。例如,我們可以在此方法就計算好每個cell的屬性、整個CollectionView的內容尺寸等等。此方法在布局之前會調用一次,之后只有在調用 invalidateLayout 、 shouldInvalidateLayoutForBoundsChange: 返回 YES 和 UICollectionView刷新 的時候才會調用。

而在BGGrideLayout的prepareLayout方法中,我們有兩個目的:

一是獲取對應indexPath的 UICollectionViewLayoutAttributes 對象,並存儲到二維數組 layoutInfoArr 當中;

二是計算出內容尺寸並保存到全局變量 contentSize 當中。

代碼如下:

- (void)prepareLayout{ [super prepareLayout]; NSMutableArray *layoutInfoArr = [NSMutableArray array]; NSInteger maxNumberOfItems = 0; //獲取布局信息 NSInteger numberOfSections = [self.collectionView numberOfSections]; for (NSInteger section = 0; section < numberOfSections; section++){ NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section]; NSMutableArray *subArr = [NSMutableArray arrayWithCapacity:numberOfItems]; for (NSInteger item = 0; item < numberOfItems; item++){ NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section]; UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; [subArr addObject:attributes]; } if(maxNumberOfItems < numberOfItems){ maxNumberOfItems = numberOfItems; } //添加到二維數組 [layoutInfoArr addObject:[subArr copy]]; } //存儲布局信息 self.layoutInfoArr = [layoutInfoArr copy]; //保存內容尺寸 self.contentSize = CGSizeMake(maxNumberOfItems*(self.itemSize.width+self.interitemSpacing)+self.interitemSpacing, numberOfSections*(self.itemSize.height+self.lineSpacing)+self.lineSpacing); } 

在上面的代碼中,我們看到了 UICollectionViewLayoutAttributes 這個類,這個類其實專門用來存儲視圖的內容,例如frame、size、apha、hiden等等,layout最后會拿着這些frame設置給對應的視圖。 而上面代碼中,獲取 UICollectionViewLayoutAttributes 是通過 layoutAttributesForItemAtIndexPath: 方法

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; //每一組cell為一行 attributes.frame = CGRectMake((self.itemSize.width+self.interitemSpacing)*indexPath.row+self.interitemSpacing, (self.itemSize.height+self.lineSpacing)*indexPath.section+self.lineSpacing, self.itemSize.width, self.itemSize.height); return attributes; } 

在這個方法中, itemSize 是cell的大小, interitemSpacing 是cell與cell之間的間距, lineSpacing 是行距。

隨后,覆寫collectionViewContentSize

collectionViewContentSize返回內容尺寸給UICollectionView。注意這個方法返回的尺寸是給UICollectionView這個繼承於 UIScrollView 的視圖作為 contentSize ,不是UICollectionView的視圖尺寸。正是因為這一點,我們自定義layout如果想讓它只能橫向滑動,只需要將這個 size.height 設置成 collectionView.height 就行了。 這個方法會多次調用,所以最好是在prepareLayout里就計算好。 在BGGrideLayout類中,我們只需要返回前面計算好的內容尺寸就行了。

- (CGSize)collectionViewContentSize{ return self.contentSize; } 

最后,覆寫layoutAttributesForElementsInRect:方法

此方法需要返回一組UICollectionViewLayoutAttributes類型對象。它代表着在這個指定的區域中,我們需要顯示 cell 、 Supplementary View 和 Decoration View中哪些視圖,而這些視圖的屬性則保存UICollectionViewLayoutAttributes中。 此方法會多次調用,為了更好的性能,在這個方法當中,我們使用的UICollectionViewLayoutAttributes最好是在prepareLayout已經布局好的信息。

在BGGrideLayout中,我們遍歷二維數組,找出了與指定區域有交接的UICollectionViewLayoutAttributes對象放到一個數組中,然后返回。

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ NSMutableArray *layoutAttributesArr = [NSMutableArray array]; [self.layoutInfoArr enumerateObjectsUsingBlock:^(NSArray *array, NSUInteger i, BOOL * _Nonnull stop) { [array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *obj, NSUInteger idx, BOOL * _Nonnull stop) { if(CGRectIntersectsRect(obj.frame, rect)) { [layoutAttributesArr addObject:obj]; } }]; }]; return layoutAttributesArr; } 

到這里,我們的BGGrideLayout已經寫好了,使用部分的代碼,請直接查看 BGCustomLayoutCollectionViewDemo 中ViewController里面的代碼就行了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM