24

Everyone and every book claims that there are implicit animations happening in CALayer. However, every time I wanted to verify that so far, I end up with a hard-snap to the set value. No animation at all.

Here's an example in a project where nothing else is happening. All I do is create a view, then get it's CALayer instance and do something that should be implicitly animated.

[theLayer setValue:[NSNumber numberWithFloat:M_PI * 1.1] forKeyPath:@"transform.rotation.z"];

Another one:

CGRect currentBounds = theLayer.bounds;
currentBounds.size.width += 120.f;
[self.animatedLayer setBounds:currentBounds];

The view contains some stuff of course so I can see the change. I see the visual change, but as a hard snap. No animation at all.

So either all those books are wrong and have old Mac OS X knowledge in mind when writing about Core Animation and implicit animations, or I'm doing something wrong. Can anyone provide an working example that demonstrates implicit animations on the iPhone?

Proud Member
  • 40,078
  • 47
  • 146
  • 231

6 Answers6

71

UIKit disables implicit animations. To be more specific, a CALayer associated with a UIView will never implicitly animate. CALayers that you create yourself and that are not associated with a UIView will buy into the normal implicit animation machinery.

If you're interested in how this works, implicit animations happen after the normal -actionForKey: lookup. If there's a CAAction for a given property, it's used. Otherwise, the implicit animation is used. In the case of a CALayer associated with a UIView, UIView implements the -actionForLayer:forKey: delegate method, and when not in a UIView animation block it always returns [NSNull null] to signify that the action lookup should stop here. This prevents implicit animations from working. Inside of a UIView animation block, it constructs its own action to represent the current UIView animation settings. Again, this prevents implicit animations.

If you need to animate CALayer properties that UIView won't do for you, you can use an explicit CAAnimation subclass (such as CABasicAnimation).

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 2
    Great answer! I'm curious - where did you gain all this detailed information? I checked so many books and sources but they lack of these details. – Proud Member Jan 20 '11 at 21:03
  • 1
    Mostly trial and error. If you override `-actionForLayer:forKey:` on a UIView subclass you can log out the results of the call to super and correlate that with whether there's an active animation block. It also makes sense if you think about it - the UIView is the CALayer's delegate, and `-actionForLayer:forKey:` is the appropriate place to hook in to if you want to disable implicit animations. – Lily Ballard Jan 20 '11 at 21:18
  • Makes a lot of sense. Thanks! – Proud Member Jan 21 '11 at 22:13
  • But why this is not written in the official documentation?! It simply says "To trigger implicit animations, all you have to do is update the properties of your layer object." – MatterGoal Jul 13 '13 at 08:05
  • 1
    @MatterGoal: The docs you're referencing are CoreAnimation docs. And it's correct; in a regular `CALayer`, implicit animations work. The trick here is that `CALayer`s that belong to a `UIView` have their implicit animations disabled. But any `CALayer`s you create yourself still get them. – Lily Ballard Jul 13 '13 at 08:45
  • It is possible to do implicit animation and the example is shown (How to Animate Layer-Backed Views): https://developer.apple.com/library/ios/documentation/cocoa/conceptual/coreanimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html#//apple_ref/doc/uid/TP40004514-CH3-SW16 – Ronnie Liew Nov 18 '13 at 17:26
2

Old post, but link below points to a section in the Core Animation Programming Guide that helps shed some more light on what Kevin Ballard was saying. There really needs to be a blatant note that mentions that in order to animate a UIView's underlying layer properties you need to ensure that you set the layer's delegate to an instance that will adopt the "actionForLayer:ForKey:" method and return nil. It's been found that you can also set the delegate to an instance that doesn't adopt this method and it still allows implicit animations, but this is a sloppy and confusing practice.

Core Animation - Layer Actions - Defined Search Pattern for Action Keys

JonnyB
  • 335
  • 1
  • 3
  • 13
1

It's exactly correct of Kevin Ballard's answer. I want to provide something more here.

The detail information from the official document

There is a section called: Rules for Modifying Layers in iOS, and you will find

The UIView class disables layer animations by default but reenables them inside animation blocks.

Except enable the implicit in the UIView animation block. There are two another solution:

  1. Make the layer you need animation a sublayer of a view. Refer to the [answer][2]

    [2]: Implicit property animations do not work with CAReplicatorLayer? of Implicit animation fade-in is not working. I tried this, it worked as expected.

  2. assign the layer's delegate to the layer itself. The default delegate of the layer in iOS is its backing store view which implements the actionForLayer:(id)layer forKey:(NSString *)key and return [NSNull null] to disable the implicit animation of the layer.
Community
  • 1
  • 1
Danyun Liu
  • 3,072
  • 24
  • 22
1

Keep in mind that sublayers of a UIView's primary layer do not normally have the UIView as their delegate. Therefore, they participate in implicit animation unless you explicitly block this, even if you haven't provided a delegate to return a nil action in response to actionForLayer:forKey:.

So, one simple way to get implicit animation in a generic view is simply to add a sublayer that covers the primary layer.

Matt Neuberg's book Programming iOS 5 (currently in beta on Safari Books Online) explains this topic very lucidly.

GSnyder
  • 470
  • 4
  • 10
0

actually, you could just return nil to get the default animation of CALay.

  • (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event{ return nil; }
  • /* If defined, called by the default implementation of the * -actionForKey: method. Should return an object implementating the * CAAction protocol. May return 'nil' if the delegate doesn't specify * a behavior for the current event. Returning the null object (i.e. * '[NSNull null]') explicitly forces no further search. (I.e. the * +defaultActionForKey: method will not be called.) */ – dongxin Jan 18 '16 at 01:54
0

You could also set the layer's delegate to your own object and handle -actionForLayer:forKey:. Seems as if you get implicit animation even if the delegate doesn't implement -actionForLayer:forKey:.

leftspin
  • 2,468
  • 1
  • 25
  • 40