iOS 8 animation bug

2019-02-02 10:14发布

问题:

I have a simple method for animate view.

-(void)animateSelf
{
    CABasicAnimation * animation;

    animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
    // settings ...
    [self.view.layer addAnimation:animation forKey:@"position.y"];

    animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    // settings ...
    [self.view.layer addAnimation:animation forKey:@"transform.rotation.z"];

    [UIView animateWithDuration: 1.0 animations:^{
        CGRect rect = self.view.frame;
        rect.origin.y += 800;
        self.view.frame = rect;
    } completion:nil];
}

For iOS 7 it worked well. But for iOS 8 animation behaves unpredictably. Is there a way to combine these animations for iOS 8?

I tried to replace animateWithDuration: by another CABasicAnimation, but it did not help. The view.frame logs are correct, but the animation of view.frame jumps out of obscure origin.

After removing CABasicAnimation for position.y (the first one) bug is gone.

回答1:

You have three animations:

  1. You animate the layer's position
  2. You animate the layer's transform
  3. You animate the view's frame which in turn animates the layer's position.

Animations 1 and 3 collide.

  • On iOS 7 the behavior is that animation 3 cancels animation 1.
  • On iOS 8 the behavior is that animations 1 and 3 run concurrently ("Additive Animations").

I suggest you just remove animation 1 and also check out the related WWDC 2014 video (I think it was Building Interruptible and Responsive Interactions).



回答2:

What worked for me was disabling autolayouts on the view and THE SUBVIEWS of the view I was animating at some point before doing the animation. Disabling autolayouts from the storyboard was not enough in iOS8.

[viewToAnimate removeFromSuperview];
[viewToAnimate setTranslatesAutoresizingMaskIntoConstraints:YES];
//addSubview again at original index
[superView insertSubview:viewToAnimate atIndex:index];


回答3:

This example might help you, I wish I had discovered it before wasting hours. Rather than animate the frame, animates its contraints. Click on the auto layout constraint you would like to adjust (in interface builder e.g top constraint). Next make this an IBOutlet;

@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;

Animate upwards;

self.topConstraint.constant = -100;    
[self.viewToAnimate setNeedsUpdateConstraints]; 
[UIView animateWithDuration:1.5 animations:^{
    [self.viewToAnimate layoutIfNeeded]; 
}];

Animate back to original place

self.topConstraint.constant = 0;    
[self.viewToAnimate setNeedsUpdateConstraints];  
[UIView animateWithDuration:1.5 animations:^{
    [self.viewToAnimate layoutIfNeeded];
}];

Originally posted by me here

So you would adjust the constraint for self.view.frame in your example.



回答4:

I also got an issue of little nasty differences between iOS7 and iOS8 animation. In most cases it was broken it was either:

  • single combination of Scale, Transform and Rotate CGAffineTransforms - the result was dependant on iOS version
  • or complex sequence of animations on different views - some views were 'reseting' their positions before commencing a new piece of animations. About 5% of animation pieces were affected.

I'm pretty sure there were no simultaneous animations on the problematic views. Autolayout and constraints suggestions did not help (moreover, all animated views were create in code as autolayout interfered with animation a lot even before iOS8).

What turned out to be a universal solution for both problems is to put the problematic views into a wrapper view and use it to split-off Rotation animation or to do the animation that causes 'reset' effect. Now it functions the same in 7.1.1 and 8.1.1.