如何使用自定义布局跳转到 UICollectionView 中的任意 Cell?

How to jump to any Cell in a UICollectionView by using a custom layout?

我的水平 UICollectionView 中有 40 个单元格和一个按钮。

当我点击按钮时,我可以从 5 号单元格跳转到 10 号单元格。

但是一旦我想去另一个单元格(例如从 5 到 25),

它不起作用,而是转到 0。

代码:

func setValue(value: Int, animated: Bool) {
   self.value = value
   if let row = find(values, value) {
   let indexPath = NSIndexPath(forRow: row, inSection: 0)
   selectedCell = collectionView.cellForItemAtIndexPath(indexPath) as? StepperCell
   collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .CenteredHorizontally, animated: animated)
  }
 }

override func prepareLayout() {
   let start = Int(collectionView!.bounds.size.width * 0.5) - statics.width / 2
   let length = collectionView!.numberOfItemsInSection(0)

if cache.isEmpty && length > 0 {
   for item in 0..<length {
     let indexPath = NSIndexPath(forItem: item, inSection: 0)
     let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
     attributes.frame = CGRect(
      x: start + (statics.width + statics.padding) * indexPath.item,
      y: 0,
      width: statics.width,
      height: statics.height
    )
    cache.append(attributes)
  }
  contentWidth = CGRectGetMaxX(cache.last!.frame) + CGFloat(start)
   }
 }

 override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
   var layoutAttributes = [UICollectionViewLayoutAttributes]()
   for attributes in cache {
      if CGRectIntersectsRect(attributes.frame, rect) {
        layoutAttributes.append(attributes)
    }
  }
  return layoutAttributes
  }
 }

我认为您可以省去行逻辑。毕竟 - 您只是在使用传递的 Int。如果您的 if let 子句失败,它将不会滚动。

此外,挂断对 cellForItemAtIndexPath 的呼叫 - 您不需要它(您也没有使用它)。

我想也许你应该使用 NSIndexPath(forItem, inSection) 而不是 NSIndexPath(forRow, inSection)。 (Table视图使用row,collectionView使用item。)

collectionView.scrollToItemAtIndexPath(
      NSIndexPath(forItem: value, inSection: 0), 
      atScrollPosition: .CenteredHorizontally, animated: true)

经过一些研究,并得到了一位程序员的帮助。我想出了一个更简单、更有效的解决方案。 我仅将自定义布局用于对齐效果(在单元格之间切换时),然后让流布局处理其余部分。 这是代码:

struct Statics {  
   static let width = CGFloat(50.0)  
   static let height = CGFloat(50.0)  
   static let padding = CGFloat(5.0)  
}  

func commonInit() {  
   ...  
   // I had to set layout from the code, didn't work from the Interface Builder  
    let layout = HorizontalLayout()  
    layout.scrollDirection = .Horizontal  
    collectionView.collectionViewLayout = layout  
    collectionView.dataSource = self  
}  

override func layoutSubviews() {  
    super.layoutSubviews()  

    // Set item size, line spacing and insets and let the flow layout do the rest  
    let layout = collectionView.collectionViewLayout as! HorizontalLayout  
    layout.itemSize = CGSize(width: Statics.width, height: Statics.height)  
    layout.minimumLineSpacing = Statics.padding  
    let sideMargin = (collectionView.bounds.width - Statics.width) / 2.0  
    layout.sectionInset = UIEdgeInsetsMake(0.0, sideMargin, 0.0, sideMargin)  

    setValue(value, animated: true)  
}  

 class HorizontalLayout: UICollectionViewFlowLayout {  
   // This the only method you need to override  
    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {  
     let contentOffset: CGPoint  
     let itemWidth = itemSize.width  
     let padding = minimumLineSpacing  

    // Calculate nearest cell from the proposed content offset  
    let index = round(proposedContentOffset.x / (itemWidth + padding))  
    let x = (itemWidth + padding) * index  
    contentOffset = CGPoint(x: x, y: proposedContentOffset.y)  

    return contentOffset  
  }  
}