swift-Can't center UIImageView in UIScrollView with AutoLayout - ios

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
}
}

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
}

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.

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

Related

Image inside Scrollview with gestures

I have a control I'm making that includes having an image inside a scrollview, and I applied many gestures to it especially for zooming (pinch). Problem is, I want the scrollView's content size to match image new size without affecting:
Image center current location
Size
I can scroll to see all of image boundaries, with no extra space.
If there was anything not clear, please let me know.
You should do like this:
Swift3:
import UIKit
class ViewController: UIViewController,UIScrollViewDelegate
{
#IBOutlet weak var ScrollView: UIScrollView!
#IBOutlet weak var ScrollImgView: UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
self.ScrollView.minimumZoomScale = 0.1
self.ScrollView.maximumZoomScale = 5.0
ScrollView.isUserInteractionEnabled = true
}
func viewForZooming(in scrollView: UIScrollView) -> UIView?
{
return self.ScrollImgView
}
func scrollViewDidZoom(_ scrollView: UIScrollView)
{
let imgViewSize:CGSize! = self.ScrollImgView.frame.size;
let imageSize:CGSize! = self.ScrollImgView.image?.size;
var realImgSize : CGSize;
if(imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height)
{
realImgSize = CGSize(width: imgViewSize.width,height: imgViewSize.width / imageSize.width * imageSize.height);
}
else
{
realImgSize = CGSize(width: imgViewSize.height / imageSize.height * imageSize.width, height: imgViewSize.height);
}
var fr:CGRect = CGRect.zero
fr.size = realImgSize;
self.ScrollImgView.frame = fr;
let scrSize:CGSize = scrollView.frame.size;
let offx:CGFloat = (scrSize.width > realImgSize.width ? (scrSize.width - realImgSize.width) / 2 : 0);
let offy:CGFloat = (scrSize.height > realImgSize.height ? (scrSize.height - realImgSize.height) / 2 : 0);
scrollView.contentInset = UIEdgeInsetsMake(offy, offx, offy, offx);
let scrollViewSize:CGSize = self.scrollViewVisibleSize();
var imageCenter:CGPoint = CGPoint(x: self.ScrollView.contentSize.width/2.0, y:
self.ScrollView.contentSize.height/2.0);
let scrollViewCenter:CGPoint = self.scrollViewCenter()
if (self.ScrollView.contentSize.width < scrollViewSize.width)
{
imageCenter.x = scrollViewCenter.x;
}
if (self.ScrollView.contentSize.height < scrollViewSize.height)
{
imageCenter.y = scrollViewCenter.y;
}
self.ScrollImgView.center = imageCenter;
}
func scrollViewCenter() -> CGPoint
{
let scrollViewSize:CGSize = self.scrollViewVisibleSize()
return CGPoint(x: scrollViewSize.width/2.0, y: scrollViewSize.height/2.0);
}
func scrollViewVisibleSize() -> CGSize
{
let contentInset:UIEdgeInsets = self.ScrollView.contentInset;
let scrollViewSize:CGSize = self.ScrollView.bounds.standardized.size;
let width:CGFloat = scrollViewSize.width - contentInset.left - contentInset.right;
let height:CGFloat = scrollViewSize.height - contentInset.top - contentInset.bottom;
return CGSize(width:width, height:height);
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
}

Cannot Zoom UIImageView

I am new to swift.
I am doing an exercise from an app development book. I am trying to create an UIImageView and put a image in it, but I can not zoom the image in. There is something wrong in my code, but I don't know what is going on.
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var image: UIImageView!
#IBOutlet weak var scroll: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
scroll.delegate = ViewController()
updateZoomFor(size:view.bounds.size)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func viewForZooming(in scrollView: UIScrollView) -> UIImageView{
return image
}
func updateZoomFor(size:CGSize){
let widthScale = size.width / image.bounds.width
let heightScale = size.height / image.bounds.height
let scale = min(widthScale, heightScale)
scroll.minimumZoomScale = scale
}
}
First, set scroll.delegate = self as suggested by 3stud1ant3.
Second, you need to match the definition of theUIScrollViewDelegate method:
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return image
}
Make sure you set the zoom scale in updateZoomFor as well: scroll.zoomScale = scale.
Also you need to implement this:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
updateZoomFor(size: view.bounds.size)
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
updateConstraintsForSize(view.bounds.size)
}
fileprivate 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()
}

Swift Add Image to UIImageView from User Tap

I have a ScrollView with an ImageView inside.
Here a user can select a point on the image and it will print out the coordinates.
I'm also trying to add an image ("pin") to the tapped coordinate but am unsure how to...
// MARK: - Outlets
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var sharkImage: UIImageView!
// MARK: - View Did Load
override func viewDidLoad() {
super.viewDidLoad()
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 6.0
scrollView.delegate = self
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapAction))
self.sharkImage.isUserInteractionEnabled = true
self.sharkImage.addGestureRecognizer(tapGestureRecognizer)
}
// MARK: - Scroll View
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return sharkImage
}
// MARK: - Functions
func tapAction(sender: UITapGestureRecognizer) {
// Get points for the UIImageView
let touchPoint = sender.location(in: self.sharkImage)
print(touchPoint)
// Get points from the image itself
let z1 = touchPoint.x * (sharkImage.image?.size.width)! / sharkImage.frame.width
let z2 = touchPoint.y * (sharkImage.image?.size.height)! / sharkImage.frame.height
print("Touched point (\(z1), \(z2)")
}
In your tapAction:
let pin = UIImageView(frame: CGRect(x: touchPoint.x - 20, y: touchPoint.y - 20, width: 40, height: 40))
pin.image = UIImage(named: "pin")
sharkImage.addSubview(pin)
So what I´m doing is adding a new UIImageView with the x and y from touchPoint.x and touchPoint.y into your current sharkImage.

