CAGradientLayer properties not animating within UIView animation block

前端 未结 2 2036
长发绾君心
长发绾君心 2020-12-31 09:19

I have a feeling I\'m overlooking something elementary, but what better way to find it than to be wrong on the internet?

I have a fairly basic UI. The view for my

相关标签:
2条回答
  • 2020-12-31 09:50

    You have to use explicit CAAnimations, because you're changing the value of a CALayer. UIViewAnimations work on UIView properties, but not directly on their CALayer's properties...

    Actually, you should use a CABasicAnimation so that you can access its fromValue and toValue properties.

    The following code should work for you:

    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        [UIView animateWithDuration:2.0f
                              delay:0.0f
                            options:UIViewAnimationCurveEaseInOut
                         animations:^{
                             CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"colors"];
                             animation.duration = 2.0f;
                             animation.delegate = self;
                             animation.fromValue = ((CAGradientLayer *)self.layer).colors;
                             animation.toValue = [NSArray arrayWithObjects:(id)[UIColor blackColor].CGColor,(id)[UIColor whiteColor].CGColor,nil];
                             [self.layer addAnimation:animation forKey:@"animateColors"];
                         }
                         completion:nil];
    }
    
    -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
        NSString *keyPath = ((CAPropertyAnimation *)anim).keyPath;
        if ([keyPath isEqualToString:@"colors"]) {
            ((CAGradientLayer *)self.layer).colors = ((CABasicAnimation *)anim).toValue;
        }
    }
    

    There is a trick with CAAnimations in that you HAVE to explicitly set the value of the property AFTER you complete the animation.

    You do this by setting the delegate, in this case I set it to the object which calls the animation, and then override its animationDidStop:finished: method to include the setting of the CAGradientLayer's colors to their final value.

    You'll also have to do a bit of casting in the animationDidStop: method, to access the properties of the animation.

    0 讨论(0)
  • 2020-12-31 10:03

    There seem to be two things going on here. The first (as Travis correctly points out, and the documentation states) is that UIKit animations don't seem to hold any sway over the implicit animation applied to CALayer property changes. I think this is weird (UIKit must be using Core Animation), but it is what it is.

    Here's a (possibly very dumb?) workaround for that problem:

      NSTimeInterval duration = 2.0; // slow things down for ease of debugging
      [UIView animateWithDuration:duration animations:^{
        [CATransaction begin];
        [CATransaction setAnimationDuration:duration];
    
        // ... do stuff to things here ...
    
        [CATransaction commit];
      }];
    

    The other key is that this gradient layer is my view's layer. That means that my view is the layer's delegate (where, if the gradient layer was just a sublayer, it wouldn't have a delegate). And the UIView implementation of -actionForLayer:forKey: returns NSNull for the "colors" event. (Probably every event that isn't on a specific list of UIView animations.)

    Adding the following code to my view will cause the color change to be animated as expected:

    - (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
    {
      id<CAAction> action = [super actionForLayer:layer forKey:event];
      if( [@"colors" isEqualToString:event]
          && (nil == action || (id)[NSNull null] == action) ) {
        action = [CABasicAnimation animationWithKeyPath:event];
      }
      return action;
    }
    
    0 讨论(0)
提交回复
热议问题