CAGradientLayer属性不在UIView动画块中设置动画

我有一种感觉,我忽略了一些基本的东西,但有什么更好的方法来找到它而不是在互联网上出错?

我有一个相当基本的用户界面。 我的UIViewController的视图是一个子类,其+layerClassCAGradientLayer 。 根据用户的操作,我需要移动一些UI元素,并更改背景渐变的值。 代码看起来像这样:

 [UIView animateWithDuration:0.3 animations:^{ self.subview1.frame = CGRectMake(...); self.subview2.frame = CGRectMake(...); self.subview2.alpha = 0; NSArray* newColors = [NSArray arrayWithObjects: (id)firstColor.CGColor, (id)secondColor.CGColor, nil]; [(CAGradientLayer *)self.layer setColors:newColors]; }]; 

问题是我在这个块中对子视图所做的更改动画就好了(东西移动和淡化),但渐变颜色的变化却没有。 它只是交换。

现在, 文档确实说动画块中的Core Animation代码不会inheritance块的属性(持续时间,缓动等)。 但是,根本没有定义动画交易吗? (文档的含义似乎是你会得到一个默认动画,我得不到。)

是否必须使用显式CAAnimation才能使其工作? (如果是这样,为什么?)

这里似乎有两件事情。 第一个(正如Travis正确指出的那样,文档说明)是UIKit动画似乎对应用于CALayer属性更改的隐式动画没有任何影响。 我认为这很奇怪(UIKit 必须使用Core Animation),但它就是这样。

对于该问题,这是一个(可能非常愚蠢?)解决方法:

  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]; }]; 

另一个关键是这个渐变图层是我视图的图层。 这意味着我的视图是图层的委托(如果渐变图层只是一个子图层,它将没有委托)。 并且-actionForLayer:forKey:UIView实现为"colors"事件返回NSNull 。 (可能每个事件都不在特定的UIView动画列表中。)

将以下代码添加到我的视图将导致颜色更改按预期动画:

 - (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { id action = [super actionForLayer:layer forKey:event]; if( [@"colors" isEqualToString:event] && (nil == action || (id)[NSNull null] == action) ) { action = [CABasicAnimation animationWithKeyPath:event]; } return action; } 

您必须使用显式CAAnimations,因为您正在更改CALayer的值。 UIViewAnimations可以在UIView属性上工作,但不能直接在他们的CALayer属性上工作……

实际上,您应该使用CABasicAnimation,以便可以访问其fromValuefromValue属性。

以下代码应该适合您:

 -(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; } } 

CAAnimations有一个技巧,你必须在完成动画后显式设置属性的值。

您可以通过设置委托来完成此操作,在这种情况下,我将其设置为调用动画的对象,然后覆盖其animationDidStop:finished:方法,以将CAGradientLayer的颜色设置包含在其最终值中。

您还需要在animationDidStop:方法中进行一些转换,以访问动画的属性。