UICollectionView

概述

UICollectionView是iOS開發(fā)中最常用的UI控件之一,可以用它來管理一組有序的不同尺寸的視圖威沫,并以可定制的布局來展示它們。UICollectionView支持動(dòng)畫脂凶,當(dāng)視圖被插入敲街,刪除或重新排序時(shí),會(huì)觸發(fā)動(dòng)畫绞旅,動(dòng)畫效果支持自定義摆尝。為了更好的使用UICollectionView,我們有必要對(duì)其進(jìn)行深入了解因悲。

基礎(chǔ)

UICollectionView是由多個(gè)對(duì)象協(xié)作實(shí)現(xiàn)的

集合視圖將視圖的數(shù)據(jù)內(nèi)容與視圖的布局方式分開來管理堕汞。數(shù)據(jù)內(nèi)容由集合視圖的dataSource對(duì)象管理,而布局方式則是由許多不同的對(duì)象協(xié)作來管理晃琳。下表列出了UIKit中與集合視圖有關(guān)的類讯检,并根據(jù)它們?cè)诩弦晥D中的作用進(jìn)行了劃分。

目的 類/協(xié)議 描述
頂層容器和管理者 UICollectionView
UICollectionViewController
UICollectionView定義了顯示視圖內(nèi)容的空間卫旱,它繼承自UIScrollView人灼,能夠根據(jù)內(nèi)容的高度來調(diào)整其滾動(dòng)區(qū)域。其layout布局對(duì)象會(huì)提供布局信息來呈現(xiàn)數(shù)據(jù)顾翼。
UICollectionViewController對(duì)象提供了一個(gè)UICollectionView的視圖控制器級(jí)管理支持投放。
內(nèi)容管理 UICollectionViewDataSource協(xié)議
UICollectionViewDelegate協(xié)議
DataSource協(xié)議是必須實(shí)現(xiàn)的,它創(chuàng)造并管理UICollectionView的視圖內(nèi)容暴构。
Delegate協(xié)議能獲取視圖的信息并自定義視圖的行為跪呈,這個(gè)協(xié)議是可選實(shí)現(xiàn)的段磨。
內(nèi)容視圖 UICollectionReusableView
UICollectionViewCell
UICollectionView展示的所有視圖都必須是UICollectionReusableView類的實(shí)例,該類支持回收復(fù)用機(jī)制耗绿。在視圖滾動(dòng)時(shí),回收復(fù)用視圖而不是重新創(chuàng)建苹支,能極大提高性能。
UICollectionViewCell對(duì)象是用來展示主要數(shù)據(jù)的可重用視圖,該類繼承自UICollectionReusableView误阻。
布局 UICollectionViewLayout
UICollectionViewLayoutAttributes
UICollectionViewUpdateItem
UICollectionViewLayout的子類被稱為布局對(duì)象债蜜,它負(fù)責(zé)定義集合視圖中的cell和可重用視圖的位置,大小究反,視覺效果寻定。在布局過程中,布局對(duì)象UICollectionViewLayout會(huì)創(chuàng)建一個(gè)布局屬性對(duì)象UICollectionViewLayoutAttributes去告訴集合視圖在什么位置精耐,用什么樣視覺外觀去展示cell和可重用視圖狼速。當(dāng)在集合視圖中插入、刪除卦停、移動(dòng)數(shù)據(jù)項(xiàng)時(shí)向胡,布局對(duì)象會(huì)接收到UICollectionViewUpdateItem類的實(shí)例,不需要自行創(chuàng)建該類的實(shí)例惊完。
流水布局 UICollectionViewFlowLayout協(xié)議
UICollectionViewDelegateFlowLayout協(xié)議
UICollectionViewFlowLayout類是用于實(shí)現(xiàn)網(wǎng)格或其他基于行的布局的具體布局對(duì)象僵芹。 可以按照原樣使用該類或者配合UICollectionViewDelegateFlowLayout協(xié)議一起使用,這樣就可以動(dòng)態(tài)自定義布局信息小槐。

集合視圖從其dataSource對(duì)象中獲取要展示的cell的數(shù)據(jù)內(nèi)容拇派,并通過其delegate對(duì)象去管理cell的選中和高亮等狀態(tài)。布局對(duì)象負(fù)責(zé)決定cell所在的位置凿跳,布局屬性對(duì)象記錄了cell的布局屬性件豌,布局對(duì)象將布局屬性對(duì)象傳遞給集合視圖,集合視圖接收到布局屬性信息后創(chuàng)建并展示cell控嗜。

