鍍金池/ 教程/ iOS/ 自定義 Collection View 布局
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開發(fā)
Collection View 動(dòng)畫
截圖測(cè)試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動(dòng)布局工具箱
動(dòng)畫
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動(dòng)畫解釋
響應(yīng)式 Android 應(yīng)用
初識(shí) TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測(cè)試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動(dòng)游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動(dòng)畫
常見的后臺(tái)實(shí)踐
糟糕的測(cè)試
避免濫用單例
數(shù)據(jù)模型和模型對(duì)象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場(chǎng)
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測(cè)試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測(cè)試
值對(duì)象
活動(dòng)追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測(cè)試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場(chǎng)
游戲
調(diào)試核對(duì)清單
View Controller 容器
學(xué)無止境
XCTest 測(cè)試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動(dòng)畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲(chǔ)
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫(kù)支持
Fetch 請(qǐng)求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語(yǔ)言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

自定義 Collection View 布局

UICollectionView 在 iOS6 中第一次被引入,也是 UIKit 視圖類中的一顆新星。它和 UITableView 共享一套 API 設(shè)計(jì),但也在 UITableView 上做了一些擴(kuò)展。UICollectionView 最強(qiáng)大、同時(shí)顯著超出 UITableView 的特色就是其完全靈活的布局結(jié)構(gòu)。在這篇文章中,我們將會(huì)實(shí)現(xiàn)一個(gè)相當(dāng)復(fù)雜的自定義 collection view 布局,并且順便討論一下這個(gè)類設(shè)計(jì)的重要部分。項(xiàng)目的示例代碼在 GitHub 上。

布局對(duì)象 (Layout Objects)

UITableView 和 UICollectionView 都是 data-source 和 delegate 驅(qū)動(dòng)的。它們?cè)陲@示其子視圖集的過程中僅扮演容器角色(dumb containers),且對(duì)子視圖集真正的內(nèi)容毫不知情。

UICollectionView 在此之上進(jìn)行了進(jìn)一步抽象。它將其子視圖的位置,大小和外觀的控制權(quán)委托給一個(gè)單獨(dú)的布局對(duì)象。通過提供一個(gè)自定義布局對(duì)象,你幾乎可以實(shí)現(xiàn)任何你能想象到的布局。布局繼承自 UICollectionViewLayout 抽象基類。iOS6 中以 UICollectionViewFlowLayout 類的形式提出了一個(gè)具體的布局實(shí)現(xiàn)。

我們可以使用 flow layout 實(shí)現(xiàn)一個(gè)標(biāo)準(zhǔn)的 grid view,這可能是在 collection view 中最常見的使用案例了。盡管大多數(shù)人都這么想,但是 Apple 很聰明,沒有明確的命名這個(gè)類為 UICollectionViewGridLayout,而使用了更為通用的術(shù)語(yǔ) flow layout,更好的描述了該類的功能:它通過一個(gè)接一個(gè)的放置 cell 來建立自己的布局,當(dāng)需要的時(shí)候,插入橫排或豎排的分欄符。通過自定義滾動(dòng)方向,大小和 cell 之間的間距,flow layout 也可以在單行或單列中布局 cell。實(shí)際上,UITableView 的布局可以想象成 flow layout 的一種特殊情況。

在你準(zhǔn)備自己寫一個(gè) UICollectionViewLayout 的子類之前,你需要問你自己,你是否能夠使用 UICollectionViewFlowLayout 實(shí)現(xiàn)你心里的布局。這個(gè)類是很容易定制的,并且可以繼承本身進(jìn)行近一步的定制。感興趣的看這篇文章

Cells 和其他 Views

為了適應(yīng)任意布局,collection view 建立一個(gè)了類似、但比 table view 更靈活的視圖層級(jí)(view hierarchy)。像往常一樣,你的主要內(nèi)容顯示在 cell 中,cell 可以被任意分組到 section 中。Collection view 的 cell 必須是 UICollectionViewCell 的子類。除了 cell,collection view 額外管理著兩種視圖:supplementary views 和 decoration views。

