刚开始使用Xcode 4.5,我在控制台得到了这个错误:

警告:试图在< ViewController: 0x1ec3e000>上显示< finishViewController: 0x1e56e0a0 >,其视图不在窗口层次结构中!

视图仍在显示,应用程序中的一切都在正常工作。这是iOS 6的新功能吗?

这是我用来在视图之间更改的代码:

UIStoryboard *storyboard = self.storyboard;
finishViewController *finished = 
[storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];

[self presentViewController:finished animated:NO completion:NULL];

当前回答

我修复了这个错误,将最顶部的视图控制器存储为常量,这是在rootViewController的while循环中发现的:

if var topController = UIApplication.shared.keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }
    topController.present(controller, animated: false, completion: nil)
    // topController should now be your topmost view controller
}

其他回答

它工作得很好,试试这个。链接

UIViewController *top = [UIApplication sharedApplication].keyWindow.rootViewController;
[top presentViewController:secondView animated:YES completion: nil];

我已经结束了这样一个代码,最终工作给我(Swift),考虑到你想要显示一些viewController从几乎任何地方。当没有可用的rootViewController时,这段代码显然会崩溃,这是开放式结局。它也不包括通常需要切换到UI线程使用

dispatch_sync(dispatch_get_main_queue(), {
    guard !NSBundle.mainBundle().bundlePath.hasSuffix(".appex") else {
       return; // skip operation when embedded to App Extension
    }

    if let delegate = UIApplication.sharedApplication().delegate {
        delegate.window!!.rootViewController?.presentViewController(viewController, animated: true, completion: { () -> Void in
            // optional completion code
        })
    }
}

该消息显示为警告,有时代码拒绝工作。(!引文:更新的SDK可能有严格的规则)。

我遇到这种情况的原因不止一个,主要是复杂的视图控制器场景。举个例子。

Scenario: MainViewController (responsible to load: ViewControllerA & ViewControllerB)

从MainViewController中呈现ViewControllerA,并且在不解散ViewControllerA的情况下,尝试从MainViewController中呈现viewControllerB(使用委托方法)。

在这个场景中,你必须确保你的viewcontrollerera被解散,然后ViewControllerB被调用。

因为在呈现ViewControllerA之后(ViewControllerA开始负责显示视图和视图控制器,当MainViewController尝试加载另一个视图控制器时,它拒绝抛出警告)。

遗憾的是,这个公认的解决方案并不适用于我的案例。我试图在从另一个视图控制器unwind后导航到一个新的视图控制器。

我找到了一个解决方案,使用一个标志来指示哪个unwind segue被调用。

@IBAction func unwindFromAuthenticationWithSegue(segue: UIStoryboardSegue) {
    self.shouldSegueToMainTabBar = true
}

@IBAction func unwindFromForgetPasswordWithSegue(segue: UIStoryboardSegue) {
    self.shouldSegueToLogin = true
}

然后用present(_ viewcontrollertoppresent: UIViewController)来呈现想要的VC

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    if self.shouldSegueToMainTabBar {
        let mainTabBarController = storyboard.instantiateViewController(withIdentifier: "mainTabBarVC") as! MainTabBarController
        self.present(mainTabBarController, animated: true)
        self.shouldSegueToMainTabBar = false
    }
    if self.shouldSegueToLogin {
        let loginController = storyboard.instantiateViewController(withIdentifier: "loginVC") as! LogInViewController
        self.present(loginController, animated: true)
        self.shouldSegueToLogin = false
    }
}

基本上,上面的代码将让我从login/SignUp VC捕获unwind并导航到仪表板,或者从forget password VC捕获unwind操作并导航到登录页面。

我也有同样的问题。问题是,performSegueWithIdentifier是由通知触发的,只要我把通知放在主线程上,警告消息就消失了。