13

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

我有一个相当基本的用户界面。my 的视图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];
}];

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

现在,文档确实说动画块中的核心动画代码不会继承块的属性(持续时间、缓动等)。但这是否根本没有定义动画事务?(文档的含义似乎是您将获得默认动画,而我没有。)

必须使用显式CAAnimation来完成这项工作吗?(如果是这样,为什么?)

4

2 回答 2

13

这里似乎发生了两件事。第一个(正如 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];
  }];

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

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

- (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;
}
于 2012-02-28T17:46:50.663 回答
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:方法以将 CAGradientLayer 的颜色设置包含为其最终值。

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

于 2012-02-25T08:50:06.853 回答