I'm trying to do simple rotations in Objective-C and there are a couple problems right off the bat. One thing is the inconsistency I get from CGAffineTransformRotate and how M_PI is inconsistent when used inside that function.
Suppose I do this, and attach it to a button. When I press it once it will rotate 180 degrees counterclockwise (good and follows the documentation) but when I press it again, it will rotate 180 clockwise even though the value is not negative. Changing M_PI to -M_PI does the exact same thing with no differences in the rotation:
[UIView animateWithDuration:secs delay:0.0 options:option
animations:^{
self.transform = CGAffineTransformRotate(self.transform, M_PI); //Inconsistent
} completion:nil];
Now suppose I changed M_PI to 3.141593, which is the value that M_PI contains when I print it. Now, when I press the button, it works completely fine. Both times, it'll rotate 180 degrees counter clockwise. When I change it to -3.141593, it will work completely good too, clockwise:
self.transform = CGAffineTransformRotate(self.transform, 3.141593); //Works
When I play with it more, the behavior gets more weird.
Suppose I want to rotate 90 degrees (pi/2). M_PI now has the same behavior as using a value but the rotation is opposite to what it should be:
//Should be Clockwise but rotates CounterClockwise
self.transform = CGAffineTransformRotate(self.transform, -M_PI/2);
self.transform = CGAffineTransformRotate(self.transform, -1.5707965);
//Should be CounterClockwise but rotates Clockwise
self.transform = CGAffineTransformRotate(self.transform, M_PI/2);
self.transform = CGAffineTransformRotate(self.transform, 1.5707965);
And if I want to rotate anything over 180 degrees(PI), the behavior is just to rotate the shortest route even though I specify a positive or negative rotation. When I rotate 360 Degrees (2PI), it doesn't even rotate.
Why these things happen and what can I do to make it more consistent?
And a secondary question I have is how can I rotate things 270 and 360 degrees.
The problem is you can't really control the rotation direction when using animateWithDuration. It will always take the shortest route. When rotating 180 degrees the behavior is undetermined because in theory there are 2 possibilities for the shortest route. This also explains why you can't rotate more than 180 degrees. If you want to have more control over the animation or do more complex animations use a CAKeyframeAnimation.
As phix23 said: is always take the shortest route. So You can avoid this by using this trick for example to go clockwise full circle: you must divide circle to for example 3 control points:
[UIView animateWithDuration:secs delay:0.0 options:UIViewAnimationOptionCurveLinear
animations:^{
self.transform = CGAffineTransformRotate(self.transform, M_PI_2/3);
} completion:(BOOL end)
{
[UIView [UIView animateWithDuration:secs delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.transform = CGAffineTransformRotate(self.transform, -M_PI_2/3);
} completion:(BOOL end)
{
}
}
];
This code was not tested, so if there is problem, try to play with it. Hope this will help.
Related
I am trying to perform an animation that does three things at once: translates, rotates and changes the size of an image.
I can do two at once, translate and size. However, when I add in rotation at the end of the following code, it is ignored. And if I place it at the beginning of the code, the size change is ignored. I've read that you can do a composite transition with view.transform, however, I have not been able to get that to work.
Here is my current code:
CGPoint destPoint = CGPointMake(-100,-50);
float radians =[self Degrees2Radians:-35];
[UIView animateWithDuration:2
animations:^{
//TRANSLATE
self.imageView.center = CGPointMake(self.imageView.center.x + destPoint.x, self.imageView.center.y + destPoint.y);
//ROTATE
self.imageView.transform = CGAffineTransformMakeRotation(radians);
//SCALE
self.imageView.transform = CGAffineTransformMakeScale(0.2, 0.2); // here the final size will be 20%
}
completion:nil
];
}
Can anyone recommend way to get all three things to occur simultaneously.
Here is some code for swift that uses the transform property of the view, but I have not been able to find the equivalent in Objective-C.
view.transform= CGAffineTransform(scaleX: 1.5, y: 1.5)
view.transform = view.transform.rotated(by angle: CGFloat(45 * M_PI / 180))
Thanks in advance for any suggestions.
You can use CGAffineTransformRotate function on the existing transform to apply a rotation. You can also use CGAffineTransformTranslate and CGAffineTransformScale to apply translation and scaling. Please note that, order of the operations matter.
For example if you have an existing transform myTransform you can rotate it like:
myTransform = CGAffineTransformRotate(myTransform, M_PI / 2);
The operation does not affect the input variable, instead, it returns a new transform so make sure you use the return value of the function. That's why I started the line with myTransform = ....
More information is available at https://developer.apple.com/documentation/coregraphics/cgaffinetransform-rb5?language=objc.
I want to perform scale and rotate animation simultaneously but instead only scale animation is performed over time as intended, while rotation takes place instantly.
this is the code I use:
CGAffineTransform scale = CGAffineTransformMakeScale(scaleRatio, scaleRatio);
CGAffineTransform rotate = CGAffineTransformMakeRotation(rotationRatio);
CGAffineTransform entireAnimation = CGAffineTransformConcat(scale, rotate);
[UIView animateWithDuration:0.5f
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^(void){
view.transform = entireAnimation;
}completion:^(BOOL finished){
}];
even if I change concat order like this, issue remains the same:
CGAffineTransform entireAnimation = CGAffineTransformConcat(rotate,scale);
And the issue is: Instead of both scaling and rotating at the same time, my view is instantly rotated and scaling animation is performed over time.
How can I achieve simultaneous transformation animation ?
EDIT: Rotation doesn't work if rotation ratio is 2*M_PI. Why doesn't the 360 degrees rotation work and how to make it work?
I am simply trying to spin a UIImageView 360 degrees clockwise using this:
#define DEGREES_TO_RADIANS(angle) (angle / 180.0 * M_PI)
and this
imageView.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(360));
However imageView doesn't spin at all even those it is being called. Is there something wrong I am doing? I do not want to use CAAnimations so please don't recommend me to do so.
Thanks!
The problem is that Core Animation will apply animations by finding the most direct route from the current state to the new state. Rotation by 0 degrees is the same as rotation by 360 degrees so the most direct route to your final transform is to do absolutely nothing.
Two steps of 180 degrees would be problematic because there are two equally direct routes from 0 to 180 degrees and Core Animation could pick either. So you probably need to break your animation into three steps. Do the first with a UIViewAnimationOptionCurveEaseIn, the second with UIViewAnimationOptionCurveLinear and the final with UIViewAnimationOptionCurveEaseOut.
Perhaps because the transform isn't happening over a duration of time. It seems to me that as long as it isn't perceivable over time you may need to perform the transform or sequence of transforms over a fraction of time.
Here is the code inspired by Tommy's answer.
I used
NSInteger step;
to keep track of current rotated degree of the image view.
- (void)startAnimation
{
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^{
imageView.transform = CGAffineTransformMakeRotation(120 * step / 180.0f * M_PI);
}
completion:^(BOOL finished) {
step++;
// rotation completed, reset for the next round
if (step == 4){
step = 1;
}
// perform the next rotation animation
[self startAnimation];
}];
}
I'm trying to do a simple horizontal flip animation with a bounce effect.
[UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationCurveEaseIn animations:^{
front.layer.transform=CATransform3DMakeRotation(DEGREES_TO_RADIANS(90), -1.0,0.0,0.0); // flip halfway
}
completion:^(BOOL finished) { // swap view
front.alpha=0;
back.alpha=1;
[UIView animateWithDuration:0.5 animations:^{ // flip remaining + bounce
back.layer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(45), 1.0,0.0,0.0); // final + 45
}
completion:^(BOOL finished) {
[UIView animateWithDuration:0.25 animations:^{
back.layer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(-22.5), 1.0,0.0,0.0); // bounce back
}
completion:^(BOOL finished) {
[UIView animateWithDuration:0.125 animations:^{
back.layer.transform = CATransform3DMakeRotation(0, 1.0,0.0,0.0); // final
}];
}];
}];
}];
Works well except for the 'bounce'. The -22.5 rotation transitions to 0 and than back to 22.5 instead of continuing to -22.5.
I've tried various values and also including an intermediate nested block that transitions the bounce to '0' before going to negative. Didn't help. The rotation always animates to a positive instead of a negative angle.
Just as a test, changing the 'final + 45' to a negative angle does however stop the animation at the desired angle. So the angles themselves are ok.
Problem seems to be doing a 'counter clockwise' rotation starting from zero or going trough zero. Values smaller than zero than always get converted to a positive angle.
Independent if the above is the right technique for implementing a bounce effect, how would a (nested) layer animation be constructed that rotates via CATransform3DMakeRotation from a positive (45) to a negative angle (-45)?
You are rotating around the x-axis. Without a perspective on your transform, how would you see the difference between positive and negative angles? Without perspective, both positive and negative rotations look the same (the decrease the height). You can apply perspective to your transform by changing the value in the third row and fourth column of your transform matrix by setting .m34. Search for "Core Animation perspective" and you will find a good explanation.
I ran your code above (on a simple view, filled with orange).
To more easily be able to see the rotation and the difference between positive and negative angles, I changed it so that it rotates around the z-axis.
These are four screenshots that I took during the animation. As you can see, the negative angle works, unless I misunderstood what you were trying to do.
I expanded and moved a label (instructionLabel), then returned it to its original size but left it in the new position.
[UIView animateWithDuration:0.5
animations:^{
CGAffineTransform scale = CGAffineTransformMakeScale(2.0, 2.0);
CGAffineTransform translate = CGAffineTransformMakeTranslation(0.0, -70.0); // up 70
self.instructionsLabel.transform = CGAffineTransformConcat(scale,translate);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:0.25
animations:^{
CGAffineTransform scale = CGAffineTransformMakeScale(1.0,1.0);
CGAffineTransform translate = CGAffineTransformMakeTranslation(0,-70.0); //left in place up 70
self.instructionsLabel.transform = CGAffineTransformConcat(scale, translate);
}
completion:^(BOOL finished) {}
];
Later, I explicitly use CGPointMake to put the label back in its original spot, but it remains in the translated position (70 pts up from its original place).
instructionsLabel.frame = CGRectMake(384, 601, 655, 40);
//Adding this doesn't make any difference, in or out.
instructionsLabel.center=CGPointMake(384, 601);
I have verified by Breaks and NSLog that the CGPointMake and CGRectMake statements are reached...they just don't work after that affine transformation. Does anyone know why?
(I don't want to move the label back immediately after the translation routine, but I might have to if I can't figure out why the CGPointMake routine doesn't do it.)
Thanks for any suggestions.
-Rob
Unless Im mistaken one of the major reason for using affine transform to scale, move etc views is that, that you can later set transform to CGAffineTransformIdentity this will cancel out any transforms you have applied. I believe that your problem here is that you are setting center to what ever position it of before you applied translate transform. View indeed moves to that point + whatever transform is applied to that view. So just set transform identity.