Search code examples
swiftxcodeuiscrollviewzoominguiedgeinsets

UIScrollView Zooming & contentInset


Simliar to iOS Photos App where the user is zooming in and out of an image by pinching:

UIView > UIScrollView > UIImageView > UIImage

Initially, I had the issue of zooming below scale 1: image being off centered. I got it fixed by doing this:

func scrollViewDidZoom(scrollView: UIScrollView) {
        let offsetX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
        let offsetY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
        scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0)
}

This works well when zooming out.

UIImage content mode is aspectFit

Issue

When I ZOOM IN, when zoomScale is above 1, scroll view insets need to hug the surroundings of the UIImage that the scroll view contains. This takes away the dead-space that was surrounding the UIImage. IE, Photos app when zooming-in by pinching or double tapping.

Tried

    func scrollViewDidZoom(scrollView: UIScrollView) {
    if scrollView.zoomScale > 1 {
        let imageScale = (self.imageView.bounds.width/self.imageView.image!.size.width)
        let imageWidth = self.imageView.image!.size.width * imageScale
        let imageHeight = self.imageView.image!.size.height * imageScale
        scrollView.contentInset = UIEdgeInsetsMake(((scrollView.frame.height - imageHeight) * 0.5), (scrollView.frame.width - imageWidth) * 0.5 , 0, 0)
        print (scrollView.contentInset.top)
    }
    else {

        let offsetX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
        let offsetY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
        scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0)
    }
}

Above addition seems to vary the inset amount still.

Update (images added)

First image shows the default layout. Rest shows when zoomed in.....

enter image description here


Solution

  • Your approach looks correct. You need to update your code as below.

        func scrollViewDidZoom(scrollView: UIScrollView) {
        
        if scrollView.zoomScale > 1 {
            
            if let image = imageView.image {
                
                let ratioW = imageView.frame.width / image.size.width
                let ratioH = imageView.frame.height / image.size.height
                
                let ratio = ratioW < ratioH ? ratioW:ratioH
                
                let newWidth = image.size.width*ratio
                let newHeight = image.size.height*ratio
                
                let left = 0.5 * (newWidth * scrollView.zoomScale > imageView.frame.width ? (newWidth - imageView.frame.width) : (scrollView.frame.width - scrollView.contentSize.width))
                let top = 0.5 * (newHeight * scrollView.zoomScale > imageView.frame.height ? (newHeight - imageView.frame.height) : (scrollView.frame.height - scrollView.contentSize.height))
                
                scrollView.contentInset = UIEdgeInsetsMake(top, left, top, left)
            }
        } else {
            scrollView.contentInset = UIEdgeInsetsZero
        }
    }
    

    Swift 5

        func scrollViewDidZoom(scrollView: UIScrollView) {
        
        if scrollView.zoomScale > 1 {
    
            if let image = imageView.image {
    
                let ratioW = imageView.frame.width / image.size.width
                let ratioH = imageView.frame.height / image.size.height
    
                let ratio = ratioW < ratioH ? ratioW:ratioH
    
                let newWidth = image.size.width*ratio
                let newHeight = image.size.height*ratio
    
                let left = 0.5 * (newWidth * scrollView.zoomScale > imageView.frame.width ? (newWidth - imageView.frame.width) : (scrollView.frame.width - scrollView.contentSize.width))
                let top = 0.5 * (newHeight * scrollView.zoomScale > imageView.frame.height ? (newHeight - imageView.frame.height) : (scrollView.frame.height - scrollView.contentSize.height))
    
                scrollView.contentInset = UIEdgeInsets(top: top, left: left, bottom: top, right: left)
            }
        } else {
            scrollView.contentInset = .zero
        }
    }