collection view 中的 Supplementary views 相當(dāng)于 table view 的 section header 和 footer views。像 cells 一樣,他們的內(nèi)容都由數(shù)據(jù)源對(duì)象驅(qū)動(dòng)。然而和 table view 中用法不一樣,supplementary view 并不一定會(huì)作為 header 或 footer view;他們的數(shù)量和放置的位置完全由布局控制。

Decoration views 純粹為一個(gè)裝飾品。他們完全屬于布局對(duì)象,并被布局對(duì)象管理,他們并不從 data source 獲取的 contents。當(dāng)布局對(duì)象指定需要一個(gè) decoration view 的時(shí)候,collection view 會(huì)自動(dòng)創(chuàng)建,并將布局對(duì)象提供的布局參數(shù)應(yīng)用到上面去。并不需要為自定義視圖準(zhǔn)備任何內(nèi)容。

Supplementary views 和 decoration views 必須是 UICollectionReusableView 的子類。布局使用的每個(gè)視圖類都需要在 collection view 中注冊(cè),這樣當(dāng) data source 讓它們從 reuse pool 中出列時(shí),它們才能夠創(chuàng)建新的實(shí)例。如果你是使用的 Interface Builder,則可以通過在可視編輯器中拖拽一個(gè) cell 到 collection view 上完成 cell 在 collection view 中的注冊(cè)。同樣的方法也可以用在 supplementary view 上,前提是你使用了 UICollectionViewFlowLayout。如果沒有,你只能通過調(diào)用 registerClass: 或者 registerNib: 方法手動(dòng)注冊(cè)視圖類了。你需要在 viewDidLoad 中做這些操作。

自定義布局

作為一個(gè)非常有意義的自定義 collection view 布局的例子,我們不妨設(shè)想一個(gè)典型的日歷應(yīng)用程序中的周 (week) 視圖。日歷一次顯示一周,星期中的每一天顯示在列中。每一個(gè)日歷事件將會(huì)在我們的 collection view 中以一個(gè) cell 顯示,位置和大小代表事件起始日期時(shí)間和持續(xù)時(shí)間。

http://wiki.jikexueyuan.com/project/objc/images/3-10.png" alt="" /> 一般有兩種類型的 collection view 布局:

1.獨(dú)立于內(nèi)容的布局計(jì)算。這正是你所知道的像 UITableView 和 UICollectionViewFlowLayout 這些情況。每個(gè) cell 的位置和外觀不是基于其顯示的內(nèi)容,但所有 cell 的顯示順序是基于內(nèi)容的順序??梢园涯J(rèn)的 flow layout 做為例子。每個(gè) cell 都基于前一個(gè) cell 放置(或者如果沒有足夠的空間,則從下一行開始)。布局對(duì)象不必訪問實(shí)際數(shù)據(jù)來計(jì)算布局。

2.基于內(nèi)容的布局計(jì)算。我們的日歷視圖正是這樣類型的例子。為了計(jì)算顯示事件的起始和結(jié)束時(shí)間,布局對(duì)象需要直接訪問 collection view 的數(shù)據(jù)源。在很多情況下,布局對(duì)象不僅需要取出當(dāng)前可見 cell 的數(shù)據(jù),還需要從所有記錄中取出一些決定當(dāng)前哪些 cell 可見的數(shù)據(jù)。

在我們的日歷示例中,布局對(duì)象如果訪問某一個(gè)矩形內(nèi) cells 的屬性,那就必須迭代數(shù)據(jù)源提供的所有事件來決定哪些位于要求的時(shí)間窗口中。 與一些相對(duì)簡(jiǎn)單,數(shù)據(jù)源獨(dú)立計(jì)算的 flow layout 比起來,這足夠計(jì)算出 cell 在一個(gè)矩形內(nèi)的 index paths 了(假設(shè)網(wǎng)格中所有cells的大小都一樣)。

