{"id":61,"date":"2012-01-07T15:40:13","date_gmt":"2012-01-07T07:40:13","guid":{"rendered":"http:\/\/hoo.onpech.net\/?p=61"},"modified":"2012-01-10T13:30:10","modified_gmt":"2012-01-10T05:30:10","slug":"calayer-implicit-animation-of-a-custom-property","status":"publish","type":"post","link":"https:\/\/hoo.onpech.net\/?p=61","title":{"rendered":"CALayer: Implicit animation of a custom property"},"content":{"rendered":"<p>For Apple&#8217;s iOS, below is the code to implicitly animate a custom property, using float as an example, in a subclass of CALayer:<br \/>\n<pre><code class=\"preserve-code-formatting\">\/* MyLayer.m *\/\n@interface MyLayer() {\n&nbsp;&nbsp;&nbsp;&nbsp;BOOL _isPresenting; \/* TRUE for presentation layer *\/\n}\n@property (nonatomic) float property; \/* normally defined in MyLayer.h *\/\n@end\n@implementation MyLayer\n@synthesize property = _property;\n- (void)setProperty:(float)property {\n&nbsp;&nbsp;&nbsp;&nbsp;if (_isPresenting) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NSLog(@&quot;Presenting layer&quot;);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_property = property;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;NSLog(@&quot;Setting value&quot;);\n&nbsp;&nbsp;&nbsp;&nbsp;if ([self animationForKey:@&quot;animateProperty&quot;]) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_property = [[self presentationLayer] property];\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[self removeAnimationForKey:@&quot;animateProperty&quot;];\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@&quot;property&quot;];\n&nbsp;&nbsp;&nbsp;&nbsp;[a setFromValue:[NSNumber numberWithFloat:_property]];\n&nbsp;&nbsp;&nbsp;&nbsp;[a setToValue:[NSNumber numberWithFloat:property]];\n&nbsp;&nbsp;&nbsp;&nbsp;\/* Set other animation properties *\/\n&nbsp;&nbsp;&nbsp;&nbsp;[self addAnimation:a forKey:@&quot;animateProperty&quot;];\n&nbsp;&nbsp;&nbsp;&nbsp;_property = property;\n}\n+ (BOOL)needsDisplayForKey:(NSString *)key {\n&nbsp;&nbsp;&nbsp;&nbsp;if ([key isEqualToString:@&quot;property&quot;]) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return YES;\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;return [super needsDisplayForKey:key];\n}\n- (id)init {\n&nbsp;&nbsp;&nbsp;&nbsp;if (self = [super init]) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_property = 10; \/* initial value *\/\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&nbsp;self;\n}\n- (id)initWithLayer:(id)layer {\n&nbsp;&nbsp;&nbsp;&nbsp;if (self = [super initWithLayer:layer]) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyLayer *l = (MyLayer *)layer;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_isPresenting = TRUE;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_property = l.property;\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;return self;\n}\n- (void)drawInContext:(CGContextRef)ctx {\n&nbsp;&nbsp;&nbsp;&nbsp;NSLog(@&quot;%f&quot;, _property);\n&nbsp;&nbsp;&nbsp;&nbsp;\/* Add drawing code *\/\n}\n@end\n<\/code><\/pre>Then, setting the custom property with:<br \/>\n<pre><code class=\"preserve-code-formatting\">myLayer.property = 50;\n<\/code><\/pre>gives the following in the log:<br \/>\n<pre><code class=\"preserve-code-formatting\">Setting value\nPresenting layer\n18.953653\nPresenting layer\n25.723625\nPresenting layer\n..\n..\n..\nPresenting layer\n49.999870\n50.000000\n50.000000\n<\/code><\/pre>The animation is working, but it cannot be overridden.  It is time to change that by adding:<br \/>\n<pre><code class=\"preserve-code-formatting\">+ (id&lt;CAAction&gt;)defaultActionForKey:(NSString *)key {\n&nbsp;&nbsp;&nbsp;&nbsp;if ([key isEqualToString:@&quot;property&quot;]) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@&quot;property&quot;];\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[a setValue:@&quot;defaultAction&quot; forKey:@&quot;animationType&quot;];\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return a;\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;return [super defaultActionForKey:key];\n}\n<\/code><\/pre>and modifying the setter to:<br \/>\n<pre><code class=\"preserve-code-formatting\">- (void)setProperty:(float)property {\n&nbsp;&nbsp;&nbsp;&nbsp;if (_isPresenting) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NSLog(@&quot;Presenting layer&quot;);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_property = property;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;NSLog(@&quot;Setting value&quot;);\n&nbsp;&nbsp;&nbsp;&nbsp;if ([self animationForKey:@&quot;animateProperty&quot;]) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_property = [[self presentationLayer] property];\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[self removeAnimationForKey:@&quot;animateProperty&quot;];\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;CABasicAnimation *a = (CABasicAnimation *)[self actionForKey:@&quot;property&quot;];\n&nbsp;&nbsp;&nbsp;&nbsp;if (a) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ([[a valueForKey:@&quot;animationType&quot;] isEqualToString:@&quot;defaultAction&quot;]) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[a setFromValue:[NSNumber numberWithFloat:_property]];\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[a setToValue:[NSNumber numberWithFloat:property]];\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/* Set other animation properties *\/\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[self addAnimation:a forKey:@&quot;animateProperty&quot;];\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;_property = property;\n}\n<\/code><\/pre>Now, all is good.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For Apple&#8217;s iOS, below is the code to implicitly animate a custom property, using float as an example, in a subclass of CALayer: \/* MyLayer.m *\/ @interface MyLayer() { &nbsp;&nbsp;&nbsp;&nbsp;BOOL _isPresenting; \/* TRUE for presentation layer *\/ } @property (nonatomic) float property; \/* normally defined in MyLayer.h *\/ @end @implementation MyLayer @synthesize property = _property; [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[10],"class_list":["post-61","post","type-post","status-publish","format-standard","hentry","category-programming","tag-ios"],"_links":{"self":[{"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=\/wp\/v2\/posts\/61","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=61"}],"version-history":[{"count":15,"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=\/wp\/v2\/posts\/61\/revisions"}],"predecessor-version":[{"id":78,"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=\/wp\/v2\/posts\/61\/revisions\/78"}],"wp:attachment":[{"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=61"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=61"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hoo.onpech.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=61"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}