swift-无法使用 AutoLayout 在 UIScrollView 中将 UIImageView 居中

swift-Can't center UIImageView in UIScrollView with AutoLayout

我搜索了很多关于 UIScrollView 的文章,其中包含 UIImageView,但都没有 AutoLayout 信息。我所做的是:在 UIViewController 中放置一个 UIScrollView,约束前 10 个,前 10 个,后 50 个,尾随 10 个;在 UIScrollView 内放置一个 UIImageView,约束条件为顶部 0、前导 0、底部 0、尾随 0。我想要的很简单:让我的 UIImageView 适合我的 UIScrollView应用程序首先加载,并让我的 UIImageView 水平和垂直居中。现在我可以看到图像并且正好适合屏幕,但是图像刚好在 UIScrollView 的顶部。(注意:在这种情况下,我的图像在缩放到最小 zoomScale 后的宽度等于到 UIScrollView 的宽度,缩放到最小 zoomScale 后我的图像的高度小于 UIScrollView 的宽度。) 我真的无法将我的 UIImageView 垂直移动到 UIScrollView 的中心,也找不到我出错的地方。非常感谢。

import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
func centerScrollViewContents() {
    let boundsSize = scrollView.bounds.size
    var contentsFrame = imageView.frame
    if contentsFrame.size.width < boundsSize.width {
        contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0
    } else {
        contentsFrame.origin.x = 0.0
    }

    if contentsFrame.size.height < boundsSize.height {
        contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0
    } else {
        contentsFrame.origin.y = 0.0
    }
}

override func viewDidLayoutSubviews() {
    let myImage = UIImage(named: "7.jpg")
    imageView.image = myImage        
    let weightScale = scrollView.bounds.size.width / myImage!.size.width
    let heightScale = scrollView.bounds.size.height / myImage!.size.height
    let minScale = min(weightScale, heightScale)
    let imagew = myImage!.size.width * minScale
    let imageH = myImage!.size.height * minScale
    let imageRect = CGRectMake(0, 0, imagew, imageH)
    imageView.frame = imageRect
    scrollView.contentSize = myImage!.size
    scrollView.maximumZoomScale = 1.0
    scrollView.minimumZoomScale = minScale
    scrollView.zoomScale = minScale
    imageView.frame = imageRect
    centerScrollViewContents()
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return imageView
}

}

我有相同的 "issue".. 但没有自动布局。我想出了这个解决方案(使用插图使图像居中)。 automaticallyAdjustsScrollViewInsets 属性 必须设置为 false。

override func viewDidLoad() {
    super.viewDidLoad()
    //create scrollview
    self.automaticallyAdjustsScrollViewInsets = false
    myScrollview = MyScrollview(frame: self.view.frame)
    myScrollview.alwaysBounceHorizontal = true
    myScrollview.alwaysBounceVertical = true
    myScrollview.delegate = self
    //....
}
override func viewWillLayoutSubviews() {
    //setup frame portrait and landscape
    myScrollview.frame = self.view.frame
    myScrollview.zoomScale = self.view.frame.width / photo.size.width
    myScrollview.minimumZoomScale = self.view.bounds.width / photo.size.width

    // calculate inset to center vertically
    let shift = myScrollview.frame.height/2.0 - myScrollview.contentSize.height/2.0
    myScrollview.contentInset.top = shift
}

override func viewDidLayoutSubviews() {

}

func scrollViewDidZoom(scrollView: UIScrollView) {
    let shift = myScrollview.frame.height/2.0 - myScrollview.contentSize.height/2.0
    myScrollview.contentInset.top = shift
}

我使用了 UIScrollView 的子类,"MyScrollview" 到 "see" 这是怎么回事,我认为这对了解滚动视图、边界框架和内容大小很有用:

//
//  MyScrollview.swift
//  CampingDeHooiberg  
//
//  Created by Andre Lam on 09-03-15.
//  Copyright (c) 2015 Andre Lam. All rights reserved.
//

import UIKit

class MyScrollview: UIScrollView {

    override var bounds: CGRect { willSet { println("bounds set: \(newValue) contentSize:\(self.contentSize) frame:\(self.frame)")}}
    override var contentSize: CGSize { willSet { println("bounds:\(self.bounds) set contentsize:\(newValue)  frame: \(self.frame)")}}
    override var frame: CGRect { willSet { println("bounds: \(self.bounds) contentSize:\(self.contentSize) frame set:\(newValue)")}}

override init(frame: CGRect) {
    super.init(frame: frame)
}

required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

}

