72

I'm trying to rotate an UIView a few radians but after applying the transformation it doesn't look to be keeping its size. What's the proper way to achieve this?

Here's what I'm doing and what I get (Blue box with the arrow is the View I'm trying to rotate -- it should keep same aspect as red box behind):

#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)

double rads = DEGREES_TO_RADIANS(240);
CGAffineTransform transform = CGAffineTransformRotate(CGAffineTransformIdentity, rads);
self.arrowView.transform = transform;

enter image description here

Thanks!

Diego Acosta
  • 1,737
  • 3
  • 16
  • 21
  • 1
    Did you forget to invoke your macro? E.g. CGAffineTransform transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(45)); – Marco Jan 26 '14 at 23:40
  • 1
    @Marco Sorry, I'm editing the question. I'm not using the macro but still not working as intended. – Diego Acosta Jan 26 '14 at 23:41
  • 1
    Well you need to because the parameter for CGAffineTransformMakeRotation is in radians. – Marco Jan 26 '14 at 23:43
  • 1
    @Marco you're right, my bad. Now it doesn't seem to be keeping its center the issue, however its still being resized after rotation. I've updated the code accordingly on the question. – Diego Acosta Jan 27 '14 at 00:07

10 Answers10

63

I avoid using macros unless necessary. This works perfectly well

float degrees = 20; //the value in degrees
view.transform = CGAffineTransformMakeRotation(degrees * M_PI/180);
Shaheen Ghiassy
  • 7,397
  • 3
  • 40
  • 40
50

Swift + extension are your friends!!

// MARK: - UIView Extension
extension UIView {

    /**
       Rotate a view by specified degrees
       parameter angle: angle in degrees
     */

    func rotate(angle: CGFloat) {
        let radians = angle / 180.0 * CGFloat.pi
        let rotation = CGAffineTransformRotate(self.transform, radians);
        self.transform = rotation
    }

}

In this way, anywhere in your code:

let view = UIView(frame: CGRectMake(0, 0, 100, 100))
view.backgroundColor = UIcolor.redColor() 
view.rotate(angle:90)
Community
  • 1
  • 1
Luca Davanzo
  • 21,000
  • 15
  • 120
  • 146
  • 2
    I would rename it to be called `setRotation` as `rotate` sounds like you are rotating it by a certain angle. I.e. calling `rotate` twice would rotate it twice which it does not do. – Sunkas May 29 '19 at 07:21
32

You're probably hitting a problem with Autolayout. You probably have constraints on the rotated view pinning it to the edges of the superview. When the transform is applied, Autolayout is updating the view's size to still fit within the superview.

You can experiment with different constraints (e.g. pinning the centre of the view to the centre of another view, and pinning the width and height to constant values) or turn Autolayout off for the rotated view, or, if these don't work or don't suit your needs, use a container view which is laid out under Autolayout, and add your rotating view to this, without using Autolayout.

This can only be done in code - you can make individual views subject to Autolayout or not by setting translatesAutoresizingMasksIntoConstraints to NO (Autolayout on) or YES (Autolayout off). You'll need to set the appropriate autoresizing masks if you switch a view from one to the other.

jrturton
  • 118,105
  • 32
  • 252
  • 268
  • Thanks!. This is exactly what I was looking for. Adding this line solved my issue: [self.arrowView setTranslatesAutoresizingMaskIntoConstraints:YES]; Isn't it any way to visualize this or change from IB? – Diego Acosta Jan 27 '14 at 10:52
  • 1
    With IB, it's all auto layout or nothing auto layout. Glad it worked, it was a bit of a guess but I had the exact same issue a couple of days ago. – jrturton Jan 27 '14 at 11:38
  • 1
    `translatesAutoresizingMaskIntoConstraints` not `translatesAutoresizingMasksIntoConstraints` – James Nelson Jul 07 '16 at 18:44
28

Update Luca Davanzo's answer with Swift 4:

/**
 Rotate a view by specified degrees

 - parameter angle: angle in degrees
 */
func rotate(angle: CGFloat) {
    let radians = angle / 180.0 * CGFloat.pi
    let rotation = self.transform.rotated(by: radians)
    self.transform = rotation
}
Robin Stewart
  • 3,147
  • 20
  • 29
Bill Chan
  • 3,199
  • 36
  • 32
12

The CGAffineTransformRotate transformation rotates from an existing affine transform. The fact that you are using CGAffineTransformIdentity might be the issue. You must specify the current transform of your view.

#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
...
double rads = DEGREES_TO_RADIANS(240);
CGAffineTransform transform = CGAffineTransformRotate(self.arrowView.transform, rads);
self.arrowView.transform = transform;

Also, you might want to consider:

self.arrowView.transform = CGAffineTransformMakeRotation(rads);

EDIT : If you can, share what you kind of transformation (animated/inanimate , single/iterative) you want to achieve. I believe there might be a better, optimized way of doing this.

n00bProgrammer
  • 4,261
  • 3
  • 32
  • 60
8

Swift 5:

extension UIView {
    func setTransformRotation(toDegrees angleInDegrees: CGFloat) {
        let angleInRadians = angleInDegrees / 180.0 * CGFloat.pi
        let rotation = self.transform.rotated(by: angleInRadians)
        self.transform = rotation
    }
}
Sunkas
  • 9,542
  • 6
  • 62
  • 102
6

Try with this code:

#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)

double rads = DEGREES_TO_RADIANS(240);
self.arrowView.layer.transform = CATransform3DMakeRotation(rads, 0, 0, 1);
Abhinav
  • 37,684
  • 43
  • 191
  • 309
3

Swift5

Rotate UIView Upside down

let degrees:CGFloat = -180 //angle to convert upside down
rotatingUI.transform = CGAffineTransform(rotationAngle: degrees * CGFloat(Double.pi)/180);
    
Lahiru Pinto
  • 1,621
  • 19
  • 20
2

On Swift 3:

let degrees: CGFloat = -90
let radians = CGFloat(__sinpi(degrees.native/180.0))
view.transform = CGAffineTransform(rotationAngle: radians)

I'm using -90 because of this specific part of the documentation about the rotationAngle you have to pass to CGAffineTransform:

The angle, in radians, by which this matrix rotates the coordinate system axes. In iOS, a positive value specifies counterclockwise rotation and a negative value specifies clockwise rotation.

Bruno Delgado
  • 594
  • 4
  • 8
0

To my mind you need to calculate the center of your triangle and rotate around this point. Now you rotate the arrow around the center of your square.

See: One step affine transform for rotation around a point?

I Hope it will help you.

Community
  • 1
  • 1
Mike
  • 481
  • 10
  • 24
  • Thanks for linking to that answer, I've tried to implement that solution, however the arrow is still getting resized after rotation. – Diego Acosta Jan 27 '14 at 01:01