ios - UICollectionViewLayout中两个方法的疑惑
高洛峰
高洛峰 2017-04-17 15:00:09
0
1
698

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

高洛峰
高洛峰

拥有18年软件开发和IT教学经验。曾任多家上市公司技术总监、架构师、项目经理、高级软件工程师等职务。 网络人气名人讲师,...

reply all(1)
PHPzhong

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.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template