5
votes

UPDATED: Fixed via "screenshot" method outline below. This works but is there a more elegant way?

How would I go about dismissing a stack of modal view controllers with animation without flashing on screen any of the presented VCs between the top and bottom? Trying to do this with animation isn't working. See code below and inline comments describing my issue. You can copy/paste this code into a new project in Xcode to see for yourself if you'd like!

//
//  ViewController.m
//  MultipleModals
//

#import "ViewController.h"
#import "MyViewController.h"
#import "MyHelper.h"

@interface ViewController ()

@end

@implementation ViewController

static BOOL doAgain = YES; // So when red appears again, we don't endlessly cycle (for testing)
- (void)viewDidAppear:(BOOL)animated
{
    // Invoke super
    [super viewDidAppear:animated];

    // Prevent loop when we dismiss all the way back to red (for testing)
    if (doAgain)
    {
        // Okay here's where the demo code starts...

        // PRESENTING a full stack of modals WITHOUT animation WORKS and results in the user
        // only seeing orange when this red view controller "appears" (red never actually appears, which is great)...

        MyViewController *purple = [[MyViewController alloc] init];
        purple.title = @"purple"; // For use in MyViewController's dealloc method
        purple.view.backgroundColor = [UIColor purpleColor];
        [self presentViewController:purple animated:NO completion:^{ // Purple successfully gets presented and the user never sees purple, great.
            NSLog(@"Purple?");
            MyViewController *green = [[MyViewController alloc] init];
            green.view.backgroundColor = [UIColor greenColor];
            green.title = @"green"; // For use in MyViewController's dealloc method
            [purple presentViewController:green animated:NO completion:^{ // Green successfully gets presented and the user never sees green, great.
                NSLog(@"Green?");
                MyViewController *orange = [[MyViewController alloc] init];
                orange.view.backgroundColor = [UIColor orangeColor];
                orange.title = @"orange"; // For use in MyViewController's dealloc method
                [green presentViewController:orange animated:NO completion:^{ // Orange successfully gets presented and the user DOES see orange, great.
                    NSLog(@"Orange?");

                    // FIXED MY ISSUE STARTING HERE

                    // Comment out the following code to toggle between
                    // the "flashing purple issue" and "the desired outcome" (single
                    // animation from top to bottom regardless of how many VCs are
                    // on the stack, i.e. no flashing).

                    // Get orange screenshot
                    UIImage *orangeScreenShotImage = [MyHelper screenshot];
                    UIImageView *orangeScreenShotImageView = [[UIImageView alloc] initWithImage:orangeScreenShotImage];

                    // Give purple an orange screenshot since orange will just "flash away" and then purple will animate
                    // away but we'll disguise purple to appear as if it's orange by layering a screenshot of orange on purple. Boom.
                    [purple.view addSubview:orangeScreenShotImageView];

                    // FIXED MY ISSUE ENDING HERE

                    // FOR TESTING PURPOSES... dismiss after 5 seconds...
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                        doAgain = NO; // Prevent viewDidAppear loop (related to my testing code)...

                        // THIS IS MY BUG HERE. WHEN I WANT TO **ANIMATE** THE DISMISSAL OF ORANGE ALL THE WAY BACK TO RED, HOWEVER, I SEE PURPLE FOR A FLASH BEFORE RED!! WHY?

                        // If I do not animate, things work as expected and I go from orange directly back to red in one flash. Why can't I go from orange back red WITH ANIMATION without seeing a flash of purple?
                        BOOL animateDismissalOfOrangeBackToRed = YES; // YES causes me to see a flash of purple before red, why?
                        [self dismissViewControllerAnimated:animateDismissalOfOrangeBackToRed completion:^{
                            NSLog(@"Back to red...");
                        }];
                    });
                }];
            }];
        }];
    }
}

- (void)viewDidLoad
{
    // Invoke super
    [super viewDidLoad];

    // Set self's background color
    self.view.backgroundColor = [UIColor redColor]; // Color of self, root VC
}

@end

MyViewController.m (enables us to have a custom dealloc method for debugging)

//
//  MyViewController.m
//  MultipleModals
//

#import "MyViewController.h"

@interface MyViewController ()

@end

@implementation MyViewController

- (void)dealloc
{
    NSLog(@"Inside dealloc self.title = %@", self.title);
}

@end

UPDATED: Added new MyViewController.m file for dealloc debugging.

Interestingly, the log comes out looking like this:

2014-11-20 10:06:28.847 MultipleModals[5470:946774] Purple?
2014-11-20 10:06:28.851 MultipleModals[5470:946774] Green?
2014-11-20 10:06:28.853 MultipleModals[5470:946774] Orange?
2014-11-20 10:07:04.055 MultipleModals[5470:946774] Inside dealloc self.title = orange
2014-11-20 10:07:04.056 MultipleModals[5470:946774] Inside dealloc self.title = green
2014-11-20 10:07:04.565 MultipleModals[5470:946774] Back to red...
2014-11-20 10:07:04.566 MultipleModals[5470:946774] Inside dealloc self.title = purple

UPDATED: I've added a sample project so you can observe this first handle very easily if you'd like: https://github.com/johnerck/MultipleModals

Also, I have read Presenting View Controllers from Other View Controllers many times. They even say, "For example, if the user cancels the current operation, you can remove all objects in the chain by dismissing the first presented view controller. Dismissing a view controller dismisses not only that view controller but also any view controllers it presented." I am seeing this behavior but animation is displaying 3 views in total, not the expected 2.

1

1 Answers

0
votes

It is not a bug. You change the removal animation only for self.presentedViewController("purple") when dismissViewControllerAnimated but not for all nested VCs. When "purple" receives a message about deleting with animation, it deletes all presentedViewController without animation. And then you see the animation of the "purple" without nested controllers. To check this you can simply create your own VC class and check -dealloc method.