我遇到了同样的问题。我找到了一个完美的解决方案,查看这个 github 项目:https://github.com/evgenyneu/ios-imagescroll-swift

它的基本工作原理如下:如果图像缩小得足够远,它会调整您的约束以将图像居中,并在宽度或高度超过您的相应宽度或高度后使其能够缩放和滚动容器。

感谢这个线程找到它:Zooming UIImageView inside UIScrollView with autolayout(obj-c 和 swift 解决方案可用)!

//  ViewController.swift
//  image-scroll-swift
//
//  Created by Evgenii Neumerzhitckii on 4/10/2014.
//  Copyright (c) 2014 Evgenii Neumerzhitckii. All rights reserved.
//

import UIKit

let imageScrollLargeImageName = "wallabi.jpg"


class ViewController: UIViewController, UIScrollViewDelegate {

  @IBOutlet weak var scrollView: UIScrollView!
  @IBOutlet weak var imageView: UIImageView!

  @IBOutlet weak var imageConstraintTop: NSLayoutConstraint!
  @IBOutlet weak var imageConstraintRight: NSLayoutConstraint!
  @IBOutlet weak var imageConstraintLeft: NSLayoutConstraint!
  @IBOutlet weak var imageConstraintBottom: NSLayoutConstraint!

  var lastZoomScale: CGFloat = -1

  override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    imageView.image = UIImage(named: imageScrollLargeImageName)
    scrollView.delegate = self
    updateZoom()
  }

  // Update zoom scale and constraints
  // It will also animate because willAnimateRotationToInterfaceOrientation
  // is called from within an animation block
  //
  // DEPRECATION NOTICE: This method is said to be deprecated in iOS 8.0. But it still works.
  override func willAnimateRotationToInterfaceOrientation(
    toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {

    super.willAnimateRotationToInterfaceOrientation(toInterfaceOrientation, duration: duration)
    updateZoom()
  }

  func updateConstraints() {
    if let image = imageView.image {
      let imageWidth = image.size.width
      let imageHeight = image.size.height

      let viewWidth = view.bounds.size.width
      let viewHeight = view.bounds.size.height

      // center image if it is smaller than screen
      var hPadding = (viewWidth - scrollView.zoomScale * imageWidth) / 2
      if hPadding < 0 { hPadding = 0 }

      var vPadding = (viewHeight - scrollView.zoomScale * imageHeight) / 2
      if vPadding < 0 { vPadding = 0 }

      imageConstraintLeft.constant = hPadding
      imageConstraintRight.constant = hPadding

      imageConstraintTop.constant = vPadding
      imageConstraintBottom.constant = vPadding

      // Makes zoom out animation smooth and starting from the right point not from (0, 0)
      view.layoutIfNeeded()
    }
  }

  // Zoom to show as much image as possible unless image is smaller than screen
  private func updateZoom() {
    if let image = imageView.image {
      var minZoom = min(view.bounds.size.width / image.size.width,
        view.bounds.size.height / image.size.height)

      if minZoom > 1 { minZoom = 1 }

      scrollView.minimumZoomScale = minZoom

      // Force scrollViewDidZoom fire if zoom did not change
      if minZoom == lastZoomScale { minZoom += 0.000001 }

      scrollView.zoomScale = minZoom
      lastZoomScale = minZoom
    }
  }


  // UIScrollViewDelegate
  // -----------------------

  func scrollViewDidZoom(scrollView: UIScrollView) {
    updateConstraints()
  }

  func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return imageView
  }

}

确保您的故事板包含 viewcontroller。在 'View' 中放置一个 'Scroll View',在 'Scroll View' 中放置一个 'Image View'。

在 'Scroll View' 和 'View' 之间建立 top/bottom/leading/trailing 0(或您喜欢的任何值)的约束。然后在图像和滚动视图之间建立相同的约束并将它们连接到@IBOutlets。

这应该可以正常工作,因为代码会在缩放时更改约束。

支持 pic-o-matic 的回答。我有一个案例,只有当它小于滚动视图框架时我才需要将图像居中。使用为直接解决方案制作的插图。

if imageView.frame.height <= scrollView.frame.height {
        let shiftHeight = scrollView.frame.height/2.0 - scrollView.contentSize.height/2.0
        scrollView.contentInset.top = shiftHeight
    }
    if imageView.frame.width <= scrollView.frame.width {
        let shiftWidth = scrollView.frame.width/2.0 - scrollView.contentSize.width/2.0
        scrollView.contentInset.left = shiftWidth
    }