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
}
我搜索了很多关于 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
}