iOS Present modal view controller on startup without flash

前端 未结 9 1149
囚心锁ツ
囚心锁ツ 2020-12-08 00:53

I\'d like to present modally, at first startup, a tutorial wizard to the user.

Is there a way to present a modal UIViewController on application startup

相关标签:
9条回答
  • 2020-12-08 01:01

    Bruce's upvoted answer in Swift 3:

    if let vc = window?.rootViewController?.storyboard?.instantiateViewController(withIdentifier: "LOGIN")
        {
            let launch = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()!
            launch.view.frame = vc.view.bounds
            launch.view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
            window?.makeKeyAndVisible()
            window?.addSubview(launch.view)
    
            //Using DispatchQueue to prevent "Unbalanced calls to begin/end appearance transitions"
            DispatchQueue.global().async {
                // Bounce back to the main thread to update the UI
                DispatchQueue.main.async {
                    self.window?.rootViewController?.present(vc, animated: false, completion: {
    
                        UIView.animate(withDuration: 0.5, animations: {
                            launch.view.alpha = 0
                        }, completion: { (_) in
                            launch.view.removeFromSuperview()
                        })
                    })
                }
            }
        }
    
    0 讨论(0)
  • 2020-12-08 01:03

    This is how I do it with storyboards and it works with multiple modals. This example has 3. Bottom, middle, and top.

    Just be sure to have the storyboardID of each viewController set correctly in interface builder.

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
        UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        BottomViewController *bottomViewController = [storyboard instantiateViewControllerWithIdentifier:@"BottomViewController"];
        UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        [window setRootViewController:bottomViewController];
        [window makeKeyAndVisible];
    
        if (!_loggedIn) {
            MiddleViewController *middleViewController = [storyboard instantiateViewControllerWithIdentifier:@"middleViewController"];
            TopViewController *topViewController = [storyboard instantiateViewControllerWithIdentifier:@"topViewController"];
    
            [bottomViewController presentViewController:middleViewController animated:NO completion:nil];
            [middleViewController presentViewController:topViewController animated:NO completion:nil];
    
        }
        else {
            // setup as you normally would.
        }
    
        self.window = window;
    
        return YES;
    }
    
    0 讨论(0)
  • 2020-12-08 01:04

    All presentViewController methods require the presenting view controller to have appeared first. In order to hide the root VC an overlay must be presented. The Launch Screen can continued to be presented on the window until the presentation has completed and then fadeout the overlay.

        UIView* overlayView = [[[UINib nibWithNibName:@"LaunchScreen" bundle:nil] instantiateWithOwner:nil options:nil] firstObject];
    overlayView.frame = self.window.rootViewController.view.bounds;
    overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    
    UIStoryboard *storyboard = self.window.rootViewController.storyboard;
    TutorialViewController* tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:@"tutorial"];
    tutorialViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    [self.window makeKeyAndVisible];
    [self.window addSubview:overlayView];
    [self.window.rootViewController presentViewController:tutorialViewController animated:NO completion:^{
        NSLog(@"displaying");
        [UIView animateWithDuration:0.5 animations:^{
            overlayView.alpha = 0;
        } completion:^(BOOL finished) {
            [overlayView removeFromSuperview];
        }];
    }];
    
    0 讨论(0)
  • 2020-12-08 01:04

    May be a bad solution, but you could make a ViewController with 2 containers in it, where both of the containers are linked to a VC each. Then you can control which container should be visible in code, that's an idea

    if (!firstRun) {
        // Show normal page
        normalContainer.hidden = NO;
        firstRunContainer.hidden = YES;
    } else if (firstRun) {
        // Show first run page or something similar
        normalContainer.hidden = YES;
        firstRunContainer.hidden = NO;
    }
    
    0 讨论(0)
  • 2020-12-08 01:05

    This problem still exists in iOS 10. My fix was:

    1. in viewWillAppear add the modal VC as a childVC to the rootVC
    2. in the viewDidAppear:
      1. Remove the modalVC as a child of the rootVC
      2. Modally present the childVC without animation

    Code:

    extension UIViewController {
    
        func embed(childViewController: UIViewController) {
            childViewController.willMove(toParentViewController: self)
    
            view.addSubview(childViewController.view)
            childViewController.view.frame = view.bounds
            childViewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
    
            addChildViewController(childViewController)
        }
    
    
        func unembed(childViewController: UIViewController) {
            assert(childViewController.parent == self)
    
            childViewController.willMove(toParentViewController: nil)
            childViewController.view.removeFromSuperview()
            childViewController.removeFromParentViewController()
        }
    }
    
    
    class ViewController: UIViewController {
    
        let modalViewController = UIViewController()
    
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
    
            //BUG FIX: We have to embed the VC rather than modally presenting it because:
            // - Modal presentation within viewWillAppear(animated: false) is not allowed
            // - Modal presentation within viewDidAppear(animated: false) is not visually glitchy
            //The VC is presented modally in viewDidAppear:
            if self.shouldPresentModalVC {
                embed(childViewController: modalViewController)
            }
            //...
        }
    
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            //BUG FIX: Move the embedded VC to be a modal VC as is expected. See viewWillAppear
            if modalViewController.parent == self {
                unembed(childViewController: modalViewController)
                present(modalViewController, animated: false, completion: nil)
            }
    
            //....
        }
    }
    
    0 讨论(0)
  • 2020-12-08 01:05

    It can be late but in your AppDelegate you can do this :

    //Set your rootViewController
    self.window.rootViewController=myRootViewController;
    //Hide the rootViewController to avoid the flash
    self.window.rootViewController.view.hidden=YES;
    //Display the window
    [self.window makeKeyAndVisible];
    
    if(shouldPresentModal){
    
        //Present your modal controller
        UIViewController *lc_viewController = [UIViewController new];
        UINavigationController *lc_navigationController = [[UINavigationController alloc] initWithRootViewController:lc_viewController];
        [self.window.rootViewController presentViewController:lc_navigationController animated:NO completion:^{
    
            //Display the rootViewController to show your modal
            self.window.rootViewController.view.hidden=NO;
        }];
    }
    else{
    
        //Otherwise display the rootViewController
        self.window.rootViewController.view.hidden=NO;
    }
    
    0 讨论(0)
提交回复
热议问题