Animation of CGAffineTransform in iOS8 looks diffe

2019-03-11 00:34发布

问题:

I'm trying to find a reason why animation of UIView transform property looks different in iOS 8 than iOS 6/7.

For a simple example, prior to iOS 8:

myView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, 1.57);
[UIView animateWithDuration:5 animations:^{
    myView.transform = CGAffineTransformTranslate(plane.transform, 100, 0);
}];

gives expected result, "myView" is rotated 90 degrees and moves down, but in iOS8 when translation is animated it starts at a point that I couldn't find explanation for (which breaks the animation).

Does anyone know the explanation for it? Thanks in advance!

回答1:

CGAffineTransformIdentity behaves differently on ios7 and ios8. This has to do with auto-layout and size classes. The solution is to remove constraints that conflict with the animation on ios7.

// solve the constraint-animation problem
if(NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
    // iOS7 remove constraints that conflict with animation
    if (self.centerYAlignment != nil) {
        self.view.removeConstraint(self.centerYAlignment) //is an IBOutlet 
    }
} else {
    // iOS8 constraint animations are fine
}


回答2:

I think the reason is just iOS8 bug, but I use CAAnimation instead, and it works as expected on iOS8.



回答3:

I had problems with jerky rotation transform in iOS7 as well. Solved this by nesting my rotated view inside a container and centering the rotated view inside.



回答4:

I'm also experiencing the same issue with scaling. I guess it could be the same with rotation. Could you try this?

myView.transform = CGAffineTransformConcat(myView.transform , CGAffineTransformMakeRotate(1.57));
[UIView animateWithDuration:5 animations:^{
    myView.transform = CGAffineTransformTranslate(plane.transform, 100, 0);
}];

Maybe it's also necessary to use CGAffineTransformMakeTranslate and CGAffineTransformConcat that as well, I'm not sure.

The worst part about this is: You would have to do if/else on iOS versions, because this would look weird on iOS 7. I hope this is getting fixed by Apple before or with iOS 8 release.



回答5:

I agree with Pbk that it has to do with size classes in io8. uiviewcontrollers need to be resized with uitraitcollections depending on the device orientation. Otherwise, you get a uiviewcontroller in portrait mode, while the phone is in landscape mode, when you try to rotate it. So the correct steps are to rotate AND override uitraitcollections



回答6:

This isn't entirely related, but I was struggling with CGAffineTransformScale not working at all on iOS7 in a fairly complicated animation. It turns out my problem was iOS7 cannot calculate CGAffineTransformScale with CGAffineTransformRotate at the same time. In iOS7, the last animation call you make is the only one that gets animated, so only the rotation was occurring. This bug is fixed in iOS8.

My solution is to simplify my animation for iOS7, only turning on the fancy stuff in iOS8:

//Pre-animation setup:
CGFloat radians = (M_PI/180) * (-15);  //Get a human-readable number in degrees
self.badgeImage.alpha = 0;  //Start the image as invisible
self.badgeImage.transform = CGAffineTransformScale(self.badgeImage.transform, 1.5, 1.5);  //Start the image as scaled bigger than normal
if(NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) {  //See below. We will not be rotating the image in iOS7
    self.badgeImage.transform = CGAffineTransformRotate(self.badgeImage.transform, radians);  //Rotate the image if iOS8
}

//Animation Pieces:
//Fade in
[UIView animateWithDuration: 0.5
                delay:0
                options:0
                animations:^{
                    self.badgeImage.alpha = 1.0f; //Return image to opaque
                }
                completion:NULL];
//Scale with bounce
[UIView animateWithDuration: 1.1
                delay:0
                usingSpringWithDamping:0.3  //Not as good as Android's bounce interpolator, but I'll take it
                initialSpringVelocity:-1.0f //A negative velocity here makes the animation appear more like gravity than spring
                options:0
                animations:^{
                    self.badgeImage.transform = CGAffineTransformScale(self.badgeImage.transform, 0.67, 0.67);  //Return image to its original size. These arguments are relative to its current scale.
                }
                completion:NULL];
//Rotation
if(NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) { //This second animation call negates the first one on iOS7, so remove it.
    [UIView animateWithDuration: 0.9
                    delay:0
                    options:UIViewAnimationOptionCurveEaseOut
                    animations:^{
                        self.badgeImage.transform = CGAffineTransformRotate(self.badgeImage.transform, (radians * -1)); //Rotate the image back to its original orientation if iOS8
                    }
                    completion:NULL];
}

Of course, you can still combine multiple effects in iOS7 if you use the confusingly-named CGAffineTransformMakeScale() function. For instance, in the pre-animation setup, you can set both a rotation AND a scale, then set call CGAffineTransformMakeScale(1,1) to reset the image to its original metrics (MakeScale's arguments are specific, not relative - even more confusing!). This isn't always preferable, such as my example above where "bouncing" the animation would also bounce the rotation.