Reputation: 3708
I have a UIView which i want to scale and rotate via pan and pinch gesture. But issue is when i scale view and after then when i rotate it's resizing back to initial value before scaling.
extension UIView {
func addPinchGesture() {
var pinchGesture = UIPinchGestureRecognizer()
pinchGesture = UIPinchGestureRecognizer(target: self,
action: #selector(handlePinchGesture(_:)))
self.addGestureRecognizer(pinchGesture)
}
@objc func handlePinchGesture(_ sender: UIPinchGestureRecognizer) {
self.transform = self.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
}
}
// ROTATION
extension UIView {
func addRotationGesture() {
var rotationGesture = RotationGestureRecognizer()
rotationGesture = RotationGestureRecognizer(target: self,
action: #selector(handleRotationGesture(_:)))
self.addGestureRecognizer(rotationGesture)
}
@objc func handleRotationGesture(_ sender: RotationGestureRecognizer) {
var originalRotation = CGFloat()
switch sender.state {
case .began:
sender.rotation = sender.lastRotation
originalRotation = sender.rotation
case .changed:
let newRotation = sender.rotation + originalRotation
self.transform = CGAffineTransform(rotationAngle: newRotation) // Rotation is fine but it is resizing view
// self.transform = self.transform.rotated(by: newRotation / CGFloat(180 * Double.pi)) // NOT WORKING i.e. irregular rotation
case .ended:
sender.lastRotation = sender.rotation
default:
break
}
}
}
I want it to be rotate without affecting view size. How can i achieve that?
Upvotes: 0
Views: 2671
Reputation: 1287
I was facing the same issue Once I pinch from a finger, then after I rotate from a button it automatically scales down from the current but I set logic below.
@objc func rotateViewPanGesture(_ recognizer: UIPanGestureRecognizer) {
touchLocation = recognizer.location(in: superview)
let center = CGRectGetCenter(frame)
switch recognizer.state {
case .began:
deltaAngle = atan2(touchLocation!.y - center.y, touchLocation!.x - center.x) - CGAffineTrasformGetAngle(transform)
initialBounds = bounds
initialDistance = CGpointGetDistance(center, point2: touchLocation!)
case .changed:
let ang = atan2(touchLocation!.y - center.y, touchLocation!.x - center.x)
let angleDiff = deltaAngle! - ang
let a = transform.a
let b = transform.b
let c = transform.c
let d = transform.d
let sx = sqrt(a * a + b * b)
let sy = sqrt(c * c + d * d)
let currentScale = CGPoint(x: sx, y: sy)
let scale = CGAffineTransform(scaleX: currentScale.x, y: currentScale.y)
self.transform = scale.rotated(by: -angleDiff)
layoutIfNeeded()
case .ended:
print("end gesture status")
default:break
}
}
Upvotes: 0
Reputation: 21144
You are resetting the scale transform of view when applying rotation transform. Create a property to hold original scale of the view.
var currentScale: CGFloat = 0
And when pinch is done, store the currentScale value to current scale. Then when rotating also use this scale, before applying the rotation.
let scaleTransform = CGAffineTransform(scaleX: currentScale, y: currentScale)
let concatenatedTransform = scaleTransform.rotated(by: newRotation)
self.transform = concatenatedTransform
You are using extension to add gesture recognizers, for that reason you cannot store currentScale. You can also get the scale values of view from current transform values. Here is how your code would look like,
extension UIView {
var currentScale: CGPoint {
let a = transform.a
let b = transform.b
let c = transform.c
let d = transform.d
let sx = sqrt(a * a + b * b)
let sy = sqrt(c * c + d * d)
return CGPoint(x: sx, y: sy)
}
func addPinchGesture() {
var pinchGesture = UIPinchGestureRecognizer()
pinchGesture = UIPinchGestureRecognizer(target: self,
action: #selector(handlePinchGesture(_:)))
self.addGestureRecognizer(pinchGesture)
}
@objc func handlePinchGesture(_ sender: UIPinchGestureRecognizer) {
self.transform = self.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
}
}
// ROTATION
extension UIView {
func addRotationGesture() {
var rotationGesture = RotationGestureRecognizer()
rotationGesture = RotationGestureRecognizer(target: self,
action: #selector(handleRotationGesture(_:)))
self.addGestureRecognizer(rotationGesture)
}
@objc func handleRotationGesture(_ sender: RotationGestureRecognizer) {
var originalRotation = CGFloat()
switch sender.state {
case .began:
sender.rotation = sender.lastRotation
originalRotation = sender.rotation
case .changed:
let scale = CGAffineTransform(scaleX: currentScale.x, y: currentScale.y)
let newRotation = sender.rotation + originalRotation
self.transform = scale.rotated(by: newRotation)
case .ended:
sender.lastRotation = sender.rotation
default:
break
}
}
}
I used this answer as a reference for extracting the scale value.
Upvotes: 1