Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why NSAnimationContext completionHandler does not work (sometimes)?


    // wc here is an NSWindowController

    [NSAnimationContext beginGrouping];
    [[NSAnimationContext currentContext] setDuration:0.5f];

    if (duplication) {
        NSPoint origin = initialSize.origin;
        origin.y += initialSize.size.height;
        origin = [wc.window cascadeTopLeftFromPoint:origin];
        origin.y -= initialSize.size.height;
        //[[wc.window animator] setFrameOrigin:origin];   // Why setFrameOrigin and cascadeTopLeftFromPoint are not animated?
        initialSize.origin = origin;
        [[wc.window animator] setFrame:initialSize display:YES];
    }

    // This block should be invoked when all of the animations started above have completed or been cancelled.
    // For not to show the edit window till the duplication animation not finished
    [NSAnimationContext currentContext].completionHandler = ^{
        if (edit)
            [wc editDocument:self];
        else
            if (fullScreen)
                [wc.window toggleFullScreen:self];
    };

    [NSAnimationContext endGrouping];

In this case the completion block executed but unfortunately does not wait for the window reposition be finished, instead it opens the window's edit sheet immediately and moves them together.

The most strange thing is that a few lines above in the same source file the same type of completition block works fine :-O

What am I missing here?

like image 868
Hofi Avatar asked Dec 10 '25 07:12

Hofi


2 Answers

This actually isn't a bug, but it has tripped me up a ton of times. You must set the completion handler before you invoke any animations.

like image 184
Mattie Avatar answered Dec 11 '25 20:12

Mattie


Check the documentation of completionHandler:

If set to a non-nil value, a context’s completionHandler is guaranteed to be called on the main thread as soon as all animations subsequently added to the current NSAnimationContext grouping have completed or been cancelled.

Source: https://developer.apple.com/documentation/appkit/nsanimationcontext/1531132-completionhandler?language=objc

The completion handler only affects animations added after the completion handler has been set.

At the end it also says:

If no animations are added before the current grouping is ended—or the completionHandler is set to a different value—the handler will be invoked immediately.

In your case, no animations are added between setting a completion handler and the end of the current grouping, thus your completion handler is called immediately.

The correct code would be:

// wc here is an NSWindowController

[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.5f];

// This block should be invoked when all of the animations started above have completed or been cancelled.
// For not to show the edit window till the duplication animation not finished
[NSAnimationContext currentContext].completionHandler = ^{
    if (edit)
        [wc editDocument:self];
    else
        if (fullScreen)
            [wc.window toggleFullScreen:self];
};

if (duplication) {
    NSPoint origin = initialSize.origin;
    origin.y += initialSize.size.height;
    origin = [wc.window cascadeTopLeftFromPoint:origin];
    origin.y -= initialSize.size.height;
    //[[wc.window animator] setFrameOrigin:origin];   // Why setFrameOrigin and cascadeTopLeftFromPoint are not animated?
    initialSize.origin = origin;
    [[wc.window animator] setFrame:initialSize display:YES];
}

[NSAnimationContext endGrouping];
like image 34
Mecki Avatar answered Dec 11 '25 20:12

Mecki



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!