How to dismiss a modal VC with fade out animation?

Gruntcakes picture Gruntcakes · Jan 21, 2012 · Viewed 34.4k times · Source

I am using the following code in my presenting VC to fade in the child modal VC, and this works fine:

self.infoViewController.view.alpha = 0.0;
[self.navigationController presentModalViewController:self.infoViewController animated:NO];
[UIView animateWithDuration:0.5
             animations:^{self.infoViewController.view.alpha = 1.0;}];

However I can't get it to fade out, I have tried a few things, this is the latest I tried that doesn't work:

- (IBAction)dismissAction:(id)sender
{
if ([[self parentViewController] respondsToSelector:@selector(dismissModalViewControllerAnimated:)])
{
    [[self parentViewController] dismissModalViewControllerAnimated:YES];
    self.parentViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    self.parentViewController.view.alpha = 0.0;
    [UIView animateWithDuration:0.5
                     animations:^{self.parentViewController.view.alpha  = 1.0;}];
} else 
{
    [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
    self.presentedViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    self.presentedViewController.view.alpha = 0.0;
    [UIView animateWithDuration:0.5
                     animations:^{
                         self.presentedViewController.view.alpha  = 1.0;}];
}

}

The modal view controller is faded out but immediately, not over a time period like it is when its displayed.

Answer

NJones picture NJones · Jan 22, 2012

This (original part) is not to take away from H2CO3's correct answer. UIModalTransitionStyleCrossDissolve does pretty-much exactly the effect you're looking for. You are just not setting the modalTransitionStyle until it's to late. Replace all of your code with these functions in there respective positions:

-(void)show{
    self.infoViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    [self presentModalViewController:self.infoViewController animated:YES];
}
- (IBAction)dismissAction:(id)sender{
    [self dismissModalViewControllerAnimated:YES];
}

Edit in response to timing being an issue: Let's talk about the offending code. We'll concentrate on just the if true part, since it's essentially identical to the else.

[[self parentViewController] dismissModalViewControllerAnimated:YES];
self.parentViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
self.parentViewController.view.alpha = 0.0;
[UIView animateWithDuration:0.5
                 animations:^{self.parentViewController.view.alpha  = 1.0;}];

If you're looking for a reciprocal animation this isn't it. In your original animation you set the next view's alpha to 0, then presented the next view controller, then set it's view's alpha to 1. So logically you need to dismiss the view controller after the animation; This is really easy using blocks. The code would look something like this:

[UIView animateWithDuration:0.5 animations:^{
    self.view.alpha = 0;
} completion:^(BOOL b){
    [self.presentingViewController dismissModalViewControllerAnimated:NO];
    self.view.alpha = 1;
}];

This line of code animates the view's alpha to 0, then (upon completion) dismisses the presented view controller, and sets the view's alpha back to 1. This is a reciprocal animation.