Brittany
Brittany

Reputation: 1439

How to present View Controller modally on app launch?

I've created a screen that ONLY appears the first time that my app is launched; it works great. However, I'd like to present this screen modally on top of my root view controller (EditorsNoteViewController being the screen I want to present modally on first launch). Does anyone know how I would do this? Here is what I have so far:

Appdelegate.m

        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
        {
            self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
            if ([[NSUserDefaults standardUserDefaults] boolForKey:@"HasLaunchedOnce"])
            {
                NSLog(@"not first launch");

                      self.viewController = [[UIStoryboard storyboardWithName:@"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:@"articlesNav"];
                self.window.rootViewController = self.viewController;

            }
            else
            {
                [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"HasLaunchedOnce"];
                [[NSUserDefaults standardUserDefaults] synchronize];



                self.viewController = [[UIStoryboard storyboardWithName:@"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:@"articlesNav"];
                self.window.rootViewController = self.viewController;


     EditorsNoteViewController *vc = [[EditorsNoteViewController alloc]init];
             [self.viewController.navigationController presentViewController:vc animated:YES completion:nil];

}
                [self.window makeKeyAndVisible];
    return YES;
}

Upvotes: 5

Views: 2424

Answers (3)

quentinadam
quentinadam

Reputation: 3150

I have read about and encountered this issue many times. A way to completely get around this issue (without any flickering or warnings) is to consider a custom container view controller that will contain your root view controller and your modally presented view controller, as child view controllers. The container view controller can then transition between the two with custom animations, with a very similar effect as present/dismiss.

Let's consider the following container view controller :

@implementation ContainerViewController {
    NSMutableArray* stack;
}

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        stack = [NSMutableArray new];
    }
    return self;
}

- (void) pushViewController:(UIViewController*) viewController {
    UIViewController* currentViewController = [stack lastObject];
    [stack addObject:viewController];
    if (self.isViewLoaded) {
        [self addChildViewController:viewController];
        [currentViewController willMoveToParentViewController:nil];
        viewController.view.frame = CGRectOffset(self.view.bounds, 0, self.view.bounds.size.height);
        [self transitionFromViewController:currentViewController toViewController:viewController duration:0.3   options:kNilOptions animations:^{
            viewController.view.frame = self.view.bounds;
        } completion:^(BOOL finished) {
            [viewController didMoveToParentViewController:self];
            [currentViewController removeFromParentViewController];
        }];
    }
}

- (void) popViewController {
    UIViewController* currentViewController = [stack lastObject];
    [stack removeLastObject];
    if (self.isViewLoaded) {
        UIViewController* viewController = [stack lastObject];
        [self addChildViewController:viewController];
        [currentViewController willMoveToParentViewController:nil];
        [self transitionFromViewController:currentViewController toViewController:viewController duration:0.3   options:kNilOptions animations:^{
            [self.view sendSubviewToBack:viewController.view];
            currentViewController.view.frame = CGRectOffset(self.view.bounds, 0, self.view.bounds.size.height);
        } completion:^(BOOL finished) {
            [viewController didMoveToParentViewController:self];
            [currentViewController removeFromParentViewController];
        }];
    }
}

- (void)viewDidLoad {
    [super viewDidLoad];
    UIViewController* viewController = [stack lastObject];
    [self addChildViewController:viewController];
    [self.view addSubview:viewController.view];
    [viewController didMoveToParentViewController:self];
}

@end

In your AppDelegate, you can then have something along those lines :

ContainerViewController* viewController = [ContainerViewController new];
[viewController pushViewController: [RootViewController new];

if ([self shouldPresentModalViewController]) {
    [viewController pushViewController: [ModalViewController new];
}

self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];

Somewhere in the modal view controller, you can use the following code to dismiss it:

[((ContainerViewController*) self.parentViewController) popViewController];

Upvotes: 1

Dulgan
Dulgan

Reputation: 6694

I figured out that you'll have to call

[self.window makeKeyAndVisible];

before presenting the view controller

Upvotes: 5

Bogdan
Bogdan

Reputation: 67

Assuming that you have created your EditorsNoteViewController in a storyboard, you should initialise it like this:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
EditorsNoteViewController *vc = (EditorsNoteViewController*)[storyboard instantiateViewControllerWithIdentifier:@"EditorsNoteOrWhateverIdentifierYouChose"];

Try to check your NSUserDefaults in your RootViewController -viewDidLoad, and if it is the first time, present the new UIViewController there (So that you don't need to mess around with your AppDelegate).

If you want to present it animated, you can do it in -viewDidAppear instead of -viewDidLoad.

Upvotes: 0

Related Questions