Question

I'm using a borderless NSWindow to create a slide out menu like you might find in many IOS apps. I currently use setFrame:display:animate: with two pre-defined rects to create the sliding effect, and I also have an animation that fades the mainwindow's background, which currently runs after the window has finished animating.

I would very much like to run both of these animations simultaneously. However, setFrame:display:animate seems to block all other code until it has finished.

Is there a way to override this feature of setFrame:display:animate? Or can anyone recommend a work around?

I have working methods that run [[self animator]setFrame:display:] but I would prefer to use setFrame:display:animate because it is featured heavily in my current code and is easy to configure for duration.

Some code:

//Currently used to animate the window
-(void)showPopWindow{
    [self selectAnimationTimeFor:NCpOpen];    
    [self setFrame:_initialRect display:YES];
    [self makeKeyAndOrderFront:NSApp];
    [self setFrame:_finalRect display:YES animate:YES];
}

And this is the other animation I have, which currently runs after setFrame has finished (note this runs on an NSView's layer).

//...
[layer setBackgroundColor:CGColorCreateGenericRGB(0, 0, 0, 0.5)];

CABasicAnimation *fadeInBackground = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeBackground.duration = [self openAnimationResizeTime];
fadeBackground.fromValue = [NSNumber numberWithFloat:0.0f];
fadeBackground.toValue = [NSNumber numberWithFloat:1.0f];

[layer addAnimation:fadeInBackground forKey:@"opacity"];
Was it helpful?

Solution

Solved problem by using NSAnimationContext along with [[self animator]setAlphaValue:], which let me run simultaneous animations and then execute a block on completion.

Documentation:

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSAnimationContext_class/Introduction/Introduction.html#//apple_ref/occ/clm/NSAnimationContext/runAnimationGroup:completionHandler:

Working code:

// To avoid a strong reference cycle to self in block
__weak typeof(self)weakSelf = self;

[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
    [context setDuration: myDuration];
    [[weakSelf animator] setFrame:_initialRect display:YES];

    // ... other animations

} completionHandler:^{
    NSLog(@"done");
    [[weakSelf overCastView] removeFromSuperview];
    weakSelf.overCastView = nil;
}];

OTHER TIPS

The following worked for me (with credit to this answer). I think this is a more "correct" solution due to the explicit NSAnimationNonblocking:

NSRect myNewFrame = NSMakeRect(0, 0, 123, 456);

NSDictionary* windowResize = [NSDictionary dictionaryWithObjectsAndKeys: window, NSViewAnimationTargetKey, [NSValue valueWithRect: myNewFrame], NSViewAnimationEndFrameKey, nil];
NSArray* animations = [NSArray arrayWithObjects:windowResize, nil];
NSViewAnimation* animation = [[NSViewAnimation alloc] initWithViewAnimations: animations];

[animation setAnimationBlockingMode: NSAnimationNonblocking];
[animation setAnimationCurve: NSAnimationEaseIn];
[animation setDuration: 0.4];     
[animation startAnimation]; 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top