UIImageView pinch zoom swift
Asked Answered
B

14

92

I was hoping someone could help me out. I am trying to allow a user to pinch zoom on a UIImageView(with a max and min level allowed). But for some reason the it does not work right. The image zooms a little then just bounces back. Thank you.

here is the zoom func

func zoom(sender:UIPinchGestureRecognizer) {


    if sender.state == .Ended || sender.state == .Changed {

        let currentScale = self.view.frame.size.width / self.view.bounds.size.width
        var newScale = currentScale*sender.scale

        if newScale < 1 {
            newScale = 1
        }
        if newScale > 9 {
            newScale = 9
        }

        let transform = CGAffineTransformMakeScale(newScale, newScale)

        self.imageView?.transform = transform
        sender.scale = 1

    }

}
Banka answered 3/5, 2015 at 13:15 Comment(3)
You can use ImageScrollView open source, a zoomable and scrollable image view. github.com/huynguyencong/ImageScrollView – Xanthous
^^^ That worked great for me, took all of about 30 seconds. Thank you – Lobbyism
@huync your ImageScrollView works great. Thanks! – Injurious
B
64

I decided to add the imageView to a UIScrollView. It allows the user to zoom and pan over. Here is the code I used.

in order to set max/min zoom I used :

    scrollImg.minimumZoomScale = 1.0
    scrollImg.maximumZoomScale = 10.0

here is the rest of the code.

    var vWidth = self.view.frame.width
    var vHeight = self.view.frame.height

    var scrollImg: UIScrollView = UIScrollView()
    scrollImg.delegate = self
    scrollImg.frame = CGRectMake(0, 0, vWidth!, vHeight!)
    scrollImg.backgroundColor = UIColor(red: 90, green: 90, blue: 90, alpha: 0.90)
    scrollImg.alwaysBounceVertical = false
    scrollImg.alwaysBounceHorizontal = false
    scrollImg.showsVerticalScrollIndicator = true
    scrollImg.flashScrollIndicators()

    scrollImg.minimumZoomScale = 1.0
    scrollImg.maximumZoomScale = 10.0

    defaultView!.addSubview(scrollImg)

    imageView!.layer.cornerRadius = 11.0
    imageView!.clipsToBounds = false
    scrollImg.addSubview(imageView!)

I also had to add this as well

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

Swift 3 & above function prototype

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return self.mainImage
}
Banka answered 3/5, 2015 at 13:57 Comment(1)
Was my case, where I needed had UIImageView within UIScrollView. Worked like a charm so far. Let's see what our QA will say about that πŸ˜€ – Anglian
S
147

UIImageView pinch zoom with UIScrollView || image zooming ios in swift 3 and Xcode 8 letter Youtube video URL

set uiscrollview Delegate in storyboard enter image description here

 class PhotoDetailViewController: UIViewController, UIScrollViewDelegate {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var imgPhoto: UIImageView!
      
    override func viewDidLoad() {
        
        super.viewDidLoad()
    
        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = 6.0        
        // scrollView.delegate = self - it is set on the storyboard.
    }  
        
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
       
        return imgPhoto
    }
