Ios 在应用程序启动时以模式显示视图控制器

Ios 在应用程序启动时以模式显示视图控制器,ios,objective-c,Ios,Objective C,我的应用程序有一个设置屏幕,如果满足某些条件,该屏幕应以模式显示在根视图控制器上 我已经在SO和互联网上四处查看过,到目前为止,最接近的答案是: 然而,这种方法存在两个问题: 在iOS 8中,这样做会在控制台中显示日志,这似乎不是错误,但可能并不好: 对UITabBarController的开始/结束外观转换的不平衡调用:0x7fe20058d570。 当应用程序启动时,根视图控制器实际上会短暂显示,然后淡入显示的视图控制器(即使我在我的presentViewController方法中显式调用

我的应用程序有一个设置屏幕,如果满足某些条件,该屏幕应以模式显示在根视图控制器上

我已经在SO和互联网上四处查看过,到目前为止,最接近的答案是:

然而,这种方法存在两个问题:

  • 在iOS 8中,这样做会在控制台中显示日志,这似乎不是错误,但可能并不好:
  • 对UITabBarController的开始/结束外观转换的不平衡调用:0x7fe20058d570。

  • 当应用程序启动时,根视图控制器实际上会短暂显示,然后淡入显示的视图控制器(即使我在我的
    presentViewController
    方法中显式调用
    animated:NO
  • 我知道我可以在
    ApplicationIDFinishLaunchingWithOptions:
    中动态设置我的根控制器,但我特别想以模式显示设置屏幕,这样当用户使用完它时,它就会消失,并显示应用程序的真实第一视图。这就是说,我不想动态地将我的根视图控制器更改为我的设置屏幕,并在用户完成设置后以模式呈现我的应用体验

    在我的根视图控制器
    viewDidLoad
    方法上显示视图控制器也会导致应用程序第一次启动时UI出现明显的闪烁

    在应用程序呈现任何内容之前,是否可以通过编程方式以模态方式呈现视图控制器,以便第一个视图是模态视图控制器

    更新:感谢您的评论,按照建议添加我当前的代码:

    在my
    AppDelegate.m
    中:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    
        [self.window makeKeyAndVisible];
        [self.window.rootViewController presentViewController:[storyboard instantiateViewControllerWithIdentifier:@"setupViewController"] animated:NO completion:NULL];
    
        return YES;
    }
    
    这就是我所需要的,只是当应用程序启动时,它会短暂地显示窗口的根视图控制器一秒钟,然后淡出setupViewController,我觉得这很奇怪,因为我在没有动画的情况下呈现它,淡出并不是模态视图控制器的呈现方式

    唯一让我接近的是在根视图控制器的view Dod load方法中手动添加视图,如下所示:

    - (void)viewDidLoad
    {
        [self.view addSubview:setupViewController.view];
        [self addChildViewController:setupViewController];
    }
    
    这种方法的问题是,我不能再“本机”关闭setupViewController,现在需要自己处理视图层次结构并设置动画,如果这是唯一的解决方案,这很好,但我希望有一种被认可的方式,在根视图控制器显示之前,在没有动画的情况下添加视图控制器

    更新2:在尝试了很多东西并等待了2个月的答案后,这个问题提出了最有创意的解决方案:

    我想是时候接受这一点了,即在根视图控制器出现之前,不可能以无动画的方式呈现视图。但是,该线程中的建议是创建启动屏幕的实例,并将其保持在比默认值更长的时间,直到模态视图控制器有机会显示自己

    我知道我可以在ApplicationIDFinishLaunchingWithOptions中动态设置我的根控制器:但我特别希望以模式显示设置屏幕,这样当用户使用完它时,它就会消失,并显示应用程序的真实第一视图

    我有两个建议。一种是尝试在
    viewdide:
    中执行此操作。我试过了,虽然如果仔细观察,您确实可以看到根视图控制器的视图,但您几乎看不到它,有时如果眨眼,您根本看不到它:

    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self presentViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"setupViewController"] animated:NO completion:NULL];
    }
    
    当然,您需要添加一个标志,这样您就不会在每次调用
    viewdide:
    时都这样做,否则您将永远无法回到这个视图控制器!但这是微不足道的,我把它留给读者作为练习

    我的另一个建议是使用自定义的嵌入式(子)视图控制器,而您显然已经考虑过这样做。这就绕过了整个“演示”的局限性

    正如您所说,我会动态地启动和设置事物,如果需要的话,在子视图控制器存在的情况下,在启动过程中对其进行配置。子视图控制器的视图只覆盖根视图控制器的视图。这就是用户在应用程序启动时所看到的


    然后,当用户的设置过程结束并且用户“取消”该视图时,您可以通过动画将该视图向下撕下,并移除子视图控制器-下面显示根视图控制器的视图。动画将使这一切与当前视图的取消无法区分,即使它不是一个真正的视图。

    我和你在同一条船上,找到了相同的答案。我了解到,通过将setupViewController的
    modalPresentationStyle
    设置为
    .OverCurrentContext
    .OverFullScreen
    ,可以解决第一个问题(不平衡呼叫警告)。问题解决了——所以我想

    直到后来我才注意到第二个问题,那是我无法忍受的。。。回到原点

    正如你所说,我想要一个具有普通视图层次结构的解决方案,我不想“伪造”某些东西。我认为最优雅的解决方案是在第一次解雇setupViewController时切换windows rootViewController

    因此,在启动时,可以将setupViewController设置为rootViewController(如果需要):

    安装完成后,在appDelegate中调用一个方法以切换到“real”rootViewController:

    func switchToTabBarController() {
        let setupUpViewController = window!.rootViewController!
    
        tabBarController.view.frame = window!.bounds
        window!.insertSubview(tabBarController.view, atIndex: 0)
    
        let height = setupUpViewController.view.bounds.size.height
        UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1, options: .allZeros, animations: { () -> Void in
            setupUpViewController.view.transform = CGAffineTransformMakeTranslation(0, height)
            }) { (completed) -> Void in
                self.window!.rootViewController = self.tabBarController
        }
    }
    
    我在看一部“封面垂直”的动画。对于crossfade和其他,您可以使用
    UIView.transitionFromView(fromView:UIView,toView:UIView…)
    。此后,您可以以正常方式显示/关闭setupController,因此您的doneButton操作可能如下所示:

    @IBAction func doneButtonSelected(sender: UIButton) {
        if presentingViewController != nil {
            presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
        } else {
            let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            appDelegate.switchToTabBarController()
        }
    }
    
    事实上,我是
    @IBAction func doneButtonSelected(sender: UIButton) {
        if presentingViewController != nil {
            presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
        } else {
            let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            appDelegate.switchToTabBarController()
        }
    }
    
    let launchScreenView = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()!.view!
    launchScreenView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    launchScreenView.frame = window!.rootViewController!.view.bounds
    window?.rootViewController?.view.addSubview(launchScreenView)
    window?.makeKeyAndVisible()
    // avoiding: Unbalanced calls to begin/end appearance transitions.
    DispatchQueue.global().async {
        DispatchQueue.main.async {
            self.window?.rootViewController?.present(myViewControllerToPresent, animated: false, completion: {
                launchScreenView.removeFromSuperview()
            })
        }
    }