13

I've read the following, from One step affine transform for rotation around a point?:

CGAffineTransform transform = CGAffineTransformMakeTranslation(x, y);
transform = CGAffineTransformRotate(transform, a);
transform = CGAffineTransformTranslate(transform,-x,-y);

However, when I do this the images transformed are all over the map, and rarely over the screen.

I've tried a bit to make a system where it will place images; if you run a loop and place several values, a spiral appears. However, while I might be able to eventually untangle the relationship between a rotation of images and the points I wanted them to rotate about, I wanted to ask for the best solution to "I have an image here; I consider this point to be its center; I want it to be rotated by this amount around its center but not otherwise displaced."

I am trying to make a modified port of http://JonathansCorner.com/ancient-clock/, and right now I am trying to place the hour hand. All attempts to do the song and dance above, and translate it so its center is at the desired center, have failed.

How, for the hands of this clock, can I say "I want the hands in the following rotations" and have them appropriately placed around the center?

--EDIT--

The crucial part of this code, edited in an attempt to use layers in response to a comment, is:

UIImage *hourHandImage = [UIImage imageNamed:@"hour-hand.png"];
UIImageView *hourHandView = [[UIImageView alloc] initWithImage:hourHandImage];
float hourRotation = .5;
hourHandImage = [UIImage imageNamed:@"hour-hand.png"];
hourHandView = [[UIImageView alloc] initWithImage:hourHandImage];
CGAffineTransform transform = CGAffineTransformMakeTranslation(centerX - 21, -centerY - 121);
// transform = CGAffineTransformRotate(transform, hourRotation);
// transform = CGAffineTransformTranslate(transform, 2 * centerX, 2 * centerY);
hourHandView.transform = transform;
hourHandView.layer.anchorPoint = CGPointMake(centerX, centerY);
hourHandView.layer.affineTransform = CGAffineTransformRotate(transform, hourRotation);
[self.view addSubview:hourHandView];

Thanks,

--EDIT--

I now have something pared down; if I follow some of the instructions, I have:

CGAffineTransform transform = CGAffineTransformMakeTranslation(100 -21, 100 -121);
transform = CGAffineTransformRotate(transform, hour / 12.0 * M_PI * 2.0);
transform = CGAffineTransformTranslate(transform, -330, 330);
hourHandView.transform = transform;

I'd like to work on getting those numbers out, but this has an hour of 9:00 correctly displayed.

Thanks for all your help!

Community
  • 1
  • 1
Christos Hayward
  • 5,777
  • 17
  • 58
  • 113
  • Do you have a couple of example images you could post? What are you using as `x` and `y` here? They should be half the width and height, respectively, of the bounding rectangle of the thing you're trying to rotate. – jscs Oct 10 '13 at 19:26
  • Also, what's `a`? Note that it needs to be expressed in radians, not degrees. – jscs Oct 10 '13 at 19:30
  • All geometric manipulations to the view occur around the anchor point. Example, if anchorPoint={0.5,0.5} (center), the view rotates as if there was a z-axis right through the center. Clock hands rotates almost around the tip, so you need something like hourHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f); – Jano Oct 10 '13 at 19:39
  • @Josh, I used as x and y for where I would place the center of the clock hand - picked out with an image editor as the center of a clock hand is not usually the center of its bounding box. a has been expressed in radians, and I have tried a few different values trying to map out its behavior; but chiefly .5 as a starting point and I have only given angles in radians. – Christos Hayward Oct 10 '13 at 19:48
  • As far as images, what displays in the orientation I'm working on first is at http://JonathansCorner.com/project/tracking_hands.png. Right now the hour hand is off the screen; it's just a nice-looking Steampunk clock face with no hands. – Christos Hayward Oct 10 '13 at 19:51
  • @JonathanHayward anchorPoint is a CGPoint that indicates where inside the bounds rectangle the layer’s position is. **Its value is in the range {0.0,0.0} – {1.0,1.0}**. Example: Top left = {0,0}, center = {0.5,0.5}, bottom right = {1,1}. Set 0.5,0.9 as in my answer below. – Jano Oct 10 '13 at 20:22
  • there is a far, far easier way to do this, which is for more solid and UIKit-like https://stackoverflow.com/a/74722372/294884 – Fattie Dec 07 '22 at 20:15

4 Answers4

13

A rotation is always done around (0,0).

To translate around a point you FIRST need to translate the rotate point to (0,0) and then rotate it, and then translate it back.

So it should be:

// cx, cy is center of screen
// move (cx,cy) to (0,0)  
CGAffineTransform transform = CGAffineTransformMakeTranslation(-cx, -cy);

// roate around (0,0)  
transform = CGAffineTransformRotate(transform, angleRadians);

// mov e (0,0) back to (cx,cy)  
transform = CGAffineTransformTranslate(transform,cx,cy);
AlexWien
  • 28,470
  • 6
  • 53
  • 83
10

This line:

CGAffineTransform transform = CGAffineTransformMakeTranslation(x, y);

moves the image by x, y.

(Note-- you will spin around the control point, whatever you've set that to be)

this line:

transform = CGAffineTransformRotate(transform, a);

rotates it around the control point.

If your control point is in the top left hand corner (the default), it will spin around the top lefthand corner.

You need to set this:

[self layer].anchorPoint = CGPointMake(0.5, 0.5);

to get it to spin around the center of the layer.

meaning-matters
  • 21,929
  • 10
  • 82
  • 142
HalR
  • 11,411
  • 5
  • 48
  • 80
  • 1
    It seems you also have not understood how trabsformations work: Mak etranslation creates an trans matrix which moves all points by +x,+y; The roatate makes an concatenation of the existing transform matrix and adds an rotation matrix around (0,0). If you set the anchor point then ios in case of rotations makes two trandformatin matrixes for you: first trans the anchor to (0,0) the do your roattion, then translate back. – AlexWien Oct 10 '13 at 19:45
8
hourImageView.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
CGFloat angle = hour / 12.0 * M_PI * 2.0;
hourHand.transform = CGAffineTransformMakeRotation(angle);
Jano
  • 62,815
  • 21
  • 164
  • 192
3

Swift 5 version from Alex's answer

A rotation is always done around (0,0).

To translate around a point you FIRST need to translate the rotate point to (0,0) and then rotate it, and then translate it back.

So it should be:

// centerX, centerY is center of screen
// move (centerX,centerY) to (0,0)  
var myTransform = CGAffineTransform(translationX: -centerX, y: -centerY)

// rotate around (0,0)  
myTransform = myTransform.concatenating(CGAffineTransform(rotationAngle: angleInRadians))

// move (0,0) back to (cx,cy)  
myTransform = myTransform.concatenating(CGAffineTransform(translationX: centerX, y: centerY))

myView.transform = myTransform
Aaron Halvorsen
  • 2,610
  • 1
  • 23
  • 31
  • 1
    Single line... `let transform = CGAffineTransform(translationX: center.x, y: center.y).rotated(by: angle).translatedBy(x: -center.x, y: -center.y)` – emrahgunduz Aug 18 '20 at 07:58