如果有一個(gè)依賴內(nèi)容的布局,那就是暗示你需要寫自定義的布局類了,同時(shí)不能使用自定義的 UICollectionViewFlowLayout,所以這正是我們需要做的事情。

UICollectionViewLayout的文檔列出了子類需要重寫的方法。

collectionViewContentSize

由于 collection view 對(duì)它的 content 并不知情,所以布局首先要提供的信息就是滾動(dòng)區(qū)域大小,這樣 collection view 才能正確的管理滾動(dòng)。布局對(duì)象必須在此時(shí)計(jì)算它內(nèi)容的總大小,包括 supplementary views 和 decoration views。注意,盡管大多數(shù)經(jīng)典的 collection view 限制在一個(gè)軸方向上滾動(dòng)(正如 UICollectionViewFlowLayout 一樣),但這不是必須的。

在我們的日歷示例中,我們想要視圖垂直的滾動(dòng)。比如,如果我們想要在垂直空間上一個(gè)小時(shí)占去 100 點(diǎn),這樣顯示一整天的內(nèi)容高度就是 2400 點(diǎn)。注意,我們不能夠水平滾動(dòng),這就意味這我們 collection view 只能顯示一周。為了能夠在日歷中的多個(gè)星期間分頁(yè),我們可以在一個(gè)獨(dú)立(分頁(yè))的 scroll view (可以使用 UIPageViewController)中使用多個(gè)collection view(一周一個(gè)),或者堅(jiān)持使用一個(gè) collection view 并且返回足夠大的內(nèi)容寬度,這會(huì)使得用戶感覺在兩個(gè)方向上滑動(dòng)自由。

- (CGSize)collectionViewContentSize
{
    // Don't scroll horizontally
    CGFloat contentWidth = self.collectionView.bounds.size.width;

    // Scroll vertically to display a full day
    CGFloat contentHeight = DayHeaderHeight + (HeightPerHour * HoursPerDay);

    CGSize contentSize = CGSizeMake(contentWidth, contentHeight);
    return contentSize;
}

為了清楚起見,我選擇布局在一個(gè)非常簡(jiǎn)單的模型上:假定每周天數(shù)相同,每天時(shí)長(zhǎng)相同,也就是說天數(shù)用 0-6 表示。在一個(gè)真實(shí)的日歷程序中,布局將會(huì)為自己的計(jì)算大量使用基于 NSCalendaar 的日期。

layoutAttributesForElementsInRect:

這是任何布局類中最重要的方法了,同時(shí)可能也是最容易讓人迷惑的方法。collection view 調(diào)用這個(gè)方法并傳遞一個(gè)自身坐標(biāo)系統(tǒng)中的矩形過去。這個(gè)矩形代表了這個(gè)視圖的可見矩形區(qū)域(也就是它的 bounds ),你需要準(zhǔn)備好處理傳給你的任何矩形。

你的實(shí)現(xiàn)必須返回一個(gè)包含 UICollectionViewLayoutAttributes 對(duì)象的數(shù)組,為每一個(gè) cell 包含一個(gè)這樣的對(duì)象,supplementary view 或 decoration view 在矩形區(qū)域內(nèi)是可見的。UICollectionViewLayoutAttributes 類包含了 collection view 內(nèi) item 的所有相關(guān)布局屬性。默認(rèn)情況下,這個(gè)類包含 frame,center,size,transform3Dalpha,zIndexhidden屬性。如果你的布局想要控制其他視圖的屬性(比如背景顏色),你可以建一個(gè) UICollectionViewLayoutAttributes 的子類,然后加上你自己的屬性。

