Search code examples
swiftuiscrollview

UIImage in UIImageView that is in UIScrollView is automatically zoomed in - how to initially view whole UIImage?


I have a UIViewController that has a UIScrollView. Within the latter view there is a UIImageView. I have implemented the UIImageView so that the user can zoom in/out. However, at the beginning I would like the user to see the entire UIImage before deciding to zoom in/out. Currently the UIImage is just automatically enlarged. A lot of the answers on stackOverflow suggested setting the UIImageView.contentMode to scaleAspectFit - i have done this but it has not worked.

class ViewImageViewController: UIViewController, UIScrollViewDelegate {

var imageToPresent: UIImage!
let scrollView: UIScrollView = {
    let view = UIScrollView()
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

let imageView: UIImageView = {
   let imgView = UIImageView()
    imgView.backgroundColor = UIColor.black
    imgView.contentMode = .scaleAspectFit
    imgView.isUserInteractionEnabled = true
    imgView.translatesAutoresizingMaskIntoConstraints = false
    return imgView
}()


override func viewDidLoad() {
    super.viewDidLoad()
    print("ViewImageViewController.viewDidLoad")

    view.backgroundColor = UIColor.appGrayForLabels
    setupViews()
    scrollView.delegate = self
}


private func setupViews(){
    view.addSubview(scrollView)
    scrollView.minimumZoomScale = 1.0
    scrollView.maximumZoomScale = 4.0
    scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
    scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true


    guard let img = imageToPresent else{return}
    imageView.image = img
    scrollView.addSubview(imageView)
    imageView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
    imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
    imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
    imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
}

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    scrollView.isScrollEnabled = true
    return imageView
}

Solution

  • You want to set the .minimumZoomScale to the ratio between your image size and the scroll view size, and then set the .zoomScale to that value to start with.

    Here's your code, with a few modifications:

    class ViewImageViewController: UIViewController, UIScrollViewDelegate {
    
        var imageToPresent: UIImage!
    
        let scrollView: UIScrollView = {
            let view = UIScrollView()
            view.translatesAutoresizingMaskIntoConstraints = false
            return view
        }()
    
        let imageView: UIImageView = {
            let imgView = UIImageView()
            imgView.backgroundColor = UIColor.black
            imgView.contentMode = .scaleAspectFit
            imgView.isUserInteractionEnabled = true
            imgView.translatesAutoresizingMaskIntoConstraints = false
            return imgView
        }()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            print("ViewImageViewController.viewDidLoad")
    
            if let img = UIImage(named: "background") {
                imageToPresent = img
            }
    
            view.backgroundColor = .gray // UIColor.appGrayForLabels
            setupViews()
            scrollView.delegate = self
    
        }
    
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
    
            let scrollViewFrame = scrollView.frame
            let scaleWidth = scrollViewFrame.size.width / imageToPresent.size.width
            let scaleHeight = scrollViewFrame.size.height / imageToPresent.size.height
    
            let minScale = min(scaleWidth, scaleHeight)
    
            scrollView.minimumZoomScale = minScale
            scrollView.maximumZoomScale = 4.0
    
            scrollView.zoomScale = minScale
        }
    
        private func setupViews(){
    
            scrollView.isScrollEnabled = true
    
            view.addSubview(scrollView)
    
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
            scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
            scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
            scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
    
    
            guard let img = imageToPresent else{return}
            imageView.image = img
            scrollView.addSubview(imageView)
    
            let g = scrollView.contentLayoutGuide
    
            imageView.topAnchor.constraint(equalTo: g.topAnchor).isActive = true
            imageView.bottomAnchor.constraint(equalTo: g.bottomAnchor).isActive = true
            imageView.leadingAnchor.constraint(equalTo: g.leadingAnchor).isActive = true
            imageView.trailingAnchor.constraint(equalTo: g.trailingAnchor).isActive = true
    
            imageView.widthAnchor.constraint(equalToConstant: imageToPresent.size.width).isActive = true
            imageView.heightAnchor.constraint(equalToConstant: imageToPresent.size.height).isActive = true
    
        }
    
        func viewForZooming(in scrollView: UIScrollView) -> UIView? {
            return imageView
        }
    
    }