Setting a Rotation Point for Cgaffinetransformmakerotation Swift

Setting a rotation point for CGAffineTransformMakeRotation Swift

What you are looking for is the anchorPoint property of your views' layers. This property basically represents the handle that is be used while the view is being moved around.

It defaults to the center of your view so and that why you are always seeing your view rotating form the middle.

You can use this great method i've found here on SO to change the anchorPoint of you views to the top (use CGPointMake(0, 0.5f))

// Swift version
// Place this before you set the transform property
setAnchorPoint(CGPointMake(0, 0.5), view: imageOne)
setAnchorPoint(CGPointMake(0, 0.5), view: imageTwo)
setAnchorPoint(CGPointMake(0, 0.5), view: imageThree)

func setAnchorPoint(anchorPoint: CGPoint, view: UIView) {
var newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y)
var oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y)

newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)

var position : CGPoint = view.layer.position

position.x -= oldPoint.x
position.x += newPoint.x;

position.y -= oldPoint.y;
position.y += newPoint.y;

view.layer.position = position;
view.layer.anchorPoint = anchorPoint;
}

This is the objective c version of the same code

// Obj-c version
// Place this before you set the transform property
[self setAnchorPoint:CGPointMake(0, 0.5f) forView:imageOne];
[self setAnchorPoint:CGPointMake(0, 0.5f) forView:imageTwo];
[self setAnchorPoint:CGPointMake(0, 0.5f) forView:imageThree];

-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x,
view.bounds.size.height * anchorPoint.y);
CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x,
view.bounds.size.height * view.layer.anchorPoint.y);

newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);

CGPoint position = view.layer.position;

position.x -= oldPoint.x;
position.x += newPoint.x;

position.y -= oldPoint.y;
position.y += newPoint.y;

view.layer.position = position;
view.layer.anchorPoint = anchorPoint;
}

I'd encourage you to try to understand yourself what this code is doing once you have seen what the anchorPoint property represents. (the method is by user: Anand K)

Why does a view changes position when rotating it using CGAffineTransformMakeRotation

I am adding another answer due to @fs_tigre request. The problem is with the auto layouts in your xib file, unfortunately is it unknown why that affects the transform.

Now here is the steps I did to solve the issue:

1- first you need to get rid off your auto layout (yes, you have to)

uncheck Use Autolayout

uncheck Use Autolayout

2- remove all constraints and autoresizing masks for your view that will be rotated, as in the screenshot
(Here I have my blue box, see on the right autoresizing, nothing is selected)

Sample Image

I have made some changes for your rotation's code

self.someView.layer.anchorPoint = CGPointMake(0.5, 0.5);
// one degree = pi/180. so...
// rotate by 90
CGFloat radians = (M_PI/180) * 90;
[UIView animateWithDuration:1.0 animations:^{
self.someView.transform = CGAffineTransformRotate(self.someView.transform, radians);
}];

Click rotate and see the magic :)

Rotate CIImage in Swift 2 using CGAffineTransformMakeRotation

That transform rotates around the image's origin. This version sets the pivot point to the centre:

var tx = CGAffineTransformMakeTranslation(
image.extent.width / 2,
image.extent.height / 2)

tx = CGAffineTransformRotate(
tx,
CGFloat(M_PI_2))

tx = CGAffineTransformTranslate(
tx,
-image.extent.width / 2,
-image.extent.height / 2)

var transformImage = CIFilter(
name: "CIAffineTransform",
withInputParameters: [
kCIInputImageKey: image,
kCIInputTransformKey: NSValue(CGAffineTransform: tx)])!.outputImage!

Simon

About CGAffineTransformMakeRotation rotation direction?

This is an error in documentation.

Here they describe matrix and provide equations which they use to do the transform:

Sample Image

If you do the math, you'll see that for positive angles, it gives rotation counterclockwise when Y axis direction is upwards as in OS X and clockwise when Y axis directed downwards as in iOS

For example you may try to transform point (5,0) with 90 degrees, you will get (0,5), which means clockwise for iOS and counterclockwise for OS X.

The same piece of documentation still have the (false) part you quoted:

In iOS, a positive value specifies counterclockwise
rotation and a negative value specifies clockwise rotation. In OS X, a
positive value specifies clockwise rotation and a negative value
specifies counterclockwise rotation.

Which is clearly wrong, because the equations (which are the part of the same document and are pretty standard rotation equations) say the opposite.

How can I reliably rotate an image around a point?

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.

Rotate UIView around its center keeping its size

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.

What is the best way to rotate a CGPoint on a grid?

You can also use that:

rotatedPoint = CGPointApplyAffineTransform(initialPoint, CGAffineTransformMakeRotation(angle));

EDIT: to perform rotation around custom point you must do like Adam described in his answer. Using CGAffineTransform it must look something like:

CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(customCenter.x, customCenter.y);
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(angle);

CGAffineTransform customRotation = CGAffineTransformConcat(CGAffineTransformConcat( CGAffineTransformInvert(translateTransform), rotationTransform), translateTransform);

rotatedPoint = CGPointApplyAffineTransform(initialPoint, customRotation);

How to rotate a Button around a point or the center of a UIView in Xcode?

If you look at earth and sun, there is no rotation involved: The letter "E" (the earth) will never rotate, e.g. the "north pole" will always point to the same direction. Anti-clockwise rotation looks like this:

                 E            
=> | =>
S-E S E-S

(letter "E" is not rotated).

To do this, your animation will have to follow a circle around the sun.
Create a view controller with two buttons (sunButton and earthButton), and upon touching the sun, the rotation will start or stop:

// some helper extension
extension CGPoint {
static func distanceBetween(point p1: CGPoint,
andPoint p2: CGPoint) -> CGFloat {
return sqrt(pow((p2.x - p1.x), 2) + pow((p2.y - p1.y), 2))
}

static func angle(from fromPoint: CGPoint, to toPoint: CGPoint) -> CGFloat {
let dx: CGFloat = fromPoint.x - toPoint.x
let dy: CGFloat = fromPoint.y - toPoint.y
let radians: CGFloat = atan2(dy, dx)
return radians
}

}

class ViewController: UIViewController {

@IBOutlet weak var sunButton: UIButton!
@IBOutlet weak var earthButton: UIButton!

var isRevolving = false

@IBAction func sunPressed(_ sender: Any) {
if (!isRevolving) {
startRevolving()
} else {
stopRevolving()
}
}

func startRevolving() {
isRevolving = true
let sunCenter = sunButton.center
let earthCenter = earthButton.center

let distance = CGPoint.distanceBetween(point: sunCenter, andPoint: earthCenter)
var angle = CGPoint.angle(from: sunCenter, to: earthCenter)
angle = .pi + angle

let circlePath = UIBezierPath(arcCenter: sunCenter, radius: distance, startAngle: angle + .pi*2, endAngle: angle, clockwise: false)
// for clockwise rotation, use:
//let circlePath = UIBezierPath(arcCenter: sunCenter, radius: distance, startAngle: angle, endAngle: angle + .pi*2, clockwise: true)

let animation = CAKeyframeAnimation(keyPath: #keyPath(CALayer.position))
animation.duration = 5
animation.repeatCount = MAXFLOAT
animation.path = circlePath.cgPath

earthButton.layer.add(animation, forKey: nil)
}

func stopRevolving() {
isRevolving = false
if let currentPosition = earthButton.layer.presentation()?.position {
earthButton.center = currentPosition
}
earthButton.layer.removeAllAnimations()
}

}

Rotate UIImageView around its center point?

I found out that the code I need to accomplish what I needed was simpler than the one given by @Albin Joseph, but it did point me to the right direction.
My animation needs to pick up where it left off and rotate to a new position. Some times it will be animated and sometimes it won't. So hence the code:

CGFloat duration = animated ? 0.5 : 0.01;

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.fromValue = [[self.turnIndicatorImage.layer presentationLayer] valueForKeyPath:@"transform.rotation.z"];
animation.toValue = angle;
animation.duration = duration;
animation.fillMode = kCAFillModeForwards;
animation.repeatCount = 0;
animation.removedOnCompletion = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[self.turnIndicatorImage.layer addAnimation:animation forKey:@"transform.rotation.z"];


Related Topics



Leave a reply



Submit