Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep collectionView cell position after pinch to zoom

I have a collectionView I am able to zoom when pinched.

It works like this:

I added a UIPinchGestureRecognizer on collectionView, when a pinch occurs I invalidate layout which force collectionView to ask delegate for new size.

It works well.

The problem I am unable to fix is that during pinch I want to keep my cell at same position. Just under the indicator at the middle of the screen. (see screenshot).

I was thinking to store current scrollView offset when pinch begins then when cell are redisplayed with new size, I calculate width difference and add or substract to contentOffset.

I have a contentInset in order to scroll first cell at the middle on the collectionView.

Here is my code:

@objc func handlePinchGesture(gesture: UIPinchGestureRecognizer) {

if (gesture.state == .Began) {
    scaleStart = metrics.scale // remember current scale
    widthStart = collectionView.visibleCells()\[0\].bounds.width // get size of a cell to calulate a difference when scale will change
    originalContentOffset = collectionView.contentOffset.x // remember original content offset
}
else if (gesture.state == .Changed) {

    let newScale = metrics.normalizeScale(scaleStart * gesture.scale) // normalize scale. give 0.5, 1, 1.5, 2

    metrics.scale = newScale // global struct

    //let ZoomIn = (newScale > scaleStart)

    collectionView.collectionViewLayout.invalidateLayout() // invalidate layout in order to redisplay cell with updated scale

    let scaleRatio = newScale / self.scaleStart
    var newContentOffset = CGFloat(0)

    let widthDiff: CGFloat = (scaleRatio * self.widthStart) - self.widthStart

    newContentOffset = originalContentOffset + widthDiff

    self.collectionView.setContentOffset(CGPointMake(newContentOffset ,0), animated: false)
    }
}

It just doest not work...

Do you have an idea?

Thanks a lot for your input.

Here is a screenshot of what I have and want with correct offsets. But I am unable to find a correct way to calculate content offset after pinch gesture.

screenshot

Thierry

like image 558
thierryb Avatar asked Jun 03 '16 19:06

thierryb


1 Answers

Regarding your original question, where you want the zoom center be at the middle of the screen, try this:

@objc func handlePinchGesture(_ gesture: UIPinchGestureRecognizer) {
    let scaleValue: CGFloat = gesture.scale
    if (gesture.state == .began) {
        scaleStart = metrics.scale // remember current scale
        widthStart = collectionView.visibleCells[0].bounds.width // get size of a cell to calulate a difference when scale will change
        originalContentOffset = collectionView.contentOffset.x // remember original content offset
        originalNumberOfCellsToOffset =  (originalContentOffset + (self.view.frame.size.width/2)) / (widthStart * 2)  //for zooming at the middle of the screen
    }
    else if (gesture.state == .changed) {

        let newScale = scaleStart * gesture.scale
        //let newScale = metrics.normalizeScale(scaleStart * gesture.scale) // use this line instead, if you want to normalize scale. give 0.5, 1, 1.5, 2

        metrics.scale = newScale // global struct

        //let ZoomIn = (newScale > scaleStart)

        collectionView.collectionViewLayout.invalidateLayout() // invalidate layout in order to redisplay cell with updated scale

        let scaleRatio = newScale / scaleStart
        var newContentOffset = CGFloat(0)

        let offsetDiffForEachCell: CGFloat = (scaleRatio * widthStart) - widthStart

        newContentOffset = (offsetDiffForEachCell)*originalNumberOfCellsToOffset + (originalContentOffset)

        collectionView.setContentOffset(CGPoint(x: newContentOffset ,y: 0), animated: false)
    }
}

Depending on the position and size of the collectionView on your screen, it might not be really zooming at the middle of the screen. If that's the case, what you want to do is to change this line:

originalNumberOfCellsToOffset =  (originalContentOffset + (self.view.frame.size.width/2)) / (widthStart * 2)  //for zooming at the middle of the screen

to e.g.

originalNumberOfCellsToOffset =  (originalContentOffset + (self.view.frame.size.width/2) - 20) / (widthStart * 2)  //for zooming at the middle of the screen

if you want the zoom center to be 20px more to the left.


BONUS: If instead, you want to zoom at the middle of the pinch, then change this line:

originalNumberOfCellsToOffset =  (originalContentOffset + (self.view.frame.size.width/2)) / (widthStart * 2)  //for zooming at the middle of the screen

to

originalNumberOfCellsToOffset = (gesture.location(ofTouch: 0, in: sectionSequenceCollectionView).x + gesture.location(ofTouch: 1, in: sectionSequenceCollectionView).x) / (widthStart * 2)
like image 193
郭呈豐 Avatar answered Nov 15 '22 00:11

郭呈豐