iOS Present Modal View Controller on Startup Without Flash

Presenting a specific view controller once immediately after launch screen closes

I went with a slightly different approach to solve this.

I embedded the main app VC in a navigation controller, then pushing the tutorialVC in App Delegate didFinishLaunchingWithOptions.

let tutorialVC = UIStoryboard(name: "FirstTutorial", bundle: nil)
let firstVC = tutorialVC.instantiateViewController(withIdentifier: "TutorialSBID") as UIViewController
if let navigationController = self.window?.rootViewController as? UINavigationController
{
navigationController.pushViewController(firstVC, animated: false)
}
return true

Perform Segue on ViewDidLoad

I answered a similar question where the developer wanted to show a login screen at the start. I put together some sample code for him that can be downloaded here. The key to solving this problem is calling things at the right time if you want to display this new view controller, you will see in the example you have to use something like this

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"LoginViewController"];
[vc setModalPresentationStyle:UIModalPresentationFullScreen];

[self presentModalViewController:vc animated:YES];
}

I also have an explanation of how segues and storyboards work that you can see here

How to skip 2 views when using navigation controller

In a nutshell, the most straightforward solution is to use -[UINavigationController setViewControllers:animated:].

You need to do the following to achieve what you want:

  1. assign storyboard IDs to all your controllers,
  2. instantiate all three view controller from the story board (this is lightweight as long as you don't do most of the initialisation work in controller's constructors)
  3. put them into an array in the same order you have them in storyboard,
  4. call -[UINavigationController setViewControllers:] with the array as the first argument.

That's it. The code in your -[AppDelegate application:didFinishLaunchingWithOptions:] might look like this (after checking your skipping condition of course):

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *vc1 = [storyboard instantiateViewControllerWithIdentifier:@"MyAuth"]; //if you assigned this ID is storyboard
UIViewController *vc2 = [storyboard instantiateViewControllerWithIdentifier:@"Login"]; //if you assigned this ID is storyboard
UIViewController *vc3 = [storyboard instantiateViewControllerWithIdentifier:@"Details"];
NSArray *controllers = @[vc1, vc2, vc3];
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
[navController setViewControllers:controllers];

If you paste just this to -[AppDelegate application:didFinishLaunchingWithOptions:], you'll see that it works immediately.

Dialog box displayed by EKEventStore.authorizationStatus(for:) apparently hidden behind overlay

The problem was not that the dialog box was hidden behind the overlay but that I called EKEventStore's requestAccess(to:completion:) synchronously (by waiting after the call for a semaphore that was signaled inside the completion handler) inside application(_:didFinishLaunchingWithOptions:launchOptions:). Hence the entire app stalled and the dialog box was not even displayed.

The solution consisted in requesting access asynchronously. The following scheme is currently good enough:

  • instance variable calendarEvent is nil if access to an EKCalendar was not (yet) granted
  • requestAccess(to:completion:) is called from inside application(_:didFinishLaunchingWithOptions:launchOptions:)
  • calendarEvent is set to this EKCalendar in completion handler of requestAccess(to:completion:)
  • before reading or writing to calendar app checks if calendarEvent != nil
  • during cycle of run loop with call to application(_:didFinishLaunchingWithOptions:launchOptions:) calendarEvent remains nil; app behaves as if calendar were empty
  • during later cycles of run loop calendarEvent has been set; app actually writes to calendar only during those later cycles


Related Topics



Leave a reply



Submit