I am unable to make custom boolean properties animatable (via CATransition).

Below is the source for a custom NSView subclass with a boolean property (`visible`).

It implements `defaultAnimationForKey:` to return a CATransition instance which should be used when the property is changed (via the animator proxy) as demonstrated in the `animateVisible` method.

Problem is that it doesn’t work. What happens is that when the property is changed via the animator proxy, it shows no updates for the duration of the animation, and then after the duration, it updates the layer to the final state.

I would expect the animator proxy to retrieve the animation using `animationForKey:`, add it to the view’s layer, and then update the NSView property.

I implemented these steps in `manualAnimateVisible` and that *does* work!

So what am I missing here?

Some further questions about animations:

1. NSView returns a CATransition set to `fade` for the `hidden` property, but `someView.animator.hidden = YES` doesn’t fade out the view for me. Is this supposed to work?

2. A few non-NSView classes conform to the NSAnimatablePropertyContainer protocol, allowing animation when changing properties. Is there an easy way for my custom controllers to also conform to this protocol? Basically obtaining an animator proxy without having to write this from scratch (it sort of seems the proxy should be reusable across classes, so that subclasses can add additional properties without breaking the properties of the superclass, but the NSView animator proxy and the non-NSView animator proxies do appear to work differently, as the former can assume a de facto layer, where the latter may have zero or more layers to animate).

3. CAAnimation, CAMediaTiming, etc. seems to be data objects that are added to a CALayer and then the CALayer has all the logic, at least judging from the public API. But I see that NSLayoutConstraint also conforms to NSAnimatablePropertyContainer. This means it must be using CAAnimation objects, but it has no layers. So how is it able to use these objects without any layers? Seems it would either be through dummy layers (wasteful) or private API (which would surprise me based on how old the protocol is).

4. It is possible to call methods via the animator proxy and side effects of these are animated. For example replacing a view using `[view.superview.animator replaceSubview:view with:[[NSView alloc] initWithFrame:view.frame]]` will run the animation for the superview’s `subviews` key, even `[view.animator removeFromSuperview]` will use the animation for this key (in the superview). How is this implemented?

Side effects do seem limited to standard properties, i.e. my custom (animatable) properties does not work like this (even though they work when set explicitly via the animator proxy). This makes me think that NSView actually tests if there is an active animator proxy, but how to do such check? The NSAnimationContext.currentContext.allowsImplicitAnimation property is NO even in methods called via the animator proxy.

- - -

The sample code for my custom view below.

While searching for answers to my problem I also came across this GitHub project showing the same issue https://github.com/jjk/TestTransitions (linked to from an unanswered Stack Overflow question).

        @interface MyImageView : NSView
        @property (nonatomic, getter = isVisible) BOOL visible;
        @end

        @implementation MyImageView
        + (id)defaultAnimationForKey:(NSAnimatablePropertyKey)key
        {
           if([key isEqualToString:@"visible"])
           {
              CATransition* transition = [CATransition animation];
              transition.type           = kCATransitionPush;
              transition.subtype        = kCATransitionFromRight;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
              transition.duration       = 1;
              return transition;
           }
           return [super defaultAnimationForKey:key];
        }

        - (instancetype)initWithFrame:(NSRect)frameRect
        {
           if(self = [super initWithFrame:frameRect])
           {
              self.wantsLayer = YES;
              self.layer = [CALayer layer];
           }
           return self;
        }

        - (void)setVisible:(BOOL)flag
        {
           if(_visible == flag)
              return;

           _visible = flag;
self.layer.contents = flag ? [NSImage imageNamed:NSImageNameMultipleDocuments] : nil;
        }

        - (void)animateVisible
        {
           self.animator.visible = YES;
        }

        - (void)manualAnimateVisible
        {
           if(id anim = [self animationForKey:@"visible"])
              [self.layer addAnimation:anim forKey:@"visible"];
           self.visible = YES;
        }
        @end
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to