在使用自定义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没有被用到呢?我被这两个方法搞糊涂了。
The rect of layoutAttributesForElementsInRect: is the rect of the visible part; layoutAttributesForItemAtIndexPath: is for any indexpath. The implementation of the former often relies on the latter. It obtains the visible indexpath based on rect, then obtains the layout attributes based on the latter, adds it to the array, and then returns.