How to inherit animation properties while animatin

2019-03-27 14:31发布

问题:

I am trying to animate a custom property on a CALayer with an implicit animation:

[UIView animateWithDuration:2.0f animations:^{
    self.imageView.myLayer.myProperty = 1;
}]; 

In -actionForKey: method I need to return the animation taking care of interpolating the values. Of course I have to tell somehow the animation how to retrieve the other parameters for the animation (i.e. the duration and the timing function).

- (id<CAAction>)actionForKey:(NSString *)event
{
    if ([event isEqualToString:@"myProperty"])
        {
            CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"myProperty"];
            [anim setFromValue:@(self.myProperty)];
            [anim setKeyPath:@"myProperty"];
            return anim;
        }
        return [super actionForKey:event];
    }
}

Any idea on how to achieve that? I tried looking for the animation in the layer properties but could not find anything interesting. I also have a problem with the layer animating since actionForKey: is called outside of animations.

回答1:

I reckon that you have a custom layer with you custom property "myProperty" that you added to the backing layer of UIView - according to the Documentation UIView animation blocks does not support the animation of custom layer properties and states the need to use CoreAnimation:

Changing a view-owned layer is the same as changing the view itself, and any animations you apply to the layer’s properties respect the animation parameters of the current view-based animation block. The same is not true for layers that you create yourself. Custom layer objects ignore view-based animation block parameters and use the default Core Animation parameters instead.

If you want to customize the animation parameters for layers you create, you must use Core Animation directly.

Further the documentation sates that UIView supports just a limited set of animatable properties which are:

  • frame
  • bounds
  • center
  • transform
  • alpha
  • backgroundColor
  • contentStretch

Views support a basic set of animations that cover many common tasks. For example, you can animate changes to properties of views or use transition animations to replace one set of views with another.

Table 4-1 lists the animatable properties—the properties that have built-in animation support—of the UIView class.

https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/AnimatingViews/AnimatingViews.html#//apple_ref/doc/uid/TP40009503-CH6-SW12

You have to create a CABasicAnimation for that.

You can have sort of a workaround with CATransactions if you return a CABasicAnimation in actionForKey: like that

[UIView animateWithDuration:duration animations:^{
    [CATransaction begin];
    [CATransaction setAnimationDuration:duration];

    customLayer.myProperty = 1000; //whatever your property takes

    [CATransaction commit];
  }];

Just change your actionForKey: method to something like that

- (id<CAAction>)actionForKey:(NSString *)event
{
    if ([event isEqualToString:@"myProperty"])
    {
        return [CABasicAnimation animationWithKeyPath:event];
    }
    return [super actionForKey:event];
 }

There is something in Github in case you wan't to have a look: https://github.com/iMartinKiss/UIView-AnimatedProperty



回答2:

I don't think you can access the duration if you use :

[UIView animateWithDuration:duration animations:^{
}]; 

Other issue with your code is, the implementation of actionForKey: of UIView only returns a CAAnimation object if the code is called inside an animation block. Otherwise it returns null to turn off animation. In your implementation, you always return a CAAnimation, hence changing to that property will always be animated.

You should use this :

[CATransaction begin];
[CATransaction setAnimationDuration:duration];
[CATransaction setAnimationTimingFunction:timingFunction];

customLayer.myProperty = 1000; //whatever your property takes

[CATransaction commit];

Then in your actionForKey: method, use [CATransaction animationDuration] and [CATransaction animationTimingFunction] to retrieve the current duration and timing function.



回答3:

The easiest way could be onother property in the your custom layer to set before myProperty. like:

self.imageView.myLayer.myTimingFunction = kCAMediaTimingFunctionEaseInEaseOut;
self.imageView.myLayer.myProperty = 1;

And

-(id<CAAction>)actionForKey:(NSString *)event
{
 if ([event isEqualToString:@"myProperty"])
    {
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"myProperty"];
        [anim setFromValue:@(self.myProperty)];
        [anim setKeyPath:@"myProperty"];
        [anim setTimingFunction:myTimingFunction];
        return anim;
    }
    return [super actionForKey:event];
 }
}

If you want to get parameters, like duration, set in

 [UIView animateWithDuration:2.0f ... 

So in this case duration = 2.0f

you can use CATransaction and valueForKey. CATransaction should return the specific value of the context.