search

Home  >  Q&A  >  body text

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

高洛峰高洛峰2784 days ago701

reply all(1)I'll reply

  • PHPz

    PHPz2017-04-17 15:02:09

    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.

    reply
    0
  • Cancelreply