41

我正在做 Facebook 集成教程,如果用户有当前状态的有效令牌,我想显示我的 MainViewViewController,否则我想显示 LoginViewController。

MainViewAppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
        // To-do, show logged in view
    } else {
        // No, display the login page.
        [self showLoginView];
    }
    return YES;
}
- (void)showLoginView
{
    UIStoryboard *mainstoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard"          bundle:nil];
    LoginViewController* loginViewController = [mainstoryboard      instantiateViewControllerWithIdentifier:@"LoginViewController"];
    [self.window.rootViewController presentViewController:loginViewController animated:YES completion:NULL];
}

控制台错误:

Warning: Attempt to present <LoginViewController: 0xb492fd0> on <MainViewViewController: 0xb1bd820> whose view is not in the window hierarchy!

我不想使用 NavigationController。

4

6 回答 6

137

我遇到过同样的问题。根据这个问题的答案,我[self.window makeKeyAndVisible]之前添加了presentViewController:animated:completion:,并且为我修复了它。

在您的情况下, showLoginView 变为

- (void)showLoginView
{
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    LoginViewController *loginViewController = [storyboard instantiateViewControllerWithIdentifier:@"LoginViewController"];
    [self.window makeKeyAndVisible];
    [self.window.rootViewController presentViewController:loginViewController animated:YES completion:NULL];
}
于 2012-12-03T16:50:02.743 回答
38

有时从 window.rootViewController 呈现模态视图控制器可能会产生相同的警告并且没有效果。这种视图控制器层次结构的示例:

  1. [ MYUITableViewController ](由 MYUIViewController 模态呈现)
  2. [ MYUIViewController ](下面 UINavigationController 的 rootViewController)
  3. [ UINavigationController ] (根)

现在打电话

[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:[UIViewController new] animated:YES completion:nil];

将导致这个确切的警告(在 iOS6 和 7 Sim 上测试)

解决方案:而不是使用 rootViewController - 使用它提供的顶部:

    UIViewController *topRootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (topRootViewController.presentedViewController) 
    {
        topRootViewController = topRootViewController.presentedViewController;
    }

    [topRootViewController presentViewController:yourController animated:YES completion:nil];
  • 有时 keyWindow 可能已被替换为 nil rootViewController 的窗口(在 iPhone 上显示 UIAlertViews、UIActionSheets 等),在这种情况下,您应该使用 UIView 的 window 属性。
于 2014-01-23T00:49:26.883 回答
4

在Swift 3中, Stepan Generalov 的回答对我来说是正确的!!!
当然使用新的语法等,所以我会在这里复制它:

let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "MainApp") as! ViewController

var topRootViewController: UIViewController = (UIApplication.shared.keyWindow?.rootViewController)!
while((topRootViewController.presentedViewController) != nil){
    topRootViewController = topRootViewController.presentedViewController!
}
topRootViewController.present(vc, animated: true, completion: nil)

在这种情况下,“MainApp”是我的主视图控制器的标识符。

我知道还有其他方法,但是如果您需要使用不同的 URL 方案来打开应用程序的不同部分,则必须在 AppDelegate 中处理它,所以这是完美的,因为在

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {}

方法,您可以检查什么url是字符串,然后决定是否执行上述编写的代码,或者可能是具有不同标识符的其他视图控制器的类似代码(withIdentifier

于 2017-03-03T00:07:32.920 回答
1

在 Swift 3 中:-

let storyboard = UIStoryboard(name: "Login", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
window?.makeKeyAndVisible()
window?.rootViewController?.present(viewController, animated: true, completion: nil)
于 2017-09-15T11:42:35.250 回答
0

以防万一,当您不使用情节提要时。首先,您需要创建 YourViewController。转到文件 -> 新建 -> 文件...(或快捷方式 - 命令 + N) 在此处输入图像描述

之后,选择 Cocoa Touch Class。单击下一步,或输入 在此处输入图像描述

然后为您的 viewController 输入名称,然后单击 Next 在此处输入图像描述

#import "AppDelegate.h"
#import "YourViewController.h"

@interface AppDelegate ()

@end

@implementaion AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Init window
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
[self.window makeKeyAndVisible];

// Init YourViewController
YourViewController *viewController = [[YourViewController alloc] init];

// Init YourNavigationController
UINavigationController *navigationContoller = [[UINavigationController alloc] initWithRootViewController: viewController];

// Set rootViewController
self.window.rootViewController = navigationContoller;

return YES;

}

@end

Swift 3/4 示例

import UIKit

@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

//1-st step
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()

//2-nd - create a window root controller, and create a layout
let layout = UICollectionViewFlowLayout()
window?.rootViewController = UINavigationController(rootViewController: HomeController(collectionViewLayout: layout))

return true

}

于 2018-04-05T08:42:50.913 回答
0

您可以在 Objective-C 中像这样从 root 启动一个新的 viewController

UIViewController *presenter = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;

[presenter presentViewController:yourViewController animated:YES completion:nil];

不要忘记添加这个:

#import "AppDelegate.h"
于 2019-12-23T16:23:43.993 回答