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 может выдавать такое же предупреждение и не иметь никакого эффекта. Пример такой иерархии контроллеров вида:
- [MYUITableViewController] (представлено модально MYUIViewController)
- [MYUIViewController] (rootViewController UINavigationController ниже)
- [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)
после этого выберите класс Cocoa Touch. Нажмите кнопку Далее или введите
чем введите имя для вашего viewController и нажмите кнопку Далее
#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
}