在我們的日歷示例中,我們想要視圖垂直的滾動。比如,如果我們想要在垂直空間上一個小時占去100點,這樣顯示一整天的內容高度就是2400點。注意,我們不能夠水平滾動,這就意味這我們collection view只能顯示一周。為了能夠在日歷中的多個星期間分頁,我們可以在一個獨立(分頁)的scroll view(可以使用UipageViewController)中使用多個collection view(一周一個),或者堅持使用一個collection view并且返回足夠大的內容寬度,這會使得用戶感覺在兩個方向上滑動自由。- (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; } 為了清楚起見,我選擇布局在一個非常簡單模型上:假定每周天數相同,每天時長相同, 也就是說天數用0-6表示。在一個真實的日歷程序中,布局將會為自己的計算大量使用基于NSCalendar的日期。 layoutAttributesForElementsInRect:這是任何布局類中最重要的方法了,同時可能也是最容易讓人迷惑的方法。collection view調用這個方法并傳遞一個自身坐標系統中的矩形過去。這個矩形代表了這個視圖的可見矩形區域(也就是它的bounds),你需要準備好處理傳給你的任何矩形。 你的實現必須返回一個包含UICollectionViewLayoutAttributes對象的數組,為每一個cell包含這樣的一個對象,supplementary view或decoration view在矩形區域內是可見的。UICollectionViewLayoutAttributes類包含了collection view內item的所有相關布局屬性。默認情況下,這個類包含frame,center,size,transform3D,alpha,zIndex屬性(PRoperties),和hidden特性(attributes)。如果你的布局想要控制其他視圖的屬性(比如,背景顏色),你可以建一個UICollectionViewLayoutAttributes的子類,然后加上你自己的屬性。 布局屬性對象通過indexPath屬性和他們對應的cell,supplementary view或者decoration view關聯在一起。collection view為所有items從布局對象中請求到布局屬性后,它將會實例化所有視圖,并將對應的屬性應用到每個視圖上去。 注意!這個方法涉及到所有類型的視圖,也就是cell,supplementary views和decoration views。一個幼稚的實現可能會選擇忽略傳入的矩形,并且為collection view中的所有視圖返回布局屬性。在原型設計和開發布局階段,這是一個有效的方法。但是,這將對性能產生非常壞的影響,特別是可見cell遠少于所有cell數量的時候,collection view和布局對象將會為那些不可見的視圖做額外不必要的工作。 你的實現需要做這幾步: 1.創建一個空的mutable數組來存放所有的布局屬性。 2.確定index paths中哪些cells的frame完全或部分位于矩形中。這個計算需要你從collection view的數據源中取出你需要顯示的數據。然后在循環中調用你實現的layoutAttributesForItemAtIndexPath:方法為每個index path創建并配置一個合適的布局屬性對象,并將每個對象添加到數組中。 3.如果你的布局包含supplementary views,計算矩形內可見supplementary view的index paths。在循環中調用你實現的layoutAttributesForSupplementaryViewOfKind:atIndexPath:,并且將這些對象加到數組中。通過為kind參數傳遞你選擇的不同字符,你可以區分出不同種類的supplementary views(比如headers和footers)。當需要創建視圖時,collection view會將kind字符傳回到你的數據源。記住supplementary和decoration views的數量和種類完全由布局控制。你不會受到headers和footers的限制。 4.如果布局包含decoration views,計算矩形內可見decoration views的index paths。在循環中調用你實現的layoutAttributesForDecorationViewOfKind:atIndexPath:,并且將這些對象加到數組中。 5.返回數組。 我們自定義的布局沒有使用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 =