圖1-1

重用視圖提高性能

集合視圖通過復(fù)用已被回收的cell來提高效率苟径,當(dāng)cell滾動(dòng)到屏幕外時(shí),它們不會(huì)被銷毀躬审,但會(huì)被移出容器視圖并放置到重用隊(duì)列中棘街。當(dāng)有新的內(nèi)容將要滾動(dòng)到屏幕中時(shí),如果重用隊(duì)列中有可復(fù)用的cell承边,會(huì)首先從重用隊(duì)列中取遭殉,并重置被取出來的cell的數(shù)據(jù),然后將其添加到容器視圖中展示博助。如果重用隊(duì)列沒有可復(fù)用的cell险污,這時(shí)才會(huì)新創(chuàng)建一個(gè)cell去展示。為了方便這種循環(huán),集合視圖中展示的視圖類都必須繼承自UICollectionReusableView類蛔糯。

集合視圖支持三種不同類型的可重用視圖拯腮,每種視圖都具有特定的用途:

  • cell(單元格)展示集合視圖的主要內(nèi)容,每個(gè)cell展示的內(nèi)容由dataSource對(duì)象提供蚁飒。每個(gè)cell都必須是UICollectionViewCell的實(shí)例动壤,同時(shí)也可以根據(jù)需要對(duì)其子類化。cell對(duì)象支持管理其選中和高亮狀態(tài)淮逻。
  • supplementary view(補(bǔ)充視圖)展示每個(gè)section(分區(qū))的信息琼懊。和cell相同的是:supplementary view也是數(shù)據(jù)驅(qū)動(dòng)的。不同的是:supplementary view是可選的而不是強(qiáng)制的爬早。supplementary view的使用和布局是由布局對(duì)象管理的哼丈,系統(tǒng)提供的流水布局支持設(shè)置header和footer作為可選的supplementary view。
  • decoration view(裝飾視圖)與dataSource對(duì)象提供的數(shù)據(jù)不相關(guān)筛严,完全屬于布局對(duì)象醉旦。布局對(duì)象可能會(huì)使用它自定義集合視圖背景。

布局對(duì)象控制視圖的視覺效果

布局對(duì)象負(fù)責(zé)確定集合視圖中每個(gè)cell的位置和視覺樣式桨啃。雖然dataSource對(duì)象提供了要展示的視圖和實(shí)際內(nèi)容髓抑,但布局對(duì)象確定了這些視圖的位置,大小以及其他與外觀相關(guān)的屬性优幸。這種責(zé)任劃分使得我們能夠在動(dòng)態(tài)的更改布局時(shí)無需更改dataSource對(duì)象提供的數(shù)據(jù)。

布局對(duì)象并不擁有任何視圖褪猛,它只會(huì)生成用來描述cell网杆,supplementary view和decoration view的位置,大小伊滋,視覺樣式的布局屬性碳却,并將布局屬性傳遞給集合視圖,集合視圖將這些屬性應(yīng)用于實(shí)際的視圖對(duì)象笑旺。

布局對(duì)象可以隨意生成視圖的位置昼浦,大小以及視覺樣式屬性,沒有任何限制筒主。只有布局對(duì)象能改變視圖在集合視圖中的位置关噪,它能移動(dòng)視圖,也能隨機(jī)切換橫豎屏乌妙,甚至能復(fù)位某視圖而不用考慮此視圖周圍的視圖使兔。例如,如果有需要藤韵,布局對(duì)象可以將所有視圖疊加在一起虐沥。

下圖顯示了垂直滾動(dòng)的流水布局對(duì)象如何布置cell。在垂直滾動(dòng)流水布局中,內(nèi)容區(qū)域的寬度保持固定欲险,高度隨著內(nèi)容高度的增加而增加镐依。布局對(duì)象一次只放置一個(gè)cell,在放置前會(huì)先計(jì)算出cell在容器視圖中的frame天试,為cell選擇最合適的位置槐壳。

圖1-2

使用

