UIScrollView with Embedded UIImageView; how to get the image to fill the screen

后端 未结 2 1225
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-26 07:30

UIKit/Programmatic UI

I have an UIScrollView with an UIImageView inside. The image is set by user selection and can have all kinds of sizes. What I want is that the image

2条回答
  •  后悔当初
    2021-01-26 07:41

    You might find this useful...

    It allows you to zoom an image in a scrollView, starting with it centered and maintaining aspect ratio.

    Here's a complete implementation. It has two important variables at the top:

    // can be .scaleAspectFill or .scaleAspectFit
    var fitMode: UIView.ContentMode = .scaleAspectFill
    
    // if fitMode is .scaleAspectFit, allowFullImage is ignored
    // if fitMode is .scaleAspectFill, image will start zoomed to .scaleAspectFill
    //  if allowFullImage is false, image will zoom back to .scaleAspectFill if "pinched in"
    //  if allowFullImage is true, image can be "pinched in" to see the full image
    var allowFullImage: Bool = true
    

    Everything is done via code - no @IBOutlet or other connections - so just create add a new view controller and assign its custom class to ZoomAspectViewController (and edit the name of the image you want to use):

    class ZoomAspectViewController: UIViewController, UIScrollViewDelegate {
        
        var scrollView: UIScrollView!
        var imageView: UIImageView!
        var imageViewBottomConstraint: NSLayoutConstraint!
        var imageViewLeadingConstraint: NSLayoutConstraint!
        var imageViewTopConstraint: NSLayoutConstraint!
        var imageViewTrailingConstraint: NSLayoutConstraint!
        
        // can be .scaleAspectFill or .scaleAspectFit
        var fitMode: UIView.ContentMode = .scaleAspectFit
        
        // if fitMode is .scaleAspectFit, allowFullImage is ignored
        // if fitMode is .scaleAspectFill, image will start zoomed to .scaleAspectFill
        //  if allowFullImage is false, image will zoom back to .scaleAspectFill if "pinched in"
        //  if allowFullImage is true, image can be "pinched in" to see the full image
        var allowFullImage: Bool = true
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            guard let img = UIImage(named: "myImage") else {
                fatalError("Could not load the image!!!")
            }
            
            scrollView = UIScrollView()
            imageView = UIImageView()
            
            scrollView.translatesAutoresizingMaskIntoConstraints = false
            imageView.translatesAutoresizingMaskIntoConstraints = false
            
            imageView.contentMode = .scaleToFill
            
            scrollView.addSubview(imageView)
            view.addSubview(scrollView)
            
            // respect safe area
            let g = view.safeAreaLayoutGuide
            
            imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: scrollView.topAnchor)
            imageViewBottomConstraint = imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
            imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor)
            imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor)
    
            NSLayoutConstraint.activate([
                
                scrollView.topAnchor.constraint(equalTo: g.topAnchor),
                scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
                scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
                scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
    
                imageViewTopConstraint,
                imageViewBottomConstraint,
                imageViewLeadingConstraint,
                imageViewTrailingConstraint,
                
            ])
            
            scrollView.delegate = self
            scrollView.minimumZoomScale = 0.1
            scrollView.maximumZoomScale = 5.0
            
            imageView.image = img
            imageView.frame.size = img.size
            
        }
    
        override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
            super.viewWillTransition(to: size, with: coordinator)
            coordinator.animate(alongsideTransition: { _ in
                self.updateMinZoomScaleForSize(size, shouldSize: (self.scrollView.zoomScale == self.scrollView.minimumZoomScale))
                self.updateConstraintsForSize(size)
            }, completion: {
                _ in
            })
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            updateMinZoomScaleForSize(scrollView.bounds.size)
            updateConstraintsForSize(scrollView.bounds.size)
            
            if fitMode == .scaleAspectFill {
                centerImageView()
            }
            
        }
        
        func updateMinZoomScaleForSize(_ size: CGSize, shouldSize: Bool = true) {
            guard let img = imageView.image else {
                return
            }
            
            var bShouldSize = shouldSize
            
            let widthScale = size.width / img.size.width
            let heightScale = size.height / img.size.height
            
            var minScale = min(widthScale, heightScale)
            let startScale = max(widthScale, heightScale)
            
            if fitMode == .scaleAspectFill && !allowFullImage {
                minScale = startScale
            }
            if scrollView.zoomScale < minScale {
                bShouldSize = true
            }
            scrollView.minimumZoomScale = minScale
            if bShouldSize {
                scrollView.zoomScale = fitMode == .scaleAspectFill ? startScale : minScale
            }
        }
        
        func scrollViewDidZoom(_ scrollView: UIScrollView) {
            updateConstraintsForSize(scrollView.bounds.size)
        }
        
        func centerImageView() -> Void {
            let yOffset = (scrollView.frame.size.height - imageView.frame.size.height) / 2
            let xOffset = (scrollView.frame.size.width - imageView.frame.size.width) / 2
            scrollView.contentOffset = CGPoint(x: -xOffset, y: -yOffset)
        }
        
        func updateConstraintsForSize(_ size: CGSize) {
            let yOffset = max(0, (size.height - imageView.frame.height) / 2)
            imageViewTopConstraint.constant = yOffset
            imageViewBottomConstraint.constant = yOffset
            
            let xOffset = max(0, (size.width - imageView.frame.width) / 2)
            imageViewLeadingConstraint.constant = xOffset
            imageViewTrailingConstraint.constant = xOffset
            
            view.layoutIfNeeded()
        }
        
        func viewForZooming(in scrollView: UIScrollView) -> UIView? {
            return imageView
        }
        
    }
    

    Edit

    As an example, I used this image (2560 x 1440):

    and I get this result on launch:

    and maximum zoom in (5.0) scrolled to top-center:


    Edit 2

    Same image, at launch, with:

    var fitMode: UIView.ContentMode = .scaleAspectFill
    

    instead of .scaleAspectFit:

提交回复
热议问题