问题描述
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中缩放图像,从居中开始并保持宽高比。
这是一个完整的实现。它的顶部有两个重要变量:
// 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):
我在启动时得到以下结果:
和最大放大倍数(5.0)滚动到顶部中心:
编辑2
启动时具有相同图像,具有:
var fitMode: UIView.ContentMode = .scaleAspectFill
代替.scaleAspectFit
:
我发现此解决方案很适合我,设置和更改图像时,我会计算所需的最小缩放比例并将其设置在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)
}
}