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

Posted

技术标签:

【中文标题】swift-无法使用 AutoLayout 在 UIScrollView 中居中 UIImageView【英文标题】:swift-Can't center UIImageView in UIScrollView with AutoLayout 【发布时间】:2015-02-16 02:41:12 【问题描述】:

我搜索了很多关于UIScrollView 的文章,其中包含UIImageView,但都没有AutoLayout 信息。我所做的是:在UIViewController 中放置一个UIScrollView,约束为前10,前10,后50,尾10;在UIScrollView 内放置一个UIImageView,约束为前0,前0,下0,尾0。我想要的很简单:让我的UIImageView 适合我的UIScrollView,当应用程序第一次加载时,让我的@ 987654330@ 水平和垂直居中。现在我可以看到图像并且正好适合屏幕但是图像正好在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



【问题讨论】:

我认为是因为自动布局问题。我需要做什么? 【参考方案1】:

支持 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
    

【讨论】:

我把它放在public func scrollViewDidZoom(scrollView: UIScrollView) 上,效果很好。我还在这两种情况下都添加了else scrollView.contentInset.top = 0 谢谢!【参考方案2】:

我遇到了同样的问题。我找到了一个完美的解决方案,看看这个 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
  


确保您的故事板包含视图控制器。在“视图”中放置一个“滚动视图”,在该“滚动视图”中放置一个“图像视图”。

在“滚动视图”和“视图”之间建立 0(或您喜欢的任何值)的顶部/底部/前导/尾随约束。然后在图像和滚动视图之间建立相同的约束并将它们连接到@IBOutlets。

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

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。 感谢 beresfordt,我的第一篇文章;)我添加了代码,希望对您有所帮助。【参考方案3】:

我也有同样的“问题”……不过没有自动布局。我想出了这个解决方案(使用插图使图像居中)。 automaticAdjustsScrollViewInsets 属性必须设置为 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”来“查看”发生了什么,我认为这对于了解滚动视图、边界框和内容大小很有用:

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

【讨论】:

以上是关于swift-无法使用 AutoLayout 在 UIScrollView 中居中 UIImageView的主要内容,如果未能解决你的问题,请参考以下文章

使用 Swift (AutoLayout) 固定 UIImage 的宽度和高度

AutoLayout - 使用水平约束保持图像的比例(Swift Xcode 6)

使用 AutoLayout 以编程方式将 UILabel 添加到 ScrollView (swift)

使用 Swift、autolayout 和 SnapKit 使带有 UIImageView 的 UIScrollView 自动缩放内容

布局布局后的 Swift Autolayout 回调函数

swift autolayout.swift