必須為集合視圖提供一個(gè)dataSource對(duì)象,集合視圖從dataSource對(duì)象中獲取要顯示的內(nèi)容秋秤。它可以是一個(gè)數(shù)據(jù)模型對(duì)象宏粤,也可以是管理集合視圖的視圖控制器,對(duì)dataSource對(duì)象的唯一要求是它必須能夠提供集合視圖所需的所有信息灼卢。delegate對(duì)象是可選提供的绍哎,其被用于管理與內(nèi)容的呈現(xiàn)以及交互有關(guān)的方面。它的主要職責(zé)是管理cell的選中和高亮狀態(tài)鞋真,也可以擴(kuò)展UICollectionViewDelegate協(xié)議以提供其他信息崇堰。流水布局對(duì)象就擴(kuò)展了UICollectionViewDelegate協(xié)議來定制布局,例如涩咖,cell的大小和它們之間的間距海诲。

UICollectionViewDataSource

提供集合視圖包含的section(分區(qū))數(shù)量:

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView
{
    return [_dataArray count];
}

提供每個(gè)section包含的item(單元格)數(shù)量:

- (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section
{
    NSArray* sectionArray = [_dataArray objectAtIndex:section];

    return [sectionArray count];
}

根據(jù)IndexPath提供對(duì)應(yīng)的cell:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"reuseIdentifier" forIndexPath:indexPath];

    return cell;
}

根據(jù)IndexPath提供對(duì)應(yīng)的supplementary view,流水布局的supplementary view分為Header和Footer兩種類型:

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    // UICollectionElementKindSectionHeader返回Header檩互,UICollectionElementKindSectionFooter返回Footer
    if ([kind isEqualToString:UICollectionElementKindSectionHeader])
    {
        UICollectionReusableView *supplementaryView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"HeaderReuseIdentifier" forIndexPath:indexPath];

        return supplementaryView;
    }else
    {
        UICollectionReusableView *supplementaryView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"FooterReuseIdentifier" forIndexPath:indexPath];

        return supplementaryView;
    }
}

注意:當(dāng)集合視圖展示的cell數(shù)量較少時(shí)特幔,集合視圖的bounce屬性會(huì)默認(rèn)關(guān)閉,而有時(shí)候我們的頁面需要下拉刷新數(shù)據(jù)的功能闸昨,這時(shí)只需要設(shè)置alwaysBounceVertical屬性設(shè)為YES即可蚯斯。

UICollectionViewDelegate

設(shè)置cell是否能被選中:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

當(dāng)集合視圖的allowsMultipleSelection多選屬性為YES時(shí),設(shè)置是否可以點(diǎn)擊取消選中已被選中的cell:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
    return NO;
}

已選中cell后回調(diào):

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    // 執(zhí)行已選中后所需要的操作
}

已取消選中cell后回調(diào):

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
    // 執(zhí)行已取消選中后所需要的操作
}

設(shè)置cell被選中時(shí)是否支持高亮:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

選中cell時(shí)觸發(fā)高亮后回調(diào)饵较,可以在這里改變cell的背景色:

- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];

    cell.contentView.backgroundColor = [UIColor lightGrayColor];
}

cell被取消選中變?yōu)槠胀顟B(tài)后回調(diào)拍嵌,可以在這里還原cell的背景色:

- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];

    cell.contentView.backgroundColor = [UIColor whiteColor];
}

注意:點(diǎn)擊cell時(shí),cell的狀態(tài)變化過程為:手指接觸屏幕時(shí)循诉,cell狀態(tài)變?yōu)楦吡梁崃荆藭r(shí)cell還未被選中。當(dāng)手指離開屏幕后茄猫,cell狀態(tài)變回到普通狀態(tài)狈蚤,然后cell被集合視圖選中。當(dāng)快速點(diǎn)擊選中cell時(shí)划纽,由于狀態(tài)變化很快炫惩,導(dǎo)致人眼看不出來cell背景色有發(fā)生變化,實(shí)際上是發(fā)生了變化的阿浓。而長(zhǎng)按選中cell時(shí)他嚷,可以看到背景色的變化。

圖2-1

UICollectionViewDelegateFlowLayout

該協(xié)議是對(duì)UICollectionViewDelegate的擴(kuò)展,能夠動(dòng)態(tài)返回cell的大小筋蓖,和cell之間的最小間距等卸耘。

根據(jù)IndexPath返回對(duì)應(yīng)的Cell的大小:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(80.0, 80.0);
}

返回cell到所在section的四周邊界的距離:

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    return UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);
}

根據(jù)Section返回對(duì)應(yīng)的cell之間的行最小間距:

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
    return 10.0;
}

根據(jù)section返回對(duì)應(yīng)的cell之間的列最小間距:

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
    return 10.0;
}