布局屬性對(duì)象 (layout attributes objects) 通過 indexPath 屬性和他們對(duì)應(yīng)的 cell,supplementary view 或者 decoration view 關(guān)聯(lián)在一起。collection view 為所有 items 從布局對(duì)象中請(qǐng)求到布局屬性后,它將會(huì)實(shí)例化所有視圖,并將對(duì)應(yīng)的屬性應(yīng)用到每個(gè)視圖上去。

注意!這個(gè)方法涉及到所有類型的視圖,也就是 cell,supplementary views 和 decoration views。一個(gè)幼稚的實(shí)現(xiàn)可能會(huì)選擇忽略傳入的矩形,并且為 collection view 中的所有視圖返回布局屬性。在原型設(shè)計(jì)和開發(fā)布局階段,這是一個(gè)有效的方法。但是,這將對(duì)性能產(chǎn)生非常壞的影響,特別是可見 cell 遠(yuǎn)少于所有 cell 數(shù)量的時(shí)候,collection view 和布局對(duì)象將會(huì)為那些不可見的視圖做額外不必要的工作。

你的實(shí)現(xiàn)需要做這幾步:

  1. 創(chuàng)建一個(gè)空的可變數(shù)組來存放所有的布局屬性。

  2. 確定 index paths 中哪些 cells 的 frame 完全或部分位于矩形中。這個(gè)計(jì)算需要你從 collection view 的數(shù)據(jù)源中取出你需要顯示的數(shù)據(jù)。然后在循環(huán)中調(diào)用你實(shí)現(xiàn)的 layoutAttributesForItemAtIndexPath: 方法為每個(gè) index path 創(chuàng)建并配置一個(gè)合適的布局屬性對(duì)象,并將每個(gè)對(duì)象添加到數(shù)組中。

  3. 如果你的布局包含 supplementary views,計(jì)算矩形內(nèi)可見 supplementary view 的 index paths。在循環(huán)中調(diào)用你實(shí)現(xiàn)的 layoutAttributesForSupplementaryViewOfKind:atIndexPath: ,并且將這些對(duì)象加到數(shù)組中。通過為 kind 參數(shù)傳遞你選擇的不同字符,你可以區(qū)分出不同種類的supplementary views(比如headers和footers)。當(dāng)需要?jiǎng)?chuàng)建視圖時(shí),collection view 會(huì)將 kind 字符傳回到你的數(shù)據(jù)源。記住 supplementary 和 decoration views 的數(shù)量和種類完全由布局控制。你不會(huì)受到 headers 和 footers 的限制。

  4. 如果布局包含 decoration views,計(jì)算矩形內(nèi)可見 decoration views 的 index paths。在循環(huán)中調(diào)用你實(shí)現(xiàn)的 layoutAttributesForDecorationViewOfKind:atIndexPath: ,并且將這些對(duì)象加到數(shù)組中。

  5. 返回?cái)?shù)組。

我們自定義的布局沒有使用 decoration views,但是使用了兩種 supplementary views(column headers和row headers):

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *layoutAttributes = [NSMutableArray array];
    // Cells
    // We call a custom helper method -indexPathsOfItemsInRect: here
    // which computes the index paths of the cells that should be included
    // in rect.
    NSArray *visibleIndexPaths = [self indexPathsOfItemsInRect:rect];
    for (NSIndexPath *indexPath in visibleIndexPaths) {
        UICollectionViewLayoutAttributes *attributes =
        [self layoutAttributesForItemAtIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }

    // Supplementary views
    NSArray *dayHeaderViewIndexPaths = [self indexPathsOfDayHeaderViewsInRect:rect];
    for (NSIndexPath *indexPath in dayHeaderViewIndexPaths) {
        UICollectionViewLayoutAttributes *attributes =
        [self layoutAttributesForSupplementaryViewOfKind:@"DayHeaderView"
atIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }

    NSArray *hourHeaderViewIndexPaths = [self indexPathsOfHourHeaderViewsInRect:rect];
    for (NSIndexPath *indexPath in hourHeaderViewIndexPaths) {
        UICollectionViewLayoutAttributes *attributes =
        [self layoutAttributesForSupplementaryViewOfKind:@"HourHeaderView"
atIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }
    return layoutAttributes;
}

layoutAttributesFor…IndexPath

有時(shí),collection view 會(huì)為某個(gè)特殊的 cell,supplementary 或者 decoration view 向布局對(duì)象請(qǐng)求布局屬性,而非所有可見的對(duì)象。這就是當(dāng)其他三個(gè)方法開始起作用時(shí),你實(shí)現(xiàn)的 layoutAttributesForItemAtIndexPath: 需要?jiǎng)?chuàng)建并返回一個(gè)單獨(dú)的布局屬性對(duì)象,這樣才能正確的格式化傳給你的 index path 所對(duì)應(yīng)的 cell。

