UICollectionViewLayout中两个方法的疑惑


在使用自定义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没有被用到呢?我被这两个方法搞糊涂了。

ios uicollectionview

渡边麻友  9 years, 7 months ago

Your Answer