UICollectionView自定义布局教程:Pinterest (2)
来源:互联网 发布:松下多功能机软件下载 编辑:程序博客网 时间:2024/05/17 03:31
override func prepareLayout() { // 1 if cache.isEmpty { // 2 let columnWidth = contentWidth / CGFloat(numberOfColumns) var xOffset = [CGFloat]() for column in 0 ..< numberOfColumns { xOffset.append(CGFloat(column) * columnWidth ) } var column = 0 var yOffset = [CGFloat](count: numberOfColumns, repeatedValue: 0) // 3 for item in 0 ..< collectionView!.numberOfItemsInSection(0) { let indexPath = NSIndexPath(forItem: item, inSection: 0) // 4 let width = columnWidth - cellPadding * 2 let photoHeight = delegate.collectionView(collectionView!, heightForPhotoAtIndexPath: indexPath, withWidth:width) let annotationHeight = delegate.collectionView(collectionView!, heightForAnnotationAtIndexPath: indexPath, withWidth: width) let height = cellPadding + photoHeight + annotationHeight + cellPadding let frame = CGRect(x: xOffset[column], y: yOffset[column], width: columnWidth, height: height) let insetFrame = CGRectInset(frame, cellPadding, cellPadding) // 5 let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath) attributes.frame = insetFrame cache.append(attributes) // 6 contentHeight = max(contentHeight, CGRectGetMaxY(frame)) yOffset[column] = yOffset[column] + height column = column >= (numberOfColumns - 1) ? 0 : ++column } }}
继续承接上一篇,在prepareLayout()
里的这个计算Item的frame是整个Layout的核心,所以必须要每一段话都读懂才能领会UICollectionViewLayout的核心.
var column = 0
:这个变量是干吗的呢?我们上文贴过这么一张图.
我解释过,在这个布局中,所谓列
这个概念是人工加上去的.在CollectionView中,没有所谓的竖排的这种列,只有Section和Item(也就是Row).而排列方式总是从左到右,一行排满了到下一行这种方式.
而我们的第二个Item的Y轴依赖于第0个Item的高度,所以,你也可以看做是第偶数个Item的Y轴总是和上一个偶数Item的高度相关.奇数的Item也类似.
那么,作者又是怎么计算的呢?
他采取了另外一种方式,声明了一个yOffset
数组,只有两个数值.yOffset[0]记录第一列的上一个Item的height,yOffset[1]记录第二列的上一个Item的height.
当for循环走到第一列的Item(也就是index为偶数的Item)的时候他就去yOffset[0]里去取,走到第二列的Item(也就是index为奇数的Item)的时候就去yOffset[1]里去拿.
column就是作者用来判别到底当前循环走到的Item是第一列还是第二列的.
不信?看这句column = column >= (numberOfColumns - 1) ? 0 : ++column
我们来走一下,column声明时赋值为0.第一个for循环走完之后,这个判断是column(现在是0) >= (numberOfColumns(始终为2,因为是定死的只有两列) - 1) ? 0: ++column
所以第一遍循环完了之后column变成1了,说明第二次要从yOffset[1]里取值了.看懂了么?然后每次循环的时候,column就在0和1之间变啊变.
其实看懂了这一句,其他的就没什么难度了,无非是从Delegate里拿到图片高度,文字标题高度,文字内容高度,加起来,就是每一个Item的height,然后依据这个Item所在的是第一列,给放到yOffset数组里就行了.
最后把每一个LayoutAttributes存到数组里,用来以后判断和读取用.以后就不需要再计算了.
返回布局的ContentSize
在for循环里,我们每执行一次之后都要做一次这个判断.contentHeight = max(contentHeight, CGRectGetMaxY(frame))
其实cotnentSize的height不就是最后的那个Cell的CGRectGetMaxY(frame).所以可以这么计算.
override func collectionViewContentSize() -> CGSize { return CGSize(width: contentWidth, height: contentHeight)}
重写layoutAttributesForElementsInRect(_:)
override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? { var layoutAttributes = [UICollectionViewLayoutAttributes]() for attributes in cache { if CGRectIntersectsRect(attributes.frame, rect) { layoutAttributes.append(attributes) } } return layoutAttributes}
这个方法的含义我上一篇已经讲过了,就是每次CollectionView滚动到某个区域的时候,CollectionView需要知道这个区域里的每个Cell的layoutAttributes.
那我们就用CGRectIntersectsRect这个方法遍历判断有哪些attributes的frame在这个区域里,然后返回给collectionView就行了.
一个获取Photo的height的代理方法
func collectionView(collectionView:UICollectionView, heightForPhotoAtIndexPath indexPath:NSIndexPath, withWidth width:CGFloat) -> CGFloat { let photo = photos[indexPath.item] let boundingRect = CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT)) let rect = AVMakeRectWithAspectRatioInsideRect(photo.image.size, boundingRect) return rect.size.height }
这个代理方法是计算Image在contentMode为aspectFit的UIImageView里压缩过之后,如何计算高度的.如果看不懂的可以看武蕴牛X(这个ID真的狂拽炫酷叼霸天)的这篇blog.
最后一步,修改我们的UICollectionViewCell中的UIImageView的height这个constraint
override func applyLayoutAttributes(layoutAttributes: UICollectionViewLayoutAttributes!) { super.applyLayoutAttributes(layoutAttributes) if let attributes = layoutAttributes as? PinterestLayoutAttributes { imageViewHeightLayoutConstraint.constant = attributes.photoHeight }}
就是重写CollectionViewCell的这个方法,在每次给我们的Cell赋layoutAttributes之后,拿到我们的Photo高度,然后把cell的imageViewHeightLayoutConstraint拉一条线,更改Constant属性为layoutAttributes中的photoHeight就行了.
完结的项目在这里
下一期,带领大家做一个英语流利说的这种布局效果的Layout.
- UICollectionView自定义布局教程:Pinterest (2)
- UICollectionView 自定义布局教程: Pinterest
- UICollectionView自定义布局教程:Pinterest (1)(翻译自raywenderlich)
- UICollectionView之自定义布局
- 自定义UICollectionView布局-入门
- 自定义UICollectionView的布局
- UICollectionView自定义布局
- UICollectionView自定义布局
- UICollectionView自定义流水布局
- UICollectionView自定义布局类
- UICollectionView自定义布局
- 自定义UICollectionView布局
- 自定义UICollectionView布局
- UICollectionView的自定义布局
- 自定义UICollectionView布局-线性布局
- UICollectionView自定义Layout教程
- UICollectionView(二)自定义布局UICollectionViewLayout
- UICollectionView详解之自定义布局
- 软件测试理论知识总结
- iOS设备中WiFi、蓝牙和飞行模式的开启与关闭
- 程序员必须知道的10大基础实用算法及其讲解
- NSCFString与NSString类型的字符串导致的崩溃问题
- iOS系统自带毛玻璃效果
- UICollectionView自定义布局教程:Pinterest (2)
- C/C++字符串
- VC程序运行时间测试函数
- OpenCV中Mat的详解
- ThinkPHP函数详解:D方法
- HTTP响应头和请求头信息对照表
- MyString类的实现--C++ primer plus 读书笔记
- jQuery UI resizable bug
- iOS 导出Private API