UIScrollView scrolls out of image after image is zoomed in?

I'm trying to implement a image zooming functionality using UIScrollview. where as I kept image as aspect fit.
Image is inside a UIScrollView, and image frame has been given similar to UIScrollView.
Here is my code.
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
scroller.minimumZoomScale = 1.0
scroller.maximumZoomScale = 7.0
}
// MARK: - User Defined Methods
#IBAction func doubleTapGestureAction(_ sender: UITapGestureRecognizer)
{
if scroller.zoomScale == 1
{
scroller.zoom(to: zoomForScale(scale: scroller.maximumZoomScale, center: sender.location(in: sender.view)), animated: true)
}
else
{
scroller.setZoomScale(1, animated: true)
}
print(isZoomedIn)
}
func zoomForScale(scale: CGFloat, center: CGPoint) -> CGRect
{
var zoomRect = CGRect.zero
zoomRect.size.height = image.frame.size.height / scale
zoomRect.size.width = image.frame.size.width / scale
let newCenter = image.convert(center, from: scroller)
zoomRect.origin.x = newCenter.x - (zoomRect.size.width / 2.0)
zoomRect.origin.y = newCenter.y - (zoomRect.size.height / 2.0)
return zoomRect
}
func viewForZooming(in scrollView: UIScrollView) -> UIView?
{
return image
}
Here is sample code:
import UIKit
class ViewController: UIViewController,UIScrollViewDelegate {
var imgDemo: UIImageView = {
let img = UIImageView()
img.contentMode = .scaleAspectFill
img.isUserInteractionEnabled = true
return img
}()
var scrollView:UIScrollView = {
let scroll = UIScrollView()
scroll.maximumZoomScale = 4.0
scroll.minimumZoomScale = 0.25
scroll.clipsToBounds = true
return scroll
}()
override func viewDidLoad() {
super.viewDidLoad()
imgDemo.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height)
imgDemo.image = UIImage(named: "5.jpg")
scrollView.delegate = self
scrollView.frame = imgDemo.frame
scrollView.addSubview(imgDemo)
view.addSubview(scrollView)
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imgDemo
}
}
Take a look at these methods. May be it will help. I have scroll view stretched to controller's view size. customizeScrollView() will calculate min and max scale options. centerImageView() will put UIImageView in the center of your UIScrollView
Call the customizeScrollView function in viewDidload.
fileprivate func customizeScrollView() {
guard let image = imageView?.image else { return }
var minZoom = fmin(self.view.frame.width / image.size.width, self.view.frame.height / image.size.height)
minZoom = fmin(1.0, minZoom)
scrollView?.contentSize = image.size
scrollView?.minimumZoomScale = minZoom
scrollView?.addSubview(self.imageView!)
scrollView?.setZoomScale(minZoom, animated: false)
centerImageView()
}
fileprivate func centerImageView() {
guard let imageView = imageView else { return }
guard let scrollView = scrollView else { return }
let boundsSize = scrollView.bounds.size
var frameToCenter = imageView.frame
// Center horizontally
if frameToCenter.size.width < boundsSize.width {
frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2
} else {
frameToCenter.origin.x = 0
}
// Center vertically
if frameToCenter.size.height < boundsSize.height {
frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2
} else {
frameToCenter.origin.y = 0
}
imageView.frame = frameToCenter
}
public func scrollViewDidZoom(scrollView: UIScrollView) {
print(imageView.frame)
centerImageView()
}

UIScrollView won't pinch to zoom. (Swift)

I'm having trouble getting my UIScrollView to zoom. My code is below:
class ViewController: UIViewController, UIScrollViewDelegate {
let imageView = UIImageView()
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let image = UIImage(named: "cats.jpg")
imageView.image = image
imageView.frame = CGRect(origin: CGPointZero, size: image!.size)
scrollView.addSubview(imageView)
scrollView.contentSize = image!.size
scrollView.clipsToBounds = false
scrollView.layer.borderColor = UIColor.yellowColor().CGColor
scrollView.layer.borderWidth = 4.0
let scrollViewFrame = scrollView.frame
let scaleWidth = scrollViewFrame.size.width / scrollView.contentSize.width
let scaleHeight = scrollViewFrame.size.height / scrollView.contentSize.height
let minScale = min(scaleWidth, scaleHeight);
scrollView.minimumZoomScale = minScale;
scrollView.maximumZoomScale = 1.0
scrollView.zoomScale = minScale;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return imageView
}
}
What am I doing wrong here? I have tried adjusting the min and max scale but doesn't make any difference. Any pointers on this would be really appreciated. Thanks!
Add this to viewDidLoad:
scrollView.delegate = self
The scroll view needs a delegate (your view controller) to get the view for zooming (the delegate method you implemented).

Resources