Abhishek Thapliyal
Abhishek Thapliyal

Reputation: 3708

iOS Swift: Rotate and Scale UIView without resizing

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

}

Before Scaling

enter image description here

After Scaling

enter image description here

After Rotation

enter image description here

I want it to be rotate without affecting view size. How can i achieve that?

Upvotes: 0

Views: 2671

Answers (2)

kalpesh
kalpesh

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

Sandeep
Sandeep

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

Related Questions