Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inherit animation properties while animating CALayer with implicit animation

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.

like image 574
Fr4ncis Avatar asked Mar 14 '14 13:03

Fr4ncis


2 Answers

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

like image 190
Alexander Ney Avatar answered Sep 21 '22 13:09

Alexander Ney


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.

like image 38
namanhams Avatar answered Sep 22 '22 13:09

namanhams