UIScrollView zoom uiview to aspect fill

点点圈 提交于 2020-06-17 15:47:49

问题


I have an image in the UIScrollView and I'm trying to make it aspect fill by default, the code below scale it as listed on the first attached image. But it's not what I expect, how to scale it properly?

    func aspectFill() {
        let imageSize = imageZoomView.bounds.size

        let xScale = bounds.width / imageSize.width
        let yScale = bounds.height / imageSize.height

        zoomScale = max(xScale, yScale)
    }

But the expected result is


回答1:


Make sure your image view is set to .scaleToFill. When you load your image, you need to set the frame of the image view to the size of the image.

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 {
            return()
        }

        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 Updated code to handle size changes (such as on device rotation).




回答2:


Make sure that your UIImage takes the full content space of the scroll view. Use proper constraints to achieve this. Then set the contentMode of the image view to .aspectFill. You can do this using the storyboard itself.

Note: It is important that the constraints are set properly for the image view to occupy entire height and width of the scroll view.



来源:https://stackoverflow.com/questions/62318895/uiscrollview-zoom-uiview-to-aspect-fill

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!