在使用自定义UICollectionViewLayout的时候有两个方法可以返回自定义的UICollectionViewLayoutAttributes:
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
返回rect中的所有的元素的布局属性
和
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
返回对应于indexPath的位置的cell的布局属性
那么问题来了,这两个方法到底区别是什么?我看了一些自定义布局的例子,有的重写了两个方法,而有的只重写了第二个方法,比如说下面的这个例子
import UIKit
protocol PinterestLayoutDelegate {
func collectionView(collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath: NSIndexPath, withWidth width: CGFloat) -> CGFloat
func collectionView(collectionView: UICollectionView, heightForAnnotationAtIndexPath indexPath: NSIndexPath, withWidth width: CGFloat) -> CGFloat
}
class PinterestLayoutAttributes: UICollectionViewLayoutAttributes {
var photoHeight: CGFloat = 0
override func copyWithZone(zone: NSZone) -> AnyObject {
let copy = super.copyWithZone(zone) as! PinterestLayoutAttributes
copy.photoHeight = photoHeight
return copy
}
override func isEqual(object: AnyObject?) -> Bool {
if let attributes = object as? PinterestLayoutAttributes {
if attributes.photoHeight == photoHeight {
return super.isEqual(object)
}
}
return false
}
}
class PinterestLayout: UICollectionViewLayout {
var cellPadding: CGFloat = 0
var delegate: PinterestLayoutDelegate!
var numberOfColumns = 2
private var cache = [PinterestLayoutAttributes]()
private var contentHeight: CGFloat = 0
private var width: CGFloat {
get {
let insets = collectionView!.contentInset
return CGRectGetWidth(collectionView!.bounds) - (insets.left + insets.right)
}
}
override class func layoutAttributesClass() -> AnyClass {
return PinterestLayoutAttributes.self
}
override func collectionViewContentSize() -> CGSize {
return CGSize(width: width, height: contentHeight)
}
override func prepareLayout() {
if cache.isEmpty {
let columnWidth = width / CGFloat(numberOfColumns)
var xOffsets = [CGFloat]()
for column in 0..<numberOfColumns {
xOffsets.append(CGFloat(column) * columnWidth)
}
var yOffsets = [CGFloat](count: numberOfColumns, repeatedValue: 0)
var column = 0
for item in 0..<collectionView!.numberOfItemsInSection(0) {
let indexPath = NSIndexPath(forItem: item, inSection: 0)
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: xOffsets[column], y: yOffsets[column], width: columnWidth, height: height)
let insetFrame = CGRectInset(frame, cellPadding, cellPadding)
let attributes = PinterestLayoutAttributes(forCellWithIndexPath: indexPath)
attributes.frame = insetFrame
attributes.photoHeight = photoHeight
cache.append(attributes)
contentHeight = max(contentHeight, CGRectGetMaxY(frame))
yOffsets[column] = yOffsets[column] + height
column = column >= (numberOfColumns - 1) ? 0 : ++column
}
}
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for attributes in cache {
if CGRectIntersectsRect(attributes.frame, rect) {
layoutAttributes.append(attributes)
}
}
return layoutAttributes
}
}
这个自定义PinterestLayout中,PinterestLayoutAttributes(UICollectionViewLayoutAttributes的子类)是创建在preparelayout中而不是在layoutAttributesForItemAtIndexPath中的,用一个cache的数组保存起来,然后layoutAttributesForElementsInRect返回这个数组,那么为什么这个例子中layoutAttributesForItemAtIndexPath没有被用到呢?我被这两个方法搞糊涂了。
layoutAttributesForElementsInRect:的rect是可见部分的rect;layoutAttributesForItemAtIndexPath:是对于任意indexpath而言的。前面一个的实现往往依赖于后者,根据rect取得可见的indexpath,再根据后者取得layout attributes,加入数组,然后返回。