根據(jù)section返回對(duì)應(yīng)的Header大小

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
    return CGSizeMake(collectionView.frame.size.width, 40.0);
}

根據(jù)section返回對(duì)應(yīng)的Footer大小

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
{
    return CGSizeMake(collectionView.frame.size.width, 40.0);
}

cell和supplementary view的重用

視圖的重用避免了不斷生成和銷毀對(duì)象的操作粘咖,提高了程序運(yùn)行的效率蚣抗。要想重用cell和supplementary view,首先需要注冊(cè)cell和supplementary view瓮下,有種三種注冊(cè)方式:

  • 使用storyboard布局時(shí)翰铡,直接拖拽cell或者supplementary view到storyboard中,設(shè)置好重用標(biāo)識(shí)即可讽坏。
  • 使用xib布局時(shí)锭魔,設(shè)置重用標(biāo)識(shí)后,使用registerNib:forCellWithReuseIdentifier:方法來注冊(cè)cell路呜,使用registerNib:forSupplementaryViewOfKind:withReuseIdentifier:方法來注冊(cè)supplementary view迷捧。
  • 使用代碼布局時(shí),使用registerClass:forCellWithReuseIdentifier:方法來注冊(cè)cell胀葱,使用registerClass:forSupplementaryViewOfKind:withReuseIdentifier:方法來注冊(cè)supplementary view漠秋。

注意:使用純代碼自定義cell和supplementary view時(shí),需要重寫initWithFrame:方法抵屿,init方法不會(huì)被調(diào)用庆锦。

dataSource對(duì)象為集合視圖配置cell和supplementary view時(shí),使用dequeueReusableCellWithReuseIdentifier:forIndexPath:方法直接從重用隊(duì)列中取cell轧葛,使用dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath:方法直接從重用隊(duì)列中取supplementary view搂抒。當(dāng)重用隊(duì)列中沒有可復(fù)用的視圖時(shí),會(huì)自動(dòng)幫我們新創(chuàng)建一個(gè)可用的視圖朝群。

cell的插入,刪除和移動(dòng)

插入中符,刪除姜胖,移動(dòng)單個(gè)cell或者某個(gè)section的所有cell時(shí),遵循下面兩個(gè)步驟:

  • 更新數(shù)據(jù)源對(duì)象中的數(shù)據(jù)內(nèi)容淀散。
  • 調(diào)用對(duì)應(yīng)的插入右莱,刪除或者移動(dòng)方法。

集合視圖插入档插,刪除和移動(dòng)cell之前慢蜓,必須先對(duì)應(yīng)更新數(shù)據(jù)源。如果數(shù)據(jù)源沒有更新郭膛,程序運(yùn)行就會(huì)崩潰晨抡。當(dāng)插入,刪除或者移動(dòng)cell時(shí),會(huì)自動(dòng)添加動(dòng)畫效果來反映集合視圖的更改耘柱。在執(zhí)行動(dòng)畫時(shí)如捅,如果還需要同步執(zhí)行其他操作,可以使用performBatchUpdates:completion:方法调煎,在updates block內(nèi)執(zhí)行所有插入镜遣,刪除或移動(dòng)調(diào)用,動(dòng)畫執(zhí)行完畢后會(huì)調(diào)用completion block士袄。

[self.collectionView performBatchUpdates:^{
    // 執(zhí)行更改操作

} completion:^(BOOL finished){

    if (finished)
    {
        // 執(zhí)行其他操作
    }
}];

長(zhǎng)按cell彈出編輯菜單

長(zhǎng)按某個(gè)cell時(shí)悲关,可以彈出一個(gè)編輯菜單,能夠用于剪切娄柳,粘貼寓辱,復(fù)制這個(gè)cell。長(zhǎng)按彈出編輯菜單西土,delegate對(duì)象必須實(shí)現(xiàn)下面3個(gè)委托方法:

是否顯示編輯菜單:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

可以執(zhí)行哪些操作:

- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender
{
    if ([NSStringFromSelector(action) isEqualToString:@"copy:"]|| [NSStringFromSelector(action) isEqualToString:@"paste:"])
    {
        return YES;
    }
    return NO;
}

點(diǎn)擊菜單中選項(xiàng)后回調(diào):

- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender
{
    if ([NSStringFromSelector(action) isEqualToString:@"cut:"])
    {
        // 剪切操作
    }else if ([NSStringFromSelector(action) isEqualToString:@"copy:"])
    {
        // 復(fù)制操作
    }else if ([NSStringFromSelector(action) isEqualToString:@"paste:"])
    {
        // 粘貼操作
    }
}