Separate answered 3/5, 2015 at 13:15 Comment(10)
Remarkable! It really is as easy as this post says! – Riddick
This should be the accepted answer for Swift 3. Nothing else I tried worked, including the accepted answer above. – Chiang
It is not necessary to set the scroll views delegate in code since it was already set via the storyboard as seen in the image above. – Ravin
Im m very new to swift and xcode, and im trying to do the same thing in a tableview, which contains images, each tablecell has its own backend. So im trying to put the delegate as the backend for each tablecell, but it doesnt work. Did the steps above, is there anything I am missing? – Waxwork
Great answer! I like the simplicity (no unnecessary subclasses etc) and that it works well with no storyboard (just need to set the delegate and use add the UIImageView as a subview of the UIScrollView. – Lully
Also if you are the storyboard type, no need to set the zoom scales in code, you can do it right in the storyboard's attributes inspector. This means you don't necessarily even need an outlet for the UIScrollView. – Lully
scrollView.minimumZoomScale is less than 1 let say 0.5 how can i make it in center? – Leveridge
@Neighbor huh? can't they unpinch using their fingers? – Sacrificial
how can i limit the zoom for the example in the video? – Waterer
work only for pitch zoom. how about double tap zooming – Knighten
B
64

I decided to add the imageView to a UIScrollView. It allows the user to zoom and pan over. Here is the code I used.

in order to set max/min zoom I used :

    scrollImg.minimumZoomScale = 1.0
    scrollImg.maximumZoomScale = 10.0

here is the rest of the code.

    var vWidth = self.view.frame.width
    var vHeight = self.view.frame.height

    var scrollImg: UIScrollView = UIScrollView()
    scrollImg.delegate = self
    scrollImg.frame = CGRectMake(0, 0, vWidth!, vHeight!)
    scrollImg.backgroundColor = UIColor(red: 90, green: 90, blue: 90, alpha: 0.90)
    scrollImg.alwaysBounceVertical = false
    scrollImg.alwaysBounceHorizontal = false
    scrollImg.showsVerticalScrollIndicator = true
    scrollImg.flashScrollIndicators()

    scrollImg.minimumZoomScale = 1.0
    scrollImg.maximumZoomScale = 10.0

    defaultView!.addSubview(scrollImg)

    imageView!.layer.cornerRadius = 11.0
    imageView!.clipsToBounds = false
    scrollImg.addSubview(imageView!)

I also had to add this as well

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

Swift 3 & above function prototype

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return self.mainImage
}
Banka answered 3/5, 2015 at 13:57 Comment(1)
Was my case, where I needed had UIImageView within UIScrollView. Worked like a charm so far. Let's see what our QA will say about that πŸ˜€ – Anglian
A
53

Supporting Swift 5.1, You can create an extension of UIImageView, like this:

extension UIImageView {
  func enableZoom() {
    let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(startZooming(_:)))
    isUserInteractionEnabled = true
    addGestureRecognizer(pinchGesture)
  }

  @objc
  private func startZooming(_ sender: UIPinchGestureRecognizer) {
    let scaleResult = sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)
    guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
    sender.view?.transform = scale
    sender.scale = 1
  }
}
Abrams answered 25/10, 2019 at 12:9 Comment(3)
love this solution. Can it possible to scroll right left? @John Lima – Diatribe
I found this always zooms in on the same area, no matter where you pinch to zoom. Would there be a way to have this zoom wherever you pinch to zoom? – Eugenol
The scrolling works better using a scroll view – Abrams
M
17

The option for swift 4

class ViewController: UIViewController, UIScrollViewDelegate {

@IBOutlet weak var scrolView: UIScrollView!
@IBOutlet weak var imgPhoto: UIImageView!

  override func viewDidLoad() {
    super.viewDidLoad()
    scrolView.delegate = self
    scrolView.minimumZoomScale = 1.0
    scrolView.maximumZoomScale = 10.0
  }

  func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return imgPhoto
  }
}
Modestia answered 3/4, 2018 at 8:48 Comment(2)
When pinching ends, call scrollView.zoomScale = 1.0 – Adeleadelheid
Working Solution, Thanks – Diatribe
X
15

You can use ImageScrollView open source, a zoomable and scrollable image view. http://github.com/huynguyencong/ImageScrollView

Like this opensource, add ImageView to ScrollView

open class ImageScrollView: UIScrollView {
   var zoomView: UIImageView? = nil
}

extension ImageScrollView: UIScrollViewDelegate{

    public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return zoomView
    }

    public func scrollViewDidZoom(_ scrollView: UIScrollView) {
        adjustFrameToCenter()
    }
}
Xanthous answered 20/9, 2016 at 18:56 Comment(3)
I think this is the best answer. It's zoomable and also scrollable. In addition it supports double tab action. – Khalsa
I want to say one more things. It's really easy to use. – Khalsa
This approach worked best for me. – Untrimmed
G
9

Using Swift 5.0, here is how it works for me:

let myImageView = UIImageView(image: myImage)
myImageView.isUserInteractionEnabled = true
let pinchMethod = UIPinchGestureRecognizer(target: self, action: #selector(pinchImage(sender:)))
myImageView.addGestureRecognizer(pinchMethod)

@objc func pinchImage(sender: UIPinchGestureRecognizer) {
  guard let sender = sender.view else { return }

if let scale = (sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)) {
  guard scale.a > 1.0 else { return }
  guard scale.d > 1.0 else { return }
  sender.view?.transform = scale
  sender.scale = 1.0
 }
}

You can use scale.a, scale.b, scale.c, scale.d, scale.tx and scale.ty to set your scale limits.

Generative answered 24/7, 2019 at 23:46 Comment(0)
L
6

