CAGradientLayer属性不在UIView动画块中设置动画
我有一种感觉,我忽略了一些基本的东西,但有什么更好的方法来找到它而不是在互联网上出错?
我有一个相当基本的用户界面。 我的UIViewController
的视图是一个子类,其+layerClass
是CAGradientLayer
。 根据用户的操作,我需要移动一些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,以便可以访问其fromValue
和fromValue
属性。
以下代码应该适合您:
-(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:
方法中进行一些转换,以访问动画的属性。