4

I would like to make an IBOutlet rotate around a specific point in the parent view, currently i only know how to rotate it around an anchor point, however, i want to use a point outside the object's layer.

The rotation angle is calculated relative to the device heading from the point.

- (void)viewDidLoad{
[super viewDidLoad];
locationManager=[[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.headingFilter = 1;
locationManager.delegate=self;
[locationManager startUpdatingHeading];
[locationManager startUpdatingLocation];
compassImage.layer.anchorPoint=CGPointZero;
}

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{
    // Convert Degree to Radian and move the needle
    float oldRad =  -manager.heading.trueHeading * M_PI / 180.0f;
    float newRad =  -newHeading.trueHeading * M_PI / 180.0f;        
    CABasicAnimation *theAnimation;
    theAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    theAnimation.fromValue = [NSNumber numberWithFloat:oldRad];
    theAnimation.toValue=[NSNumber numberWithFloat:newRad];
    theAnimation.duration = 0.5f;
    [compassImage.layer addAnimation:theAnimation forKey:@"animateMyRotation"];
    compassImage.transform = CGAffineTransformMakeRotation(newRad);

    NSLog(@"%f (%f) => %f (%f)", manager.heading.trueHeading, oldRad, newHeading.trueHeading, newRad);

  }

How can i rotate the UIImageView around (x,y) by alpha?

user3157423
  • 51
  • 1
  • 4

2 Answers2

18

For rotating around a specific point (internal or external) you can change the anchor point of the layer and then apply a regular rotation transform animation similar to what I wrote about in this blog post.

You just need to be aware that the anchor point also affects where the layer appears on the screen. When you change the anchor point you will also have to change the position to make the layer appear in the same place on the screen.

Assuming that the layer is already placed in it's start position and that the point to rotate around is known, the anchor point and the position can be calculated like this (note that the anchor point is in the unit coordinate space of the layer's bounds (meaning that x and y range from 0 to 1 within the bounds)):

CGPoint rotationPoint = // The point we are rotating around

CGFloat minX   = CGRectGetMinX(view.frame);
CGFloat minY   = CGRectGetMinY(view.frame);
CGFloat width  = CGRectGetWidth(view.frame);
CGFloat height = CGRectGetHeight(view.frame);

CGPoint anchorPoint =  CGPointMake((rotationPoint.x-minX)/width,
                                   (rotationPoint.y-minY)/height);

view.layer.anchorPoint = anchorPoint;
view.layer.position = rotationPoint; 

Then you simply apply a rotation animation to it, for example:

CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotate.toValue = @(-M_PI_2); // The angle we are rotating to
rotate.duration = 1.0;

[view.layer addAnimation:rotate forKey:@"myRotationAnimation"];

Just note that you have changed the anchor point so the position is no longer in the center of the frame and if you apply other transforms (such as a scale) they will also be relative to the anchor point.

David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
  • How to apply new position to view's frame after animation ends? – Bhargav Soni Nov 05 '16 at 10:07
  • @BhargavSoni if you specify both `toValue` and `fromValue`, then it's enough to set the property to its end value before adding the animation to have the new values after the animation ends. – David Rönnqvist Nov 05 '16 at 10:17
  • You may also notice that that OP is setting the transform property as well as adding the animation in this question. – David Rönnqvist Nov 05 '16 at 10:19
  • Thanks. This worked wonderfully for me. Had to convert it to Swift which i'll paste below to save someone else some churn, but the concept seems solid. Also, in my situation i was rotating and animating a BezierPath so i had to offset the path coordinates by the rotation point but, it worked. – Trevis Thomas May 22 '17 at 09:09
  • @TrevisThomas If you want to rotate around the center of the shape you should be able to use the same code and give the `CAShapeLayer` a `bounds` (likely the path's `boundingBoxOfPath`). By default, the shape layer will have no size (0 width and height) but draw outside it's own bounds. This effectively makes it look like it's "center" is the upper left corner. – David Rönnqvist May 22 '17 at 13:50
  • Your link is 404 – Mikalai Daronin Jul 24 '18 at 17:33
  • 1
    @MikalaiDaronin Thanks for noticing that. It had an unintended trailing slash which has now been fixed. – David Rönnqvist Jul 24 '18 at 17:37
3

Translated David's answer to swift 3:

    let rotationPoint = CGPoint(x: layer.frame.width / 2.0, y: layer.frame.height / 2.0) // The point we are rotating around

    print(rotationPoint.debugDescription)
    let width = layer.frame.width
    let height = layer.frame.height
    let minX = layer.frame.minX
    let minY = layer.frame.minY

    let anchorPoint = CGPoint(x: (rotationPoint.x-minX)/width,
                              y: (rotationPoint.y-minY)/height)

    layer.anchorPoint = anchorPoint;
    layer.position = rotationPoint;
Trevis Thomas
  • 353
  • 3
  • 6