你可以通過調(diào)用 +[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:]這個(gè)方法,然后根據(jù) index path 修改屬性。為了得到需要顯示在這個(gè) index path 內(nèi)的數(shù)據(jù),你可能需要訪問 collection view 的數(shù)據(jù)源。到目前為止,至少確保設(shè)置了 frame 屬性,除非你所有的 cell 都位于彼此上方。

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CalendarDataSource *dataSource = self.collectionView.dataSource;
    id event = [dataSource eventAtIndexPath:indexPath];
    UICollectionViewLayoutAttributes *attributes =
    [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attributes.frame = [self frameForEvent:event];
    return attributes;
}

如果你正在使用自動(dòng)布局,你可能會(huì)感到驚訝,我們正在直接修改布局參數(shù)的 frame 屬性,而不是和約束共事,但這正是 UICollectionViewLayout 的工作。盡管你可能使用自動(dòng)布局來定義collection view 的 frame 和它內(nèi)部每個(gè) cell 的布局,但 cells 的 frames 還是需要通過老式的方法計(jì)算出來。

類似的,layoutAttributesForSupplementaryViewOfKind:atIndexPath:layoutAttributesForDecorationViewOfKind:atIndexPath: 方法分別需要為 supplementary 和 decoration views 做相同的事。只有你的布局包含這樣的視圖你才需要實(shí)現(xiàn)這兩個(gè)方法。UICollectionViewLayoutAttributes 包含另外兩個(gè)工廠方法,+layoutAttributesForSupplementaryViewOfKind:withIndexPath:+layoutAttributesForDecorationViewOfKind:withIndexPath:,用他們來創(chuàng)建正確的布局屬性對(duì)象。

shouldInvalidateLayoutForBoundsChange:

最后,當(dāng) collection view 的 bounds 改變時(shí),布局需要告訴 collection view 是否需要重新計(jì)算布局。我的猜想是:當(dāng) collection view 改變大小時(shí),大多數(shù)布局會(huì)被作廢,比如設(shè)備旋轉(zhuǎn)的時(shí)候。因此,一個(gè)幼稚的實(shí)現(xiàn)可能只會(huì)簡(jiǎn)單的返回 YES。雖然實(shí)現(xiàn)功能很重要,但是 scroll view 的 bounds 在滾動(dòng)時(shí)也會(huì)改變,這意味著你的布局每秒會(huì)被丟棄多次。根據(jù)計(jì)算的復(fù)雜性判斷,這將會(huì)對(duì)性能產(chǎn)生很大的影響。

當(dāng) collection view 的寬度改變時(shí),我們自定義的布局必須被丟棄,但這滾動(dòng)并不會(huì)影響到布局。幸運(yùn)的是,collection view 將它的新 bounds 傳給 shouldInvalidateLayoutForBoundsChange: 方法。這樣我們便能比較視圖當(dāng)前的bounds 和新的 bounds 來確定返回值:

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    CGRect oldBounds = self.collectionView.bounds;
    if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds)) {
        return YES;
    }
        return NO;
}

動(dòng)畫

