在我们的日历示例中,我们想要视图垂直的滚动。比如,如果我们想要在垂直空间上一个小时占去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 =