带有嵌入式UIImageView的UIScrollView;如何使图像充满屏幕

问题描述

UIKit /程序化UI

我有一个带有UIImageView的UIScrollView。该图像由用户选择设置,可以具有各种尺寸。我想要的是图像首先填充屏幕(视图),然后可以缩放并一直滚动到图像的边缘。

如果我将ImageView直接添加到视图中(不使用scrollView),则会使用以下代码将其填充到屏幕中:

        mapImageView.image = ProjectImages.projectDefaultImage
        mapImageView.translatesAutoresizingMaskIntoConstraints = false
        mapImageView.contentMode = .scaleAspectFill
        view.addSubview(mapImageView)

现在与scrollView和嵌入式imageView相同:

        view.insertSubview(mapImageScrollView,at: 0)
        mapImageScrollView.delegate = self
        mapImageScrollView.translatesAutoresizingMaskIntoConstraints = false
        mapImageScrollView.contentMode = .scaleAspectFill
        mapImageScrollView.maximumZoomScale = 4.0
        mapImageScrollView.pinToEdges(of: view,safeArea: true)
        
        mapImageView.image = ProjectImages.projectDefaultImage
        mapImageView.translatesAutoresizingMaskIntoConstraints = false
        mapImageView.contentMode = .scaleAspectFill
        mapImageScrollView.addSubview(mapImageView)

现在,如果图像的高度小于视图的高度,则图像将不会填满屏幕,并且我在图像下方留有空白视图区域。我可以缩放并滚动确定,然后图像确实会填满视图。

添加约束可以根据需要填充视图,但是会干扰缩放和滚动,并阻止放大时进入图像的边缘。

如何正确设置?

Scrollview with embedded image

解决方法

您可能会发现这很有用...

它使您可以在scrollView中缩放图像,从居中开始并保持宽高比。

这是一个完整的实现。它的顶部有两个重要变量:

// 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

一切都是通过代码完成的-没有@IBOutlet或其他连接-因此只需创建即可添加一个新的视图控制器,并将其自定义类分配给ZoomAspectViewController(并编辑要使用的图像的名称):

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
    }
    
}

修改

作为示例,我使用了这张图片(2560 x 1440):

enter image description here

我在启动时得到以下结果:

enter image description here

和最大放大倍数(5.0)滚动到顶部中心:

enter image description here


编辑2

启动时具有相同图像,具有:

var fitMode: UIView.ContentMode = .scaleAspectFill

代替.scaleAspectFit

enter image description here

,

我发现此解决方案很适合我,设置和更改图像时,我会计算所需的最小缩放比例并将其设置在scrollView上:

    var selectedMapImage: MapImage? {
        didSet {
            mapImageView.image = mapImagesController.getImageForMapImage(selectedMapImage!)
            mapImageScrollView.minimumZoomScale = view.bounds.height / mapImageView.image!.size.height
            mapImageScrollView.setZoomScale(mapImageScrollView.minimumZoomScale,animated: true)
            mapImageScrollView.scrollRectToVisible(view.bounds,animated: true)
        }
    }

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...