I find the scale of UIPinchGestureRecognizer is confusing, so I did two experiments below, two ways to do the same thing.
gestureRecognizer.scale
start with 1.0 at the beginning of pinch (gestureRecognizer.state == .began), and gestureRecognizer.scale
in later state (.changed or .end) is always based on that, for example, if the view size is view_size
at the beginning of pinch (might not be the same with the original size orig_view_size
), gestureRecognizer.scale
always starts with 1.0, and if it becomes 2.0 later, it's size will be 2 * view_size
, so the scale always based on that when the pinch starts. And we can get the scale at the beginning of pinch (gestureRecognizer.state == .began) lastScale = self.imageView.frame.width/self.imageView.bounds.size.width
, so the scale of the original image now should be lastScale * gestureRecognizer.scale
-
lastScale
: The scale of last round of Pinch, a round of Pinch is from state.start to state.end, and the scale is based on the original view size. -
gestureRecognizer.scale
: current scale, based on the view size after last round of Pinch. -
currentScale
: current scale, based on the orignial view size. -
newScale
: new scale, based on the orignial view size.newScale = lastScale * gestureRecognizer.scale
, and you can limit the scale of the view by comparing the limitation withnewScale
.
var lastScale:CGFloat = 1.0
@objc func handlePinch(_ gestureRecognizer: UIPinchGestureRecognizer) {
var newScale = gestureRecognizer.scale
if gestureRecognizer.state == .began {
lastScale = self.imageView.frame.width/self.imageView.bounds.size.width
}
newScale = newScale * lastScale
if newScale < minScale {
newScale = minScale
} else if newScale > maxScale {
newScale = maxScale
}
let currentScale = self.imageView.frame.width/self.imageView.bounds.size.width
self.imageView.transform = CGAffineTransform(scaleX: newScale, y: newScale)
print("last Scale: \(lastScale), current scale: \(currentScale), new scale: \(newScale), gestureRecognizer.scale: \(gestureRecognizer.scale)")
}
gestureRecognizer.scale
start with 1.0 on each Pinch notification, this require you reset gestureRecognizer.scale = 1
in the code in the end of each notification handler, so now gestureRecognizer.scale
is based on the view size of last Pinch notification, NOT based on the view size at the beginning of pinch. This is the most important difference with method 1. And since we don't rely on the scale of last round, we don't need lastScale
anymore.
-
currentScale
: current scale, based on the orignial view size. -
gestureRecognizer.scale
: new scale, based on the view size of last Pinch (not the last round), the scale value based on the orignial view size will becurrentScale * gestureRecognizer.scale
And we use transform.scaledBy
now, which use the scale based on view size of last Pinch (not the last round).
@objc func handlePinch(_ gestureRecognizer: UIPinchGestureRecognizer) {
let currentScale = self.imageView.frame.width/self.imageView.bounds.size.width
var newScale = gestureRecognizer.scale
if currentScale * gestureRecognizer.scale < minScale {
newScale = minScale / currentScale
} else if currentScale * gestureRecognizer.scale > maxScale {
newScale = maxScale / currentScale
}
self.imageView.transform = self.imageView.transform.scaledBy(x: newScale, y: newScale)
print("current scale: \(currentScale), new scale: \(newScale)")
gestureRecognizer.scale = 1
}