0

Please, tell me the best way to "zoom in/out" UICollectionView and change size of UICollectionViewCell. I'm using a UIPinchGestureRecognizer now: When UICollectionView is "zoom in" and "zoom out", there are serious delays and freezing.

How can I get rid of friezes when I perform a PinchGestureRecognizer?

Here is implementation code of UIPinchGestureRecognizer in UICollectionViewController:

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
  }
}

The code below is с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)

  • I have changed implementation of **handlePinch(_ sender: UIPinchGestureRecognizer)**. I think can use CGAffineTransform in .changed of UIPinchGestureRecognizer – Blinded Astronaut Mar 02 '18 at 19:04

3 Answers3

0

Sorry to say that but my guess is that the problem is not really in your code but the way it works itself. Since as I see you have a big number of cells which makes it hard handle that's why it starts lagging.

Have you tried using the real device? Comparing latest iPhones with older ones? I'm sure you will see the difference in speed of rendering.

But in any case, as more calculations/expressions you have the laggier it will be. Also, loops are expensive.

Btw, as a solution (if the business side of the app accepts it) you may make not zooming in/out but 2 buttons that zooms in/out. I think it won't cause lags but it's not that cool as pinching for sure.

Tung Fam
  • 7,899
  • 4
  • 56
  • 63
0

In this case, I would recommend you using Xcode Time Profiler to debug performance. You will find which part causing delay and optimize it later. Good luck :)

0

Due to the fact that there are a lot of UICollectionViewCells in my collection, the best way is this:

1) Place UICollectionView on a containerView (simple UIView), and put this containerView on UIScrollView.

I limited zoom:

scrollView.minimumZoomScale = 1
scrollView.maximumZoomScale = 6

2) And used functions of 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