AppDelegate, rootViewController и presentViewController

Я делаю учебник по интеграции Facebook, я хочу показать свой MainViewViewController, если у пользователя есть действительный токен для текущего состояния, иначе я хочу показать LoginViewController.

MainViewAppDelegate.м

- (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.

5 ответов


у меня была та же проблема. Основываясь на ответе этот вопрос, я добавил [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];
}

иногда представляя модальный контроллер вида из окна.rootViewController может выдавать такое же предупреждение и не иметь никакого эффекта. Пример такой иерархии контроллеров вида:

  1. [MYUITableViewController] (представлено модально MYUIViewController)
  2. [MYUIViewController] (rootViewController UINavigationController ниже)
  3. [UINavigationController] (root)

теперь зову

[[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 может быть заменен window на window с nil rootViewController (показывая UIAlertViews, UIActionSheets на iPhone и т. д.), В этом случае вы должны использовать свойство window UIView.

ответ Степана Генералова был правильным для меня в Swift 3!!!
Конечно с новым синтаксисом и т. д. поэтому я скопирую его сюда:

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, поэтому это идеально, потому что в the

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

метод, вы можете просто проверить, что url является строкой, а затем решить, если вы выполняете выше написанный код или, возможно, аналогичный с другим идентификатором для другого контроллера вида (withIdentifier)


В 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)

в случае, если вы не используете раскадровку. Сначала вам нужно создать свой ViewController. Перейти Файл - > Создать - > Файл... (или сочетание клавиш-command + N) enter image description here

после этого выберите класс Cocoa Touch. Нажмите кнопку Далее или введите enter image description here

чем введите имя для вашего viewController и нажмите кнопку Далее enter image description here

#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

}