集合視圖只支持cut:讶舰,copy:paste:三種編輯操作需了。想要了解如何配合剪貼板使用這些操作跳昼,可以參看Text Programming Guide for iOS

切換布局時(shí)的轉(zhuǎn)場(chǎng)動(dòng)畫

切換布局最簡(jiǎn)單的方式是使用setCollectionViewLayout:animated:方法肋乍。在UICollectionViewController之間跳轉(zhuǎn)時(shí)鹅颊,如果需要交互式轉(zhuǎn)場(chǎng)切換布局或者控制切換過程,可以使用UICollectionViewTransitionLayout對(duì)象墓造。

UICollectionViewTransitionLayout類是一種特殊的布局類堪伍,它繼承自UICollectionViewLayout類,在切換到新布局的過程中,它將作為集合視圖的臨時(shí)布局觅闽。使用UICollectionViewTransitionLayout布局對(duì)象時(shí)帝雇,可以使用不同的計(jì)時(shí)算法讓動(dòng)畫遵循非線性路徑,或者根據(jù)傳入的觸摸事件進(jìn)行移動(dòng)蛉拙。官方提供的UICollectionViewTransitionLayout類支持對(duì)新布局的線性轉(zhuǎn)換尸闸,但我們可以對(duì)其進(jìn)行子類化來實(shí)現(xiàn)任何所需的效果。

UICollectionViewLayout提供了幾種跟蹤布局之間轉(zhuǎn)換進(jìn)度的方法孕锄,UICollectionViewTransitionLayout類通過transitionProgress屬性來跟蹤轉(zhuǎn)場(chǎng)切換的進(jìn)度吮廉,當(dāng)轉(zhuǎn)場(chǎng)切換開始后,需要定期更新此屬性值來指示完成的百分比畸肆。使用自定義UICollectionViewTransitionLayout對(duì)象時(shí)宦芦,UICollectionViewTransitionLayout類提供來2種跟蹤與布局相關(guān)的值的方法:updateValue:forAnimatedKey:valueForAnimatedKey:

轉(zhuǎn)場(chǎng)切換布局時(shí)轴脐,使用UICollectionViewTransitionLayout對(duì)象的步驟如下:

  • 使用initWithCurrentLayout:nextLayout:方法創(chuàng)建一個(gè)UICollectionViewTransitionLayout實(shí)例對(duì)象调卑。
  • 定期修改transitionProgress屬性值來指示轉(zhuǎn)場(chǎng)切換的進(jìn)度抡砂。在修改轉(zhuǎn)場(chǎng)進(jìn)度后,一定要調(diào)用invalidateLayout方法來廢棄當(dāng)前布局并更新布局令野。
  • 集合視圖的delegate對(duì)象實(shí)現(xiàn)委托方法collectionView:transitionLayoutForOldLayout:newLayout:返回創(chuàng)建的UICollectionViewTransitionLayout實(shí)例對(duì)象舀患。
  • 可以使用updateValue:forAnimatedKey:方法來修改與布局相關(guān)的值。

進(jìn)階

流水布局

官方提供的UICollectionViewFlowLayout流水布局對(duì)象實(shí)現(xiàn)了基于行的斷開布局气破,單元格被放置在線性路徑上聊浅,并沿著該行放置盡可能多的單元格,當(dāng)前行上的空間在使用最小間距也不足以放置下一個(gè)單元格時(shí)现使,會(huì)重新計(jì)算出合適的當(dāng)前行上擺放的單元格之間的間距低匙,如果該行上只有一個(gè)單元格,那么它會(huì)被置中碳锈,然后會(huì)創(chuàng)建新的一行并在該行重復(fù)之前的布局過程顽冶。

圖3-1
圖3-2

使用時(shí),通過固定單元格的大小和單元格之間的最小間距來實(shí)現(xiàn)網(wǎng)格狀視圖售碳,同時(shí)也可以任意設(shè)置單元格的大小和單元格之間的間距來實(shí)現(xiàn)不規(guī)則排列的視圖强重。當(dāng)單元格的大小,單元格之間的最小間距贸人,單元格到所在分區(qū)四周的邊距以及Header和Footer的大小固定時(shí)间景,可以直接設(shè)置itemSizeminimumLineSpacing艺智,minimumInteritemSpacing倘要,sectionInsetheaderReferenceSize十拣,footerReferenceSize屬性值封拧。如果想要?jiǎng)討B(tài)設(shè)置它們,需要集合視圖的delegate對(duì)象實(shí)現(xiàn)UICollectionViewDelegateFlowLayout協(xié)議的委托方法夭问。

