CATransform3DMakeRotation animating trough zero (from -45 to +45 angle)? - ios

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.

Related

CGAffineTransformMakeScale causes rotation

I am facing this weird issue, where CGAffineTransformMakeScale is causing rotation. The name suggests that it should only cause scaling, but that's not the case.
[UIView animateWithDuration:1.0 animations:^{
self.logoView.transform = CGAffineTransformMakeScale(6.0, 6.0);
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:3.0 animations:^{
self.logoView.transform = CGAffineTransformMakeScale(-6.0, -6.0);
} completion:nil];
}
}];
I would assume that the view should scale 6x and scale back 6x. However, the second animation causes a 90-degree anti-clockwise rotation of the image! Can anyone explain what's going on?
Use the relative scaling transform rather than making an absolute one. So:
self.logoView.transform = CGAffineTransformScale(self.logoView.transform,
6, 6)
you shouldn't be scaling to CGAffineTransformMakeScale(-6.0,-6.0) in order to reverse what you already did (unless thats what you want, but i doubt it) but instead animate back to CGAffineTransformMakeScale(1.0,1.0). a shortcut is CGAffineTransformIdentity constant, which is an empty transform. So change that last line in the completion block to
self.logoView.transform = CGAffineTransformIdentity;
to explain whats currently going on, by scaling to a negative value in both x and y axes you are turning the view 'inside out' in both dimensions at the same time

UIView to make an arm wave

