2012-02-21 102 views
13

我有一种感觉,我忽略了一些基本的东西,但是找到它比在互联网上错误的更好的方法是什么?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]; 
}]; 

的问题是,我在这块子视图改变动画就好了(东西移动和淡入淡出),但改变渐变的颜色没有。它只是互换。

现在,the documentation does say动画块中的Core Animation代码不会继承块的属性(持续时间,缓动等)。但是,这种情况是不是完全定义了动画交易? (文档的含义似乎是,你会得到一个默认的动画,我没有得到。)

我是否使用明确的CAAnimation使这项工作? (如果是这样,为什么?)

回答

13

这里似乎有两件事情要做。第一个(正如Travis正确指出的那样,并且文档状态)是UIKit动画似乎不适用于应用于CALayer属性更改的隐式动画。我认为这很奇怪(UIKit 必须使用核心动画),但它是什么。

这里有一个(可能是很愚蠢?)解决方法这个问题:

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

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

添加以下代码,以我的观点会引起颜色变化预计将动画:

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

神奇的发现!从来没有猜到这一点。 – 2012-08-31 04:07:13

1

您必须使用显式CAAnimations,因为您正在更改CALayer的值。 UIViewAnimations上的UIView属性的作用,而不是直接在他们的CALayer的属性...

其实,你应该使用CABasicAnimation,这样就可以访问它fromValuetoValue性能。

下面的代码应该为你工作:

-(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:方法以将CAG渐变图层的颜色设置为其最终值。

您还需要在animationDidStop:方法中执行一些操作以访问动画的属性。

+0

的事情是,CALayer的属性有隐式动画。它似乎是由UIView动画定义的动画属性不适用于CALayer更改,但这并不能解释为什么我没有看到任何*动画的颜色变化。 – 2012-02-28 17:33:39

+0

UIView和CALayer都有隐式动画。当你触发一个UIViewAnimation,并且你想要在它的CALayer上触发一个动画时,你必须同时明确地这样做。这就是CABasicAnimation在UIView动画中所做的事情:^ {} block ... – 2012-02-28 19:40:47

+0

但关键是隐式动画不会发生,因为所讨论的图层是* view的*图层。从UIView动画块内部进行更改是完全不同的问题。 (隐式动画的全部要点是它们不需要明确的动画块。) – 2012-02-28 22:36:19