自定義布局

理解布局過程

子類化UICollectionViewLayout實(shí)現(xiàn)自定義布局有兩個(gè)關(guān)鍵任務(wù)需要完成:

  • 指定可滾動(dòng)內(nèi)容區(qū)域的大小泽西。
  • 為每個(gè)單元格和補(bǔ)充視圖提供布局屬性對(duì)象以便集合視圖定位。

集合視圖和自定義布局對(duì)象一起工作來管理整體布局過程缰趋,當(dāng)集合視圖需要用到布局信息時(shí)捧杉,它會(huì)請(qǐng)求布局對(duì)象提供這些布局信息。調(diào)用布局對(duì)象的invalidateLayout方法會(huì)告知集合視圖顯式更新其布局埠胖,此方法會(huì)廢棄現(xiàn)有的布局屬性糠溜,并強(qiáng)制布局對(duì)象生成新的布局屬性淳玩。

不要將布局對(duì)象的invalidateLayout方法與集合視圖的reloadData方法混淆直撤,調(diào)用invalidateLayout方法不一定會(huì)移除當(dāng)前現(xiàn)有的單元格和子視圖,它只會(huì)強(qiáng)制布局對(duì)象重新計(jì)算移動(dòng)蜕着、添加或刪除單元格時(shí)所需的所有布局信息谋竖。如果數(shù)據(jù)源對(duì)象提供的數(shù)據(jù)發(fā)生了更改红柱,則應(yīng)該調(diào)用reloadData方法。使用這兩種方法來更新布局時(shí)蓖乘,實(shí)際的布局過程都是一樣的锤悄。

在布局過程中,集合視圖會(huì)始終按順序來調(diào)用布局對(duì)象的以下三種方法:

  • - (void)prepareLayout
  • - (CGSize)collectionViewContentSize
  • - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect

集合視圖調(diào)用布局對(duì)象的prepareLayout方法嘉抒,提供機(jī)會(huì)讓我們提前計(jì)算確定布局屬性信息時(shí)所需的數(shù)據(jù)零聚,從計(jì)算出來的數(shù)據(jù)中要能夠得知集合視圖整個(gè)內(nèi)容區(qū)域的大小。

集合視圖調(diào)用布局對(duì)象的collectionViewContentSize方法獲得內(nèi)容大小來適當(dāng)?shù)呐渲闷錆L動(dòng)視圖些侍,在這里根據(jù)提前計(jì)算的數(shù)據(jù)返回整個(gè)內(nèi)容區(qū)域的大小隶症。如果內(nèi)容大小在垂直和水平方向上都超出當(dāng)前設(shè)備屏幕的邊界,則會(huì)允許滾動(dòng)視圖同時(shí)在這兩個(gè)方向上滾動(dòng)岗宣,而UICollectionViewFlowLayout只能在一個(gè)方向上滾動(dòng)蚂会。

集合視圖會(huì)基于當(dāng)前的滾動(dòng)位置調(diào)用layoutAttributesForElementsInRect:方法來查找在特定區(qū)域中的單元格和視圖的布局屬性,此區(qū)域和可視區(qū)域可能相同也可能不同耗式,在這里遍歷提前生成的所有的布局屬性信息胁住,檢查每個(gè)布局信息的frame,返回所有frame和給定rect相交的布局屬性刊咳,這樣核心布局過程就完成了彪见。

圖4-1

可以在prepareLayout方法中生成布局屬性對(duì)象后緩存起來,也可以在layoutAttributesForElementsInRect:方法中生成布局屬性對(duì)象芦缰,但是集合視圖在滾動(dòng)過程中會(huì)多次調(diào)用layoutAttributesForElementsInRect:方法企巢,這樣就會(huì)為視圖重復(fù)計(jì)算布局屬性,會(huì)有性能損耗让蕾。

