Reputation: 57
I have searched many articles about UIScrollView
with an UIImageView
in, but all without AutoLayout
info. What I have done is: place a UIScrollView
in UIViewController
with constraints top 10, leading 10, bottom 50, trailing 10; place a UIImageView
inside of the UIScrollView
with constraints top 0, leading 0, bottom 0, trailing 0. What I want is simple: let my UIImageView
fit my UIScrollView
when app first loaded, and let my UIImageView
centered horizontally and vertically. Now I can see the image and just fit the screen nicely but the image is just at the top of UIScrollView
.(notes: in this circumstance, the width of my image after being zoomed to minimum zoomScale
equals to the width of UIScrollView
, the height of my image after being zoom to minimum zoomScale
less than the width of UIScrollView
. )
I really can't move my UIImageView
to center of UIScrollView
vertically and can not find where I made an error. Thanks a lot.
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
}
}
Upvotes: 1
Views: 8072
Reputation: 63
Piggy backing on pic-o-matic's answer. I had a case where I needed to center the image only if it was smaller than the scrollview frame. Using insets made for a straightforward solution.
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
}
Upvotes: 7
Reputation: 46
I experienced the same problem. I found a perfect solution for me, check out this github project: https://github.com/evgenyneu/ios-imagescroll-swift
How it essentially works is as follows: it adjusts your constraints as to center the image if it is zoomed out far enough and to enable it to zoom and scroll after the width or height exceeds the respective width or height of your container.
Found it thanks to this thread: Zooming UIImageView inside UIScrollView with autolayout (obj-c and swift solution available)!
// 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
}
}
Make sure your storyboard contains a viewcontroller. In the 'View' place a 'Scroll View' and in that 'Scroll View' place an 'Image View'.
Establish top/bottom/leading/trailing constraints of 0 (or whatever value you prefer) between the 'Scroll View' and the 'View'. Then establish the same constraints between the image and the scroll view and connect them to the @IBOutlets.
This should work fine as the code changes the constraints as you zoom.
Upvotes: 3
Reputation: 241
I had the same "issue".. without auto layout though. I came up with this solution(using insets to center the image). The automaticallyAdjustsScrollViewInsets property must be set to 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
}
I used a subclass of UIScrollView, "MyScrollview" to "see" whats going on, i think this is useful to learn about scrollview, bounds frames and content size:
//
// 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")
}
}
Upvotes: 1