CALayer:隐式动画从何而来



我是核心动画的新手。在学习隐式动画时,我遇到了一个问题。

我在屏幕上放了一个图层,并在按下按钮时将其颜色更改为随机值,从而触发隐式动画。

#define RANDOM_0_1 (arc4random() / (CGFloat)UINT_MAX)
- (IBAction)changeColor:(id)sender {
    CGFloat r = RANDOM_0_1;
    CGFloat g = RANDOM_0_1;
    CGFloat b = RANDOM_0_1;
    self.colorLayer.backgroundColor = [UIColor colorWithRed:r green:g blue:b alpha:1.0f].CGColor;
}

效果很好。然后我用

NSLog(@"%@", [self.colorLayer actionForKey:@"backgroundColor"]);

获取隐式传递到图层的动画对象。我得到了

<CABasicAnimation: 0x7f8ae1d21970>.

参考该文档,我了解到图层有四种方式来获取操作。 '委托、动作字典、样式字典和 +[CALayer defaultActionForKey:]' 然后我说想知道动画对象真正来自哪个步骤。所以我写这个来检查

NSLog(@"%@ %@ %@", self.colorLayer.delegate, self.colorLayer.actions, self.colorLayer.style);

它给了我三个(空(

 (null) (null) (null)

正如文档所述,默认情况下应将这些值设置为 nil。

所以一定是+[CALayer defaultActionForKey:]给了我动画对象。但是,当我打电话时

NSLog(@"%@", [self.colorLayer actionForKey:@"backgroundColor"]);

它仍然给了我一个(空(。

我想这很奇怪。我开始怀疑传入的"密钥"是否被内部实现以某种方式更改了。所以我参考这篇文章来打印传递给该方法的参数以及返回值。

static id<CAAction> (*__originalCALayerDefaultActionForKey)( CALayer *, SEL, NSString *) ;
static id<CAAction> CALayerDefaultActionForKey( CALayer * self, SEL _cmd, NSString * event )
{
    id res = (*__originalCALayerDefaultActionForKey)( self, _cmd, event );
    NSLog(@"%@<%p> %@ %@n", [ self class ], self, event, res ) ;
    return res;
}

我得到了这样的结果

CALayer<0x106da5ef0> position (null)
CALayer<0x106da5ef0> bounds (null)
CALayer<0x106da5ef0> backgroundColor (null)

传入的"键"正是属性名称,但它始终返回 null。

那么,谁能解释一下动画对象在地球上的什么地方,或者为我提供一些技术来找出答案?

谢谢,:)

CALayer的SDK默认动画是在.m中实现的,您无法以正常方式获取它。

SDK 为用户提供了使用自定义动画的可选方式:

代表

您可以使用委托对象来提供图层的内容、处理任何子图层的布局以及提供自定义操作以响应与图层相关的更改。分配给此属性的对象应实现 CALayerDelegate 非正式协议的一个或多个方法。...

操作

此属性的默认值为 nil。您可以使用此字典存储图层的自定义操作。此字典的内容作为 actionForKey: 方法的标准实现的一部分进行搜索。

它们仅用于动画自定义。

例如

,您可以创建一个自定义的CALayer子类(例如自定义层(,该子类覆盖-actionForKey

- (id<CAAction>)actionForKey:(NSString *)event
{
  if ([event isEqualToString:@"strokeStart"] || [event isEqualToString:@"strokeEnd"]) {
    CABasicAnimation * strokAnimation = [CABasicAnimation animationWithKeyPath:event];
    strokAnimation.removedOnCompletion = NO;
    strokAnimation.fillMode = kCAFillModeForwards;
    strokAnimation.duration = .3f;
    strokAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    strokAnimation.fromValue = [self.presentationLayer valueForKey:event];
    return strokAnimation;
  }
  return [super actionForKey:event];
}

这样,每当您更新 CustomLayer 实例的 strokStart & strokend 时,它都会调用您提供的strokAnimation,而不是 SDK 实现的默认动画。

<小时 />

但是style,它用于设置没有动画的可动画属性的值(因为默认情况下,每当更新 CALayer 实例的可动画属性的值时,如果您不提供自定义动画,它将调用默认动画(:

用于存储图层未显式定义的属性值的可选字典。

如果样式字典未定义属性的值,则调用接收器的 defaultValueForKey: 方法。此属性的默认值为 nil。

例如,像strokeStart&strokeEnd,它们的默认值是0.f&1.f,你可以修改样式以使用自定义的默认值,例如:

aLayer.style = @{@"strokeStart" : @.2f,
                 @"strokeEnd"   : @.6f};
这样,就不会像

在初始化步骤中那样调用图层的动画,尤其是当您为键提供复杂的动画时,它会有很大帮助。

<小时 />

对于+defaultActionForKey:,它更像是用户定义的默认操作,如文档所述:

返回给定键的合适操作对象,或者没有操作对象与该键关联。

想要提供默认操作的类可以重写此方法并使用它来返回这些操作。

您可以像 -actionForKey: 一样重写此方法,下面是修改相关属性时检查操作是否存在的顺序:

  1. 检查委托是否存在和委托方法-actionForLayer:forKey:是否实现;
  2. 检查是否在-actionForKey:中提供了密钥的动作;
  3. 检查步骤 2 后调用 [超级动作键:事件] 时actions中是否存在键的动作;
  4. 如果actions中不存在键的动作,则按照步骤3进行操作,检查+defaultActionForKey:中是否存在键的动作。
  5. 据我所知,使用 SDK 实现的默认操作我们无法获取(或可能具有未知的事件名称(。

通常,+defaultActionForKey:就像您的CALayer子类的类级别的全局自定义设置,而actions-actionForLayer:forKey:-actionForKey:来处理特殊层。

最新更新