布局完成后浪规,單元格和視圖的布局屬性會(huì)保持不變。調(diào)用布局對(duì)象的invalidateLayout會(huì)廢棄當(dāng)前所有布局信息探孝,然后再次從調(diào)用prepareLayout方法開始笋婿,重復(fù)布局過程生成新的布局信息。集合視圖在滾動(dòng)過程中顿颅,會(huì)不斷調(diào)用布局對(duì)象的shouldInvalidateLayoutForBoundsChange:方法來判斷是否需要廢棄當(dāng)前布局并重新生成布局缸濒。當(dāng)集合視圖的bounds屬性發(fā)生變化時(shí),也會(huì)調(diào)用shouldInvalidateLayoutForBoundsChange:方法粱腻。

調(diào)用invalidateLayout方法后不會(huì)立即開始布局更新過程庇配,該方法僅將布局標(biāo)記為與數(shù)據(jù)不一致并需要更新。在下一個(gè)視圖更新周期中绍些,集合視圖會(huì)檢查其布局是否為臟捞慌,如果是,則更新布局柬批。也就是說啸澡,當(dāng)我們快速連續(xù)地調(diào)用invalidateLayout方法多次后袖订,不會(huì)每次調(diào)用都立即更新布局。

創(chuàng)建布局信息對(duì)象