In my view, the problem is your determination of currentScale. It always equals 1, because you change the scale of your imageView. You should assign your currentScale as follows:

let currentScale = self.imageView?.frame.size.width / self.imageView?.bounds.size.width  
Liggins answered 3/5, 2015 at 14:2 Comment(0)
C
6

Swift 3 solution

By default UIImageView's userInteration is disabled. Enable it before adding any gestures in UIImageView.

imgView.isUserInteractionEnabled = true

The scale factor relative to the points of the two touches in screen coordinates

var lastScale:CGFloat!
func zoom(gesture:UIPinchGestureRecognizer) {
    if(gesture.state == .began) {
        // Reset the last scale, necessary if there are multiple objects with different scales
        lastScale = gesture.scale
    }
    if (gesture.state == .began || gesture.state == .changed) {
    let currentScale = gesture.view!.layer.value(forKeyPath:"transform.scale")! as! CGFloat
    // Constants to adjust the max/min values of zoom
    let kMaxScale:CGFloat = 2.0
    let kMinScale:CGFloat = 1.0
    var newScale = 1 -  (lastScale - gesture.scale)
    newScale = min(newScale, kMaxScale / currentScale)
    newScale = max(newScale, kMinScale / currentScale)
    let transform = (gesture.view?.transform)!.scaledBy(x: newScale, y: newScale);
    gesture.view?.transform = transform
    lastScale = gesture.scale  // Store the previous scale factor for the next pinch gesture call
  }
}
Creak answered 3/3, 2017 at 13:23 Comment(3)
try to explain clearly...whats is lastScale whats new scale here, where did you declared them,what's their subclass – Fairley
Can you please add more detail here. Code is not complete. whats is lastScale whats new scale here? – Sleuthhound
How to back to original size when end pinching @rajeshkumar-r – Neighbor
K
5

Swift 3 solution

This is the code I used. I added imageView to scrollView as a subview.

class ZoomViewController: UIViewController,UIScrollViewDelegate {

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

override func viewDidLoad() {

        super.viewDidLoad()
        scrollView.delegate = self

        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = 10.0//maximum zoom scale you want
        scrollView.zoomScale = 1.0

}

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
}
Knowles answered 24/7, 2017 at 10:30 Comment(0)
K
2

[September 2022, Swift 5]

One of the ways It seems to be working fine for me was embedding the image view into a scroll view which you can have a quick look at the library I'm sharing for better understanding to keep this answer short. I have built up this small library and used it in production apps. You can install or just copy-paste the files into your project. It is very easy to work with as an UIView and listen to its delegate for more options if you need them.

The library is called InteractiveImageView, it supports iOS 11.0 and up, link to GitHub: https://github.com/egzonpllana/InteractiveImageView

Karinekariotta answered 17/9, 2022 at 20:20 Comment(3)
Great, will try it soon. So is the library stable for production use right? – Fibered
Yes, it works like all good! – Karinekariotta
Great, will replace current ImageScrollView library with yours. Toggle-view option is exactly what I am missing with current library. – Fibered
M
1

I think the biggest problem is at the end of your func, you have sender.scale = 1. If you remove that line of code, your image shouldn't just bounce back each time.

Meso answered 26/1, 2017 at 23:22 Comment(0)
S
1

This is an old question but I don't see any answers that explain what is wrong with the original code.

This line:

let currentScale = self.view.frame.size.width / self.view.bounds.size.width

Is working on the main view rather than the imageView so the scale calculation is always ~1

This simple change makes it behave as expected

let currentScale = sender.view!.frame.size.width / sender.view!.bounds.size.width

by changing self to sender (and forcing view to unwrap) the scale calculation works as expected.

Sedgewake answered 4/10, 2018 at 7:51 Comment(0)
C
0

I ended up here, probably searching the wrong way.

I was after having my imageView in contentMode = .centre. But I was judging it too zoomed in and I was searching a way to zoom it out. Here's how:

    self.imageView.contentScaleFactor = 3

1 is as if you were doing anything. More that 1 zooms out... 3 works for me but you need to test it out.

Calender answered 26/10, 2018 at 23:15 Comment(0)
A
0

Confirmed working with Xcode 15 | iOS 17 | Swift 5 | Storyboard

I found this YouTube video (https://www.youtube.com/watch?v=0Tz0vI721c8) to be a much better example of what to do. It helps specifically with setting up the correct constraints.

Aldora answered 18/10, 2023 at 21:18 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.