插入和刪除

UITableView 中的 cell 自帶了一套非常漂亮的插入和刪除動(dòng)畫。但是當(dāng)為 UICollectionView 增加和刪除 cell 定義動(dòng)畫功能時(shí),UIKit 工程師遇到這樣一個(gè)問題:如果 collection view 的布局是完全可變的,那么預(yù)先定義好的動(dòng)畫就沒辦法和開發(fā)者自定義的布局很好的融合。他們提出了一個(gè)優(yōu)雅的方法:當(dāng)一個(gè) cell (或者supplementary或者decoration view)被插入到 collection view 中時(shí),collection view 不僅向其布局請(qǐng)求 cell 正常狀態(tài)下的布局屬性,同時(shí)還請(qǐng)求其初始的布局屬性,比如,需要在開始有插入動(dòng)畫的 cell。collection view 會(huì)簡(jiǎn)單的創(chuàng)建一個(gè) animation block,并在這個(gè) block 中,將所有 cell 的屬性從初始(initial)狀態(tài)改變到常態(tài)(normal)。

通過提供不同的初始布局屬性,你可以完全自定義插入動(dòng)畫。比如,設(shè)置初始的 alpha 為 0 將會(huì)產(chǎn)生一個(gè)淡入的動(dòng)畫。同時(shí)設(shè)置一個(gè)平移和縮放將會(huì)產(chǎn)生移動(dòng)縮放的效果。

同樣的原理應(yīng)用到刪除上,這次動(dòng)畫是從常態(tài)到一系列你設(shè)置的最終布局屬性。這些都是你需要在布局類中為initial或final布局參數(shù)實(shí)現(xiàn)的方法.

  • initialLayoutAttributesForAppearingItemAtIndexPath:

  • initialLayoutAttributesForAppearingSupplementaryElementOfKind:atIndexPath:

  • initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:

  • finalLayoutAttributesForDisappearingItemAtIndexPath:

  • finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:

  • finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:

布局間切換

可以通過類似的方式將一個(gè) collection view 布局動(dòng)態(tài)的切換到另外一個(gè)布局。當(dāng)發(fā)送一個(gè) setCollectionViewLayout:animated: 消息時(shí),collection view 會(huì)為 cells 在新的布局中查詢新的布局參數(shù),然后動(dòng)態(tài)的將每個(gè) cell(通過index path在新舊布局中判斷出相同的cell)從舊參數(shù)變換到新的布局參數(shù)。你不需要做任何事情。

結(jié)論

根據(jù)自定義 collection view 布局的復(fù)雜性,寫一個(gè)通常很不容易。確切的說,本質(zhì)上這和從頭寫一個(gè)完整的實(shí)現(xiàn)相同布局自定義視圖類一樣困難了。因?yàn)樗婕暗挠?jì)算需要確定哪些子視圖當(dāng)前是可見的,以及它們的位置。盡管如此,使用 UICollectionView 還是給你帶來了一些很好的效果,比如 cell 重用,自動(dòng)支持動(dòng)畫,更不要提整潔的獨(dú)立布局,子視圖管理,以及數(shù)據(jù)提供架構(gòu)規(guī)定(data preparation its architecture prescribes.)。

自定義 collection view 布局也是向輕量級(jí) view controller 邁出很好的一步,正如你的 view controller 不要包含任何布局代碼。正如 Chris 的文章中解釋的一樣,將這一切和一個(gè)獨(dú)立的 datasource 類結(jié)合在一起,collection view 的視圖控制器將很難再包含任何代碼。

每當(dāng)我使用 UICollectionView 的時(shí)候,我被其簡(jiǎn)潔的設(shè)計(jì)所折服。對(duì)于一個(gè)有經(jīng)驗(yàn)的 Apple 工程師,為了想出如此靈活的類,很可能需要首先考慮 NSTableViewUITableView。

擴(kuò)展閱讀