官方提供了三種方法來創(chuàng)建UICollectionViewLayoutAttributes布局信息對(duì)象:

  • + (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath
  • + (instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath
  • + (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath

要根據(jù)視圖的類型調(diào)用對(duì)應(yīng)的方法來生成布局屬性對(duì)象嗅虏,因?yàn)榧弦晥D會(huì)根據(jù)布局信息對(duì)象的representedElementCategory屬性從數(shù)據(jù)源對(duì)象中獲取對(duì)應(yīng)類型的視圖洛姑,使用錯(cuò)誤的方法生成布局信息對(duì)象會(huì)導(dǎo)致集合視圖在錯(cuò)誤的位置創(chuàng)建錯(cuò)誤的視圖。

生成布局屬性對(duì)象后皮服,一定要根據(jù)前面提前計(jì)算的數(shù)據(jù)設(shè)置好frame或者centersize屬性楞艾,使集合視圖能夠確定對(duì)應(yīng)的視圖的位置和大小。同時(shí)龄广,還可以設(shè)置transform产徊,alphahidden等屬性來控制對(duì)應(yīng)視圖的視覺效果蜀细。如果視圖的布局是重疊的舟铜,則可以設(shè)置zIndex屬性值來確保視圖的順序一致。如果官方提供UICollectionViewLayoutAttributes標(biāo)準(zhǔn)類無法滿足需求奠衔,可以對(duì)其子類化并擴(kuò)展谆刨,以存儲(chǔ)和視圖外觀有關(guān)的信息。當(dāng)對(duì)布局屬性進(jìn)行子類化時(shí)归斤,需要實(shí)現(xiàn)用于比較自定義屬性的isEqual:方法痊夭,因?yàn)榧弦晥D對(duì)其某些操作使用此方法。

根據(jù)需要為單個(gè)視圖提供布局屬性

布局對(duì)象還需要能夠根據(jù)需要為單個(gè)視圖提供布局屬性脏里,因?yàn)榧弦晥D會(huì)在執(zhí)行Cell的插入她我,刪除,移動(dòng)和刷新動(dòng)畫時(shí)請(qǐng)求該布局信息迫横。需要覆寫下面三種方法:

  • -(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
  • -(UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
  • -(UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath

在三種方法中番舆,需要返回已計(jì)算好的對(duì)應(yīng)視圖的布局屬性信息,返回屬性時(shí)矾踱,不應(yīng)更改布局屬性恨狈。如果布局中不包含任何補(bǔ)充視圖和裝飾視圖,則不需要覆寫后兩種方法呛讲。

自定義cell的插入禾怠、刪除、移動(dòng)和刷新動(dòng)畫

集合視圖調(diào)用對(duì)應(yīng)的方法插入贝搁、刪除吗氏、刷新、移動(dòng)cell時(shí)雷逆,布局對(duì)象會(huì)調(diào)用invalidateLayout方法廢棄現(xiàn)有的布局信息弦讽,重新執(zhí)行前面提到的布局過程生成新的布局屬性。在集合視圖更新前調(diào)用prepareForCollectionViewUpdates:方法告知要更新的cell在更新前的indexPath和更新完成后的indexPath关面,以及其要執(zhí)行的更新方式坦袍,需要重寫此方法記錄這些indexPath

之后等太,集合視圖會(huì)執(zhí)行兩個(gè)動(dòng)畫:更新布局前每個(gè)cell被移除的動(dòng)畫和更新布局后每個(gè)cell顯示的動(dòng)畫捂齐,我們看到的動(dòng)畫效果是由這兩個(gè)動(dòng)畫組合而成的。在執(zhí)行動(dòng)畫過程中缩抡,布局對(duì)象會(huì)調(diào)用finalLayoutAttributesForDisappearingItemAtIndexPath:方法獲取對(duì)應(yīng)indexPath的cell被移除時(shí)的最終布局屬性來執(zhí)行動(dòng)畫:更新布局前的布局屬性值-->cell被移除時(shí)的最終布局屬性值奠宜,調(diào)用initialLayoutAttributesForAppearingItemAtIndexPath:方法獲取對(duì)應(yīng)indexPath的cell顯示時(shí)的起始布局屬性來執(zhí)行動(dòng)畫:cell顯示時(shí)的起始布局屬性值-->更新布局后的cell布局屬性值。

插入瞻想、刪除压真、移動(dòng)cell時(shí),會(huì)導(dǎo)致其周圍cell的布局屬性發(fā)生變化蘑险,這些cell會(huì)強(qiáng)制執(zhí)行這個(gè)動(dòng)畫:cell更新布局前的frame-->cell更新布局后的frame滴肿,這是官方在內(nèi)部實(shí)現(xiàn)的。在重寫finalLayoutAttributesForDisappearingItemAtIndexPath:initialLayoutAttributesForAppearingItemAtIndexPath:方法設(shè)置執(zhí)行動(dòng)畫用到的布局屬性時(shí)佃迄,最好檢查一下傳入的indexPath與調(diào)用prepareForCollectionViewUpdates:方法時(shí)記錄的indexPath是否一致泼差。

Demo

示例代碼下載:https://github.com/Jen668/UICollectionViewDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市呵俏,隨后出現(xiàn)的幾起案子堆缘,更是在濱河造成了極大的恐慌,老刑警劉巖普碎,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吼肥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡麻车,警方通過查閱死者的電腦和手機(jī)缀皱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來动猬,“玉大人唆鸡,你說我怎么就攤上這事≡娌欤” “怎么了争占?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)序目。 經(jīng)常有香客問我臂痕,道長(zhǎng),這世上最難降的妖魔是什么猿涨? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任握童,我火速辦了婚禮,結(jié)果婚禮上叛赚,老公的妹妹穿的比我還像新娘澡绩。我一直安慰自己稽揭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布肥卡。 她就那樣靜靜地躺著溪掀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪步鉴。 梳的紋絲不亂的頭發(fā)上揪胃,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音氛琢,去河邊找鬼。 笑死阳似,一個(gè)胖子當(dāng)著我的面吹牛骚勘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撮奏,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼调鲸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了挽荡?” 一聲冷哼從身側(cè)響起藐石,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎定拟,沒想到半個(gè)月后于微,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡青自,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年株依,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片延窜。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恋腕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逆瑞,到底是詐尸還是另有隱情荠藤,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布获高,位于F島的核電站哈肖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏念秧。R本人自食惡果不足惜淤井,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧币狠,春花似錦游两、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至渐行,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铸董,已是汗流浹背祟印。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粟害,地道東北人蕴忆。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像悲幅,于是被迫代替她去往敵國(guó)和親套鹅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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

  • 翻譯自“Collection View Programming Guide for iOS” 0 關(guān)于iOS集合視...
    lakerszhy閱讀 3,859評(píng)論 1 22
  • 父類:NSObject UICollectionViewLayout是抽象基類汰具,你可以使用它的子類來生成對(duì)coll...
    Shmily落墨閱讀 1,759評(píng)論 3 3
  • 最近將 UICollectionView 進(jìn)行了一個(gè)全面的學(xué)習(xí)及總結(jié)卓鹿,參考了網(wǎng)上大量的文章,把官方文檔進(jìn)行了大概翻...
    varlarzh閱讀 21,427評(píng)論 3 94
  • UICollectionView簡(jiǎn)介 UICollectionView是iOS6添加的一個(gè)控件留荔,是一種新的數(shù)據(jù)展示...
    HolyCat_貓閱讀 3,730評(píng)論 1 3
  • UICollectionView是我們常說的集合視圖吟孙,它在iOS 6中引入,是iOS開發(fā)者中最受歡迎的UI元素之一...
    pro648閱讀 10,669評(píng)論 18 44