检测距离集合视图中心最近的集合视图单元

Detecting closest collectionviewcell to the center of the collectionview

怀疑我在下面做了一些根本性的错误...我有一个水平的集合视图,拖动后我想将最近的单元格对齐到中心。但是我的结果是不可预测的...我在这里做错了什么?

    func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    // Find collectionview cell nearest to the center of collectionView
    // Arbitrarily start with the last cell (as a default)
    var closestCell : UICollectionViewCell = collectionView.visibleCells()[0];
    for cell in collectionView!.visibleCells() as [UICollectionViewCell] {
        let closestCellDelta = abs(closestCell.center.x - collectionView.bounds.size.width/2.0)
        let cellDelta = abs(cell.center.x - collectionView.bounds.size.width/2.0)
        if (cellDelta < closestCellDelta){
            closestCell = cell
        }
    }
    let indexPath = collectionView.indexPathForCell(closestCell)
    collectionView.scrollToItemAtIndexPath(indexPath!, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)
}

试试这个:

NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:self.collectionView.center];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];

SWIFT:

let indexPath = self.collectionView.indexPathForItemAtPoint(self.collectionView.center)
self.collectionView.scrollToItemAtIndexPath(indexPath!, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)

原来我的原始代码缺少对 collecitonview 内容偏移的说明。此外,我已将居中移动到 scrollViewDidEndDecelerating 回调中。我已经修改了原始代码并将其包含在下面。

    func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
    // Find collectionview cell nearest to the center of collectionView
    // Arbitrarily start with the last cell (as a default)
    var closestCell : UICollectionViewCell = collectionView.visibleCells()[0];
    for cell in collectionView!.visibleCells() as [UICollectionViewCell] {
        let closestCellDelta = abs(closestCell.center.x - collectionView.bounds.size.width/2.0 - collectionView.contentOffset.x)
        let cellDelta = abs(cell.center.x - collectionView.bounds.size.width/2.0 - collectionView.contentOffset.x)
        if (cellDelta < closestCellDelta){
            closestCell = cell
        }
    }
    let indexPath = collectionView.indexPathForCell(closestCell)
    collectionView.scrollToItemAtIndexPath(indexPath!, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)
}

更新版本 Swift 4

var closestCell = collectionView.visibleCells[0]
for cell in collectionView.visibleCells {
    let closestCellDelta = abs(closestCell.center.x - collectionView.bounds.size.width/2.0 - collectionView.contentOffset.x)
    let cellDelta = abs(cell.center.x - collectionView.bounds.size.width/2.0 - collectionView.contentOffset.x)
    if (cellDelta < closestCellDelta){
        closestCell = cell
    }
}

let indexPath = collectionView.indexPath(for: closestCell)
collectionView.scrollToItem(at: indexPath!, at: .centeredHorizontally, animated: true)
  • 停止拖动时,只需选择中心并用它来选择索引路径

swift 4.2

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    let indexPath = photoCollectionView.indexPathForItem(at: photoCollectionView.center)
    photoCollectionView.scrollToItemAtIndexPath(indexPath!, atScrollPosition: .centeredHorizontally, animated: true)
}

None 的解决方案可靠地为我工作,所以我写了这个并且它在 100% 的时间内工作。很简单的。即使是内置的 indexPathForItemAtPoint 也不能​​ 100% 工作。

extension UICollectionView {
    
    var centerMostCell:UICollectionViewCell? {
        guard let superview = superview else { return nil }
        let centerInWindow = superview.convert(center, to: nil)
        guard visibleCells.count > 0 else { return nil }
        var closestCell:UICollectionViewCell?
        for cell in visibleCells {
            guard let sv = cell.superview else { continue }
            let cellFrameInWindow = sv.convert(cell.frame, to: nil)
            if cellFrameInWindow.contains(centerInWindow) {
                closestCell = cell
                break
            }
        }
        return closestCell
    }
    
}
var centerUICollectionViewCell: UICollectionViewCell? {
  get {
    // 1 
    guard let center = collectionView.superview?.convert(collectionView.center, to: collectionView) else { return nil }

    // 2  
    guard let centerIndexPath = collectionView.indexPathForItem(at: center) else { return nil }

    // 3
    return collectionView.cellForItem(at: centerIndexPath)
  }
}

1:需要确保我们 将点从超级视图坐标 space 转换为 collectionView 的 space

2:使用collectionView的本地space点找到indexPath

3: Return indexPath 的单元格

 public func closestIndexPathToCenter() -> IndexPath? {
    guard let cv = collectionView else { return nil }
   
    let visibleCenterPositionOfScrollView = Float(cv.contentOffset.x + (cv.bounds.size.width / 2))
        var closestCellIndex = -1
        var closestDistance: Float = .greatestFiniteMagnitude
        for i in 0..<cv.visibleCells.count {
            let cell = cv.visibleCells[i]
            let cellWidth = cell.bounds.size.width
            let cellCenter = Float(cell.frame.origin.x + cellWidth / 2)

            // Now calculate closest cell
            let distance: Float = fabsf(visibleCenterPositionOfScrollView - cellCenter)
            if distance < closestDistance {
                closestDistance = distance
                closestCellIndex = cv.indexPath(for: cell)!.row
            }
        }
        if closestCellIndex != -1 {
            return IndexPath(row: closestCellIndex, section: 0)
        }else {
            return nil
        }
}