How to enlarge hit area of UIGestureRecognizer? - iphone

I'm using few gesture recognizers on some views, but sometimes views are too small and it's hard to hit it. Using recognizers is necessary, so how can I enlarge hit area?

If you are doing this for a custom UIView, you should be able to override the hitTest:withEvent: method:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGRect frame = CGRectInset(self.bounds, -20, -20);
return CGRectContainsPoint(frame, point) ? self : nil;
}
The above code will add a 20 point border around the view. Tapping anywhere in that area (or on the view itself) will indicate a hit.

Swift version of #rmaddy answer:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let frame = self.bounds.insetBy(dx: -20, dy: -20);
return frame.contains(point) ? self : nil;
}

If you're using a UIImageView as a button, you can use the following extension (Swift 3.0):
extension UIImageView {
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.isHidden || !self.isUserInteractionEnabled || self.alpha < 0.01 { return nil }
let minimumHitArea = CGSize(width: 50, height: 50)
let buttonSize = self.bounds.size
let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0)
let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0)
let largerFrame = self.bounds.insetBy(dx: -widthToAdd / 2, dy: -heightToAdd / 2)
// perform hit test on larger frame
return (largerFrame.contains(point)) ? self : nil
}
}
Similar to the UIButton extension here

Related

Pinch and zoom UIView without transform [duplicate]

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
}
}
UIImageView pinch zoom with UIScrollView || image zooming ios in swift 3 and Xcode 8 letter Youtube video URL
set uiscrollview Delegate in storyboard
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
}
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
}
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
}
}
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
}
}
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()
}
}
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.
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
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
}
}
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
}
[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
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.
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.
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.

How do I not let UIViews overlap when I create them by tapping?

I am creating a calendar app, and I tap to create new events in a UIScrollView. Think of it like the apple calendar "day view" where you can create new events and view them in a list on a scroll view. Each event is a UIView with a TextField on it, and right now everything works but I'm not sure how to make it so the events don't overlap when I create them.
Is there a way to prevent UIViews from overlapping when I create them or move them? I only am moving them up or down, so I just need to keep them from overlapping on the y axis. Right now I tap to create them, but they can overlap. Is there a way to put them on the same plane or something so they can't be added on top of each other?
It is highly recommended you post your own code for us to understand the general direction you're heading towards. Otherwise, it could a wasted effort on both of our parts.
For example, you mentioned that "right now everything works", but I have no idea how you're moving the views in a vertical manner. I went ahead and subclassed the gesture recognizer so hopefully it fits the flow of your code.
import UIKit.UIGestureRecognizerSubclass
class ViewController: UIViewController {
let size = CGSize(width: 200, height: 100)
var v1: UIView!
var v2: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.v1 = UIView(frame: .init(origin: .init(x: 100, y: 100), size: size))
self.view.addSubview(self.v1)
self.v1.backgroundColor = .orange
self.v2 = UIView(frame: .init(origin: .init(x: 100, y: 300), size: size))
self.view.addSubview(v2)
self.view.addSubview(self.v2)
self.v2.backgroundColor = .purple
let verticalGesture = VerticalGesture(target: self, action: #selector(dragged))
self.v2.addGestureRecognizer(verticalGesture)
}
#objc func dragged(_ sender: VerticalGesture) {
}
}
class VerticalGesture: UIPanGestureRecognizer {
private var touch: UITouch!
private var currentView: UIView!
private var currentSuperview: UIView!
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
guard let firstTouch = touches.first, let currentView = self.view, let currentSuperview = currentView.superview else { return }
self.touch = firstTouch
self.currentView = currentView
self.currentSuperview = currentSuperview
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
var center = self.currentView.center
let currentLocation = self.touch.location(in: self.currentSuperview)
let previousLocation = self.touch.previousLocation(in: self.currentSuperview)
let yTranslation = currentLocation.y - previousLocation.y
center.y += yTranslation
self.view!.center = center
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
for subview in self.currentSuperview.subviews {
// this goes through all the sibling views including the non-calendar-event views i.e. calendar dates, calendar frame, etc
// so make sure to sort those out
if subview != self.currentView! && self.currentView!.frame.intersects(subview.frame) {
var center = self.currentView.center
let isHigher = subview.center.y >= center.y
// isHigher >= 0 means the current view that you've selected is located higher
// than the overlapping view below
if isHigher {
center = CGPoint(x: subview.center.x, y: subview.center.y - self.currentView.bounds.height)
self.view!.center = center
} else {
center = CGPoint(x: subview.center.x, y: subview.center.y + self.currentView.bounds.height)
self.view!.center = center
}
}
}
super.touchesEnded(touches, with: event)
}
}
Depending on whether the calendar event you're dragging is on the upper side or the lower side of the overlapping view, it will move to adjacent to it. Feel free to add a transform animation to it.

UIButton continue tracking even when out of bounds

I'm trying to track the touches on the button. But whenever the touch leaves the button frame, it stops tracking the touch all together.
I've tried using:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let relativeFrame = bounds
let hitTestInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
return UIEdgeInsetsInsetRect(relativeFrame, hitTestInsets).contains(point)
}
and
override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let minimalWidthAndHeight: CGFloat = 60
let buttonSize = frame.size
let widthToAdd = (minimalWidthAndHeight - buttonSize.width > 0) ? minimalWidthAndHeight - buttonSize.width : 0
let heightToAdd = (minimalWidthAndHeight - buttonSize.height > 0) ? minimalWidthAndHeight - buttonSize.height : 0
let largerFrame = CGRect(x: 0-(widthToAdd / 2), y: 0-(heightToAdd / 2), width: buttonSize.width + widthToAdd, height: buttonSize.height + heightToAdd)
return largerFrame.contains(point) ? self : nil
}
and I also tried using continueTracking(with:) but in all cases, whenever the touch moves away from the button frame, cancelTracking(with:) and touchesCancelled(with:) is called.
What do I have to do to make it keep tracking even when the touch leaves the frame of the button?

How to show tooltip on a point click in swift

I am drawing the lines using the following code. The line edges have dots and i want to show the tooltip when user click on the end dots.
The code snippet is below,
override func drawRect(rect: CGRect) {
// Drawing code
UIColor.brownColor().set()
let context = UIGraphicsGetCurrentContext()
//CGContextSetLineWidth(context, 5)
CGContextMoveToPoint(context, 50, 50)
CGContextAddLineToPoint(context,100, 200)
CGContextStrokePath(context)
// now add the circle on at the line edges
var point = CGPoint(x:50 , y:50)
point.x -= 5.0/2
point.y -= 5.0/2
var circle = UIBezierPath(ovalInRect: CGRect(origin: point, size: CGSize(width: 5.0,height: 5.0)))
circle.fill()
point = CGPoint(x:100 , y:200)
point.x -= 5.0/2
point.y -= 5.0/2
circle = UIBezierPath(ovalInRect: CGRect(origin: point, size: CGSize(width: 5.0,height: 5.0)))
circle.fill()
}
It is currently displaying the following image,
and i want to show the tooltip like below,
Does anybody have an idea how will i recognise that this particular dot is clicked and also how will i show the tooltip.
I am looking for a solution in swift.
Define a struct to hold points you have to touch, and the text to show:
struct TouchPoint {
var point: CGPoint // touch near here to show a tooltip
var tip: String // show this text when touched
}
then in the UIView subclass where you define drawRect, make somewhere to keep them:
var touchPoints: [TouchPoint] = [] // where we have to touch and what tooltip to show
drawRect can be called many times, so start fresh each time:
override func drawRect(rect: CGRect) {
touchPoints = []
// ...
// add a touchPoint for every place to touch
touchPoints.append(TouchPoint(point: point, tip: "point 1"))
}
You need to detect taps on the UIView, so add a gesture recognizer by changing its initialisation:
required init?(coder aDecoder: NSCoder) {
// standard init for a UIView wiht an added gesture recognizer
super.init(coder: aDecoder)
addGestureRecognizer(UITapGestureRecognizer(target: self, action: Selector("touched:")))
}
then you need a method to see whether touches are near touchpoints, and to show the right tooltip:
func touched(sender:AnyObject) {
let tapTolerance = CGFloat(20) // how close to the point to touch to see tooltip
let tipOffset = CGVector(dx: 10, dy: -10) // tooltip offset from point
let myTag = 1234 // random number not used elsewhere
guard let tap:CGPoint = (sender as? UITapGestureRecognizer)?.locationInView(self) else { print("touched: failed to find tap"); return }
for v in subviews where v.tag == myTag { v.removeFromSuperview() } // remove existing tooltips
let hitPoints:[TouchPoint] = touchPoints.filter({CGPointDistance($0.point, to: tap) < tapTolerance}) // list of tooltips near to tap
for h in hitPoints { // for each tooltip to show
let f = CGRect(origin: h.point+tipOffset, size: CGSize(width: 100, height: 20)) // fixed size label :-(
let l = UILabel(frame: f)
l.tag = myTag // just to be able to remove the tooltip later
l.text = h.tip // draw the text
addSubview(l) // add the label to the view
}
}
func CGPointDistanceSquared(from: CGPoint, to: CGPoint) -> CGFloat { return (from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y) }
func CGPointDistance(from: CGPoint, to: CGPoint) -> CGFloat { return sqrt(CGPointDistanceSquared(from, to: to)) }
and that incidentally uses a new version of the + operator to perform vector addition on CGPoint:
func +(left: CGPoint, right: CGVector) -> CGPoint { return CGPoint(x: left.x+right.dx, y: left.y+right.dy) }
and that works OK for me. Extra tweaks would be to compute the UILabel size from the text string, and move the UILabel so it didn't run off the side of the UIView at the edges. Good Luck!

UIImageView pinch zoom swift

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
}
}
UIImageView pinch zoom with UIScrollView || image zooming ios in swift 3 and Xcode 8 letter Youtube video URL
set uiscrollview Delegate in storyboard
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
}
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
}
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
}
}
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
}
}
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()
}
}
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.
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
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
}
}
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
}
[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
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.
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.
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.