objective-cmacosappkitcaanimationnstouchbar

NSScrubber pan animation end notification


The touchbar-specific NSScrubber control scrolls with inertia on a pan gesture. I want to be notified of this animation's end to perform some function.

Try 1

The NSScrubberDelegate has a - didFinishInteractingWithScrubber: method which I implemented. However, soon after I stop manipulating the scrubber directly -- lifts the finger off the touchbar -- I get a callback, but the scroll continues to happen due to inertia. The final item that gets selected is NOT the one when this delegate method was called back.

Try 2

Digging further, I came across NSAnimation. Though it isn't documented clearly, I gather that scrubber is also a NSAnimatablePropertyContainer, as its selectedIndex property documentation says one can animate the selection through the animator proxy thus: scrubber.animator.selectedIndex = i. By that virtue, assuming that the animated property for the smooth panning is the boundsOrigin, I tried querying it.

I was able to get a CAAnimation by doing this

CAAnimation* a = [NSScrubber defaultAnimationForKey:@"boundsOrigin"];
// returns the same pointer value as above
// a = [myScrubber animationForKey:@"boundsOrigin"];
a.delegate = self;

...

- (void)animationDidStop:(CAAnimation *)anim
                finished:(BOOL)flag {
    if (flag == YES)
        NSLog(@"Animation ended!\n");
}

I get a valid pointer value for a. However, I get numerous calls to animationDidStop with all of them having flag = YES; as the scrubber scrolls I keep getting these calls and when the scroll stops the calls stop. This feels closest to what I want but I dunno why so many calls come instead of just one when the animation ends.

Since NSScrubber's NSView or NSScrollView aren't exposed, I'm not sure if I'm querying the right object to get to the right NSAnimation.

Try 3

I also tried the hacky route of doing this on manipulation end code in vain

-(void)didFinishInteractingWithScrubber:(NSScrubber *)scrubber {
    NSLog(@"Manipulation ended\n");
    NSAnimationContext*c = NSAnimationContext.currentContext;
    [c setCompletionHandler:^{
        NSLog(@"Inertial scrolling stopped!\n");
    }];
}

The completion handler is called almost immediately, before the inertial scroll stops :(

Ask

Is there anyway to know when the scrubber's pan gesture inertial animation ends?


Solution

  • I finally found a way to register a callback for the pan gesture's inertial scroll animation end.

    Like any other scroll view, this also has the NSScrollViewDidEndLiveScrollNotification. Use the notification centre to register for the callback!

    NSScrollView *sv = myScrubber.enclosingScrollView;
    // register for NSScrollViewWillStartLiveScrollNotification if start is also needed
    [[NSNotificationCenter defaultCenter] addObserverForName:NSScrollViewDidEndLiveScrollNotification
                                                      object:sv
                                                       queue:nil
                                                  usingBlock:^(NSNotification * _Nonnull note) {
                                                      NSLog(@"Scroll complete");
                                                  }];
    

    Thanks to this answer for showing this approach.