iOS在启动时显示模态视图控制器,不带闪光灯

我想在第一次启动时以模态方式向用户展示教程向导。

有没有办法在应用程序启动时显示一个模态UIViewController ,至less在毫秒内看不到它后面的rootViewController

现在我正在做这样的事情(为了清晰起见,省略了首次启动检查):

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... UIStoryboard *storyboard = self.window.rootViewController.storyboard; TutorialViewController* tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:@"tutorial"]; tutorialViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; [self.window makeKeyAndVisible]; [self.window.rootViewController presentViewController:tutorialViewController animated:NO completion:NULL]; } 

没有运气。 我试图移动[self.window makeKeyAndVisible]; 之前的[... presentViewController:tutorialViewController ...]语句,但然后模式甚至不出现。

所有的presentViewController方法都要求呈现视图控制器先出现。 为了隐藏根VC,必须呈现覆盖图。 启动屏幕可以继续显示在窗口上,直到演示完成,然后淡出覆盖。

  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]; }]; }]; 

可能是你可以使用“childViewController”

 UIStoryboard *storyboard = self.window.rootViewController.storyboard; TutorialViewController* tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:@"tutorial"]; [self.window addSubview: tutorialViewController.view]; [self.window.rootViewController addChildViewController: tutorialViewController]; [self.window makeKeyAndVisible]; 

当你需要解雇你的导师时,你可以从超级视angular中删除它的视图。 也可以通过设置alpha属性在视图上添加一些animation。希望有帮助:)

布鲁斯在斯威夫特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() }) }) } } } 

这个问题在iOS 10中仍然存在。我的修复是:

  1. viewWillAppear中将模态VC作为一个childVC添加到rootVC
  2. viewDidAppear
    1. 删除作为rootVC子项的modalVC
    2. 在没有animation的情况下呈现childVC

码:

 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) } //.... } } 

布鲁斯的回答指出我的方向是正确的,但是因为我的模态可能比刚刚启动时更频繁出现(这是一个login屏幕,所以如果他们注销,就需要显示),我不想将我的覆盖直接绑定到视图控制器的介绍。

这是我提出的逻辑:

  self.window.rootViewController = _tabBarController; [self.window makeKeyAndVisible]; WSILaunchImageView *launchImage = [WSILaunchImageView new]; [self.window addSubview:launchImage]; [UIView animateWithDuration:0.1f delay:0.5f options:0 animations:^{ launchImage.alpha = 0.0f; } completion:^(BOOL finished) { [launchImage removeFromSuperview]; }]; 

在不同的部分中,我执行在典型的self.window.rootViewController presentViewController:...中呈现我的loginVC的逻辑self.window.rootViewController presentViewController:...格式,我可以使用,无论它是一个应用程序启动或其他。

如果有人关心,这里是我如何创build我的覆盖视图:

 @implementation WSILaunchImageView - (instancetype)init { self = [super initWithFrame:[UIScreen mainScreen].bounds]; if (self) { self.image = WSILaunchImage(); } return self; } 

这里是启动图像本身的逻辑:

 UIImage * WSILaunchImage() { static UIImage *launchImage = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (WSIEnvironmentDeviceHas480hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-700"]; else if (WSIEnvironmentDeviceHas568hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-700-568h"]; else if (WSIEnvironmentDeviceHas667hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-800-667h"]; else if (WSIEnvironmentDeviceHas736hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-800-Portrait-736h"]; }); return launchImage; } 

Aaaaand只是为了完成,下面是这些EnvironmentDevice方法的样子:

 static CGSize const kIPhone4Size = (CGSize){.width = 320.0f, .height = 480.0f}; BOOL WSIEnvironmentDeviceHas480hScreen(void) { static BOOL result = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ result = CGSizeEqualToSize([UIScreen mainScreen].bounds.size, kIPhone4Size); }); return result; } 

可能是一个不好的解决scheme,但是你可以创build一个2个容器的ViewController,其中两个容器都链接到一个VC。 那么你可以控制哪个容器应该在代码中可见,这是一个想法

 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; } 
 let vc = UIViewController() vc.modalPresentationStyle = .custom vc.transitioningDelegate = noFlashTransitionDelegate present(vc, animated: false, completion: nil) class NoFlashTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate { public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { if source.view.window == nil, let overlayViewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController(), let overlay = overlayViewController.view { source.view.addSubview(overlay) UIView.animate(withDuration: 0, animations: {}) { (finished) in overlay.removeFromSuperview() } } return nil } } 

这是我如何与故事板,它与多种模式。 这个例子有3个,底部,中间和顶部。

只要确保在界面构build器中正确设置了每个viewController的storyboardID即可。

 - (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; }