2

我有一个名为specialLayer. 像这样禁用不透明度的隐式动画:

self.specialLayer.actions = [NSDictionary dictionaryWithObjectsAndKeys:
                                            [NSNull null], @"opacity", nil];

只有两种方法可以访问这个子层。

- (void)touchDown {
    self.specialLayer.opacity = 0.25f;
}

- (void)touchUp {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.fromValue = [NSNumber numberWithFloat:self.specialLayer.opacity];
    animation.toValue = [NSNumber numberWithFloat:1.0];
    animation.duration = 0.5;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    [self.specialLayer addAnimation:animation forKey:@"opacity"];
}

有时候是这样的:

1)用户按下按钮(按住)并调用 -touchDown 。specialLayer 的不透明度瞬间变为 0.25

2) 用户释放按钮并调用 -touchUp。specialLayer 的不透明度动画回到 1.0

此序列仅适用一次!

3)用户再次按下按钮(按住)并调用 -touchDown 。但是specialLayer的不透明度在屏幕上没有改变!即使self.specialLayer.opacity = 0.25f;被调用也没有任何反应。不透明度似乎保持在 1.0f。

4)用户抬起手指并调用-touchUp。specialLayer 的不透明度跳到 0.25 并从那里动画到 1.0f。

我检查了 NSLog 以确保按此顺序调用方法。他们是这样。

当我取消注释动画代码并将不透明度直接设置为 1.0 时,后续直接不透明度更改立即生效。绝对是导致问题的 CABasicAnimation。删除禁用隐式动画的代码对此问题没有影响。

我一生都无法弄清楚这里发生了什么。为什么 CALayer 在我为不透明度添加 CABasicAnimation 后一段时间后没有反映我设置的不透明度值?这是一个错误吗?

我究竟做错了什么?

4

1 回答 1

8

您将动画设置removedOnCompletionNO. 这意味着当动画完成时,它仍然停留在图层上并且仍然控制 的值以opacity进行渲染。当您第二次调用此代码时,新动画会替换旧动画,从而导致跳转(因为它现在使用新设置的动画0.25f作为起点)。

解决方案是停止设置removedOnCompletionNO. 相反,您应该在添加动画的同时将图层的实际不透明度设置为与动画端点相同的值。

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
// you can omit fromValue since you're setting it to the layer's opacity
// by omitting fromValue, it uses the current value instead. Just remember
// to not change layer.opacity until after you add the animation to the layer
animation.toValue = [NSNumber numberWithFloat:1];
animation.duration = 0.5;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[layer addAnimation:animation forKey:@"opacity"];
layer.opacity = 1; // or layer.opacity = [animation.toValue floatValue];

这是因为图层渲染的工作方式。当 CoreAnimation 将图层树渲染到屏幕上时,它会复制模型树并生成所谓的演示树。它通过复制所有图层,然后应用所有动画来做到这一点。因此,它遍历图层上的每个动画,将时间插入计时函数,将生成的进度插入 from/to 值之间的插值,并将最终值应用回表示层的属性。

因此,当您将动画永久保留在图层上时,此动画将始终覆盖该键的实际模型值。无论您更改多少次layer.opacity,控制的动画opacity将始终覆盖用于演示目的的值。

同样的原因也是为什么您可以layer.opacity在添加动画后将 设置为您想要的任何内容,因为动画将优先。只有当动画被移除时(默认情况下,当动画完成时)才会再次使用该属性的模型值。

于 2012-09-13T19:46:01.340 回答