I'm trying to make an arm wave effect (Hello!) with UIView animations, but it snaps back to the beginning when the last one goes off. I want the wave to go back and forth.
First keyframe: Rotation 30˚
Second keyframe: Rotation -30˚
Third keyframe: SHOULD BE Rotation 0˚
arm.layer.anchorPoint = CGPointMake(1.0, 1.0);
float x = arm.frame.origin.x + arm.frame.size.width;
float y = arm.frame.origin.y + arm.frame.size.height;
arm.layer.frame = CGRectMake(x, y, arm.frame.size.width, arm.frame.size.height);
[self.scrollView arm];
float degrees = 30.0;
float radians = (degrees/90.0)*M_PI;
[UIView animateKeyframesWithDuration:4.0f delay:0.0 options:UIViewKeyframeAnimationOptionRepeat
animations:^{
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:.5 animations:^{
arm.transform = CGAffineTransformMakeRotation(radians);
}];
[UIView addKeyframeWithRelativeStartTime:.5 relativeDuration:.75 animations:^{
arm.transform = CGAffineTransformMakeRotation(-radians);
}];
[UIView addKeyframeWithRelativeStartTime:1.25 relativeDuration:.5 animations:^{
arm.transform = CGAffineTransformMakeRotation(0);
}];
The reason is that you used up the whole animation on the first two frames of the keyframe animation. These are relative times and relative durations; they must all be less than 1!
So, on the first one you use up half the animation (.5).
On the second one you start halfway through the animation (.5) and then you use up the entire rest of the animation (.75 is larger than .5 which is all you've got left).
So the third one never happens. And in any case it's completely nuts: you can't have a relative start time of 1.75 because 1 is the end of the animation. Read the docs:
This value must be in the range 0 to 1, where 0 represents the start of the overall animation and 1 represents the end of the overall animation. For example, for an animation that is two seconds in duration, specifying a start time of 0.5 causes the animations to begin executing one second after the start of the overall animation.

iOS Transform Translate Animation not performing correctly

So I'm trying to perform a (seemingly) simple transform animation on a UIImageView by just moving it up 100 points. This is my code:
[UIView animateWithDuration:1 delay:1 options:nil animations:^(void) {
CGAffineTransform tf = CGAffineTransformMakeTranslation(0, -100);
[image setTransform:tf];
} completion:^(BOOL finished) {} ];
It works...but it for some reason chooses a new origin for my UIImageView to start from. For example, if the image's original center coordinates are (300, 400), then by my code it should translate up 100 points to (300, 300). But for some reason, the code makes the image start a little bit lower than where it should, like around (300, 450), and then it'll move up 100 to (300, 350). How can I get it to do the animation from where the image is originally?
If you prefer to use transformation for this procedure, then you should try to get original transform and translate it by 100 px.
[image setTransform:CGAffineTransformTranslate(image.transform, 0, -100)];
If your goal is to translate the subview relative to its superview's coordinate system, you'll have more predictable results simply animating the center property:
[UIView animateWithDuration: 1.0 animations:^{
image.center = CGPointMake(image.center.x, image.center.y - 100);
}];
The transform property expresses an affine transform relative to the center property (or the layer's anchorPoint, if changed).
i'm having the same problem. if i want to translate my label 100 on the y-axis, it will start the animation -50 in the y, then moves it 100 from there. if i move it 200 in the y, it starts the animation -100 in the y, then moves it 200 from there. it's very frustrating.
#stackoverflowmaster, were you ever able to figure this out?
UPDATE: you must uncheck the "Use Auto Layout" option in the file inspector for whatever object you're trying to use. this took me way too long to figure out, hope it saves someone else some time.

UIVIew animateWithDuration linear?

I want to animate my multiple UIImageViews to move from point A to point B linearly.
i'm using options:UIViewAnimationOptionCurveLinear - Apple docs says: "A linear animation curve causes an animation to occur evenly over its duration.".
Here's the code that i'm using:
[UIView animateWithDuration:1.5
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
//start animation of random grasses with const speed and const y
for (int i=0;i<45;i++){
if ([self.view viewWithTag:i+100]){
CGRect frameOfGrass = [self.view viewWithTag:i+100].frame;
frameOfGrass.origin.y = -100;
[self.view viewWithTag:i+100].frame = frameOfGrass;
}}
}
completion:^(BOOL finished){
//
}];
NOTE:
Y position of every imageView is random number from 600-700.
But the result looks more like UIViewAnimationOptionCurveEaseOut - "An ease-out curve causes the animation to begin quickly, and then slow as it completes." Because all the images slows down at the and.
Here's screenshots of app running:
Any idea why this is happening?
The travel distance is not the same for all the grass images. Remember v=d/t. In your case, all the grass images will travel at different speeds because they need to reach y.origin = -100 at the same time.
Try this:
frameOfGrass.origin.y = frameOfGrass.origin.y - 600;
This should make all the grass images travel the same distance over the same time.

ios issue with 3d animations

I'm trying to 3D animate an UIImageView on X axis so it will do a 180 degrees rotation. At 90 degres I want to change the image. (that is why i "broke" the animation in 2).
Animation works well, but the problem is that the second image is drawn upside down. Any ideas what should i do for the image to be drawn normal?
Here is my code
i++;
[UIView animateWithDuration:2 animations:^{
self.imageView.layer.transform = CATransform3DMakeRotation(-M_PI/2 , 1, 0,0);
} completion:^(BOOL finished){
UIImage *bottomI = (UIImage *)[bottom objectAtIndex:i];
self.second.image =bottomI;
[UIView animateWithDuration:2 animations:^{
imageView.layer.transform = CATransform3DMakeRotation(M_PI , 1, 0,0);
} completion:^(BOOL finised){
}];
}];
What I've done (using grouped CABasicAnimation objects instead of view animations, but the same trick should work) is to rotate the view/layer the first 90 degrees, then rotate it 180 degrees without animation before starting the second half of the animation. That way, the second half of the animation actually causes the front face of the view/layer to rotate into view as if it was the back face, but not backwards.
The view/layer is edge-on to the viewer while you are switching it's image and rotating it 180 degrees, so the user can't see those changes. When it rotates the remaining 90 degrees, the new image is in place and it appears to be revealing the back side of the view/layer.
It works perfectly.
When you are 3d rotating a view, it is similar to rotating an object in real life. So, once the rotation is more than 90 degrees, it would invert the object. Which is why your second image is inverted. The simplest thing to do is to set the affine transform of the image to rotate it by 180 degrees before adding it in.
EDIT: My silliness....
What I intended was this
bottomI.orientation = UIImageOrientationDown

Resources