使用 UIPinchGestureRecognizer 延迟缩放动画 UICollectionView

Delay in zoom animation UICollectionView with UIPinchGestureRecognizer

请告诉我 "zoom in/out" UICollectionView 和更改 UICollectionViewCell 大小的最佳方法。我现在正在使用 UIPinchGestureRecognizer:当 UICollectionView 是 "zoom in" 和 "zoom out" 时,会出现严重的延迟和冻结。

我如何在执行 PinchGestureRecognizer 时去除条纹?

这里是UICollectionViewController中UIPinchGestureRecognizer的实现代码:

class BlocksCollectionViewController: UICollectionViewController, UIGestureRecognizerDelegate {

  private var senderStartSize = CGFloat()
  private var senderNormalPosition = CGSize()

  override func viewDidLoad() {
    super.viewDidLoad()

    let pinchRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
    pinchRecognizer.delegate = self
    collectionView?.addGestureRecognizer(pinchRecognizer)
  }

  @objc func handlePinch(_ sender: UIPinchGestureRecognizer) {
    guard let collection = collectionView else { return }
    guard let layout = collection.collectionViewLayout as? CustomCollectionViewLayout else { return }

    switch sender.state {
    case .began:

      senderStartSize = layout.cellSize

    case .changed:

      layout.cellSize = senderStartSize * sender.scale
      layout.invalidateLayout()

    default:
      break
    }
  }

  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
  }
}

下面的代码是сustom CollectionViewLayout:

class CustomCollectionViewLayout: UICollectionViewLayout {

  private var cellAttributes: [IndexPath: UICollectionViewLayoutAttributes] = [:]
  var cellSize: CGFloat = 7
  private var contentSize = CGSize.zero

  override var collectionViewContentSize: CGSize {
    return self.contentSize
  }

  override func prepare() {
    guard let collection = collectionView else { return }

    for section in 0...collection.numberOfSections - 2 {
      for item in 0...collection.numberOfItems(inSection: section) - 2 {
        let cellIndex = IndexPath(item: item, section: section)
        let xPos = CGFloat(item) * cellSize
        let yPos = CGFloat(section) * cellSize

        let attributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex)
        attributes.frame = CGRect(x: xPos, y: yPos, width: cellSize, height: cellSize)

        cellAttributes[cellIndex] = attributes
      }
    }

    let contentWidth = CGFloat(collection.numberOfItems(inSection: 0)) * cellSize
    let contentHeight = CGFloat(collection.numberOfSections) * cellSize
    contentSize = CGSize(width: contentWidth, height: contentHeight)

    let centerOffsetX = (collection.contentSize.width - collection.frame.size.width) / 2
    let centerOffsetY = (collection.contentSize.height - collection.frame.size.height) / 2
    let centerPoint = CGPoint(x: centerOffsetX, y: centerOffsetY)

    collection.setContentOffset(centerPoint, animated: false)
  }

  override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    var attributesInRect = [UICollectionViewLayoutAttributes]()

    for cellAttributes in cellAttributes.values {
      if rect.intersects(cellAttributes.frame) {
        attributesInRect.append(cellAttributes)
      }
    }
    return attributesInRect
  }

  override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    return cellAttributes[indexPath]
  }
}

result of this code (GIF)

抱歉这么说,但我的猜测是问题不在于您的代码,而在于它本身的工作方式。因为我看到你有大量的细胞,这使得它很难处理,这就是它开始滞后的原因。

你试过用真机吗?将最新款 iPhone 与旧款进行比较?我相信您会看到渲染速度的差异。

但无论如何,calculations/expressions 越多,它就会越慢。此外,循环很昂贵。

顺便说一句,作为一种解决方案(如果应用程序的业务方面接受它),您可以不缩放 in/out,而是使用 2 个缩放按钮 in/out。我认为它不会导致延迟,但它肯定没有捏的那么酷。

在这种情况下,我建议您使用 Xcode Time Profiler 来调试性能。您会找到导致延迟的部分并在稍后对其进行优化。祝你好运:)

由于我的collection里面有很多UICollectionViewCells,最好的办法是这样:

1) 把UICollectionView放在一个containerView(简单的UIView)上,把这个containerView放在UIScrollView上。

我限制缩放:

scrollView.minimumZoomScale = 1
scrollView.maximumZoomScale = 6

2) 并使用了 UIScrollViewDelegate 的功能:

  func scrollViewDidZoom(_ scrollView: UIScrollView) {
   // Code for processing zoom - scrollView.zoomScale,
   // contentOffset, contentSize etc....
  }

  func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return containerView
  }

Result of this solution