Умовно запускайте в різних місцях розкадровки від AppDelegate


107

У мене створена розкадрована дошка з робочим контролером для входу та основного перегляду; останній - це контролер перегляду, до якого користувач переходить, коли вхід успішний. Моя мета - негайно показати основний контролер подання даних, якщо аутентифікація (збережена в брелоку) успішна, і показати контролер перегляду входу, якщо аутентифікація не вдалася. В основному, я хочу це зробити у своєму AppDelegate:

// url request & response work fine, assume success is a BOOL here
// that indicates whether login was successful or not

if (success) {
          // 'push' main view controller
} else {
          // 'push' login view controller
}

Я знаю про метод performSegueWithIdentifier: але цей метод є методом екземпляра UIViewController, тому його не можна викликати з AppDelegate. Як це зробити за допомогою моєї існуючої раскадровки ??

Редагувати:

Початковий контролер перегляду Storyboard зараз є контролером навігації, який не пов'язаний ні з чим. Я використовував setRootViewController: відмінність, оскільки MainIdentifier є UITabBarController. Тоді так виглядають мої рядки:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{        
    BOOL isLoggedIn = ...;    // got from server response

    NSString *segueId = isLoggedIn ? @"MainIdentifier" : @"LoginIdentifier";
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil];
    UIViewController *initViewController = [storyboard instantiateViewControllerWithIdentifier:segueId];

    if (isLoggedIn) {
        [self.window setRootViewController:initViewController];
    } else {
        [(UINavigationController *)self.window.rootViewController pushViewController:initViewController animated:NO];
    }

    return YES;
}

Пропозиції / покращення вітаються!

Відповіді:


25

Я припускаю, що ваша розкадровка встановлена ​​як "основна UIMainStoryboardFileдошка " (ключ у вашому Info.plist). У такому випадку UIKit завантажить дошку розкадрувань і встановить її початковий контролер перегляду як контролер кореневого виду вашого вікна, перш ніж надсилатиме його application:didFinishLaunchingWithOptions:на ваш AppDelegate.

Я також припускаю, що початковим контролером перегляду у вашій розкадровці є контролер навігації, на який ви хочете натиснути свій основний або контролер перегляду входу.

Ви можете запитати у свого вікна його контролер кореневого перегляду та надіслати performSegueWithIdentifier:sender:повідомлення на нього:

NSString *segueId = success ? @"pushMain" : @"pushLogin";
[self.window.rootViewController performSegueWithIdentifier:segueId sender:self];

1
Я реалізував ваші рядки коду у своїй програмі: метод didFinishLaunchingWithOptions:. Налагодження показує, що rootViewController справді є початковим контролером навігації, але segue не виконується (відображається навігаційна панель, відпочинок - чорний). Треба сказати, що початковий контролер навігації вже не має rootViewController, лише 2 сегменти (StartLoginSegue і StartMainSegue).
— mmvie

3
Так, не працює і для мене. Чому ви відзначили це відповіддю, якщо це не працює для вас?
— дайдай

3
Я вважаю, що це правильна відповідь. Вам потрібно 1. мати властивість вікна на делегаті програми та 2. зателефонувати [[self window] makeKeyAndVisible]в програму: didFinishLaunchingWithOptions: перед тим, як спробувати виконати умовні сеги. UIApplicationMain () припускає повідомлення makeKeyAndVisible, але робить це лише після didFinish ... Опції: закінчується. Докладніше шукайте "Координація зусиль між контролерами перегляду" в документах Apple.
— edelaney05

Це правильна ідея, але не зовсім працює. Дивіться мою відповідь на робоче рішення.
— Меттью Фредерік

@MatthewFrederick Ваше рішення працюватиме, якщо початковий контролер є контролером навігації, але не, якщо це звичайний контролер перегляду. Справжня відповідь полягає в тому, щоб самостійно створити контролер перегляду вікон та коренів - дійсно це рекомендує Apple у Посібнику з програмування View Controller. Детальну інформацію див. У моїй відповіді нижче.
— followben

170

Я здивований деяким із запропонованих тут рішень.

У вашій розгортці насправді немає необхідності використовувати фіктивні контролери навігації, приховувати погляди та стріляти погляди на viewDidAppear: чи будь-які інші хаки.

Якщо у вашому файлі плістів не налаштовано розкадровку, ви повинні створити як вікно, так і контролер кореневого перегляду самостійно :

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{        
    BOOL isLoggedIn = ...;    // from your server response

    NSString *storyboardId = isLoggedIn ? @"MainIdentifier" : @"LoginIdentifier";
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil];
    UIViewController *initViewController = [storyboard instantiateViewControllerWithIdentifier:storyboardId];

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = initViewController;
    [self.window makeKeyAndVisible];

    return YES;
}

Якщо розкадровка є налаштована в PLIST в додатку, вікно і кореневої контролер уявлення вже буде настройка додаток часу: didFinishLaunching: називаюся, і makeKeyAndVisible буде викликатися вікном для вас.

У цьому випадку це ще простіше:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{        
    BOOL isLoggedIn = ...;    // from your server response

    NSString *storyboardId = isLoggedIn ? @"MainIdentifier" : @"LoginIdentifier";
    self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:storyboardId];

    return YES;
}

@AdamRabung Spot - Я просто скопіював імена змінних OP, але я оновив свою відповідь для ясності. Ура.
— followben

для випадку розкадрівки: Якщо ви використовуєте UINavigationViewcontroller як контролер кореневого перегляду, вам потрібно буде натиснути наступний контролер перегляду.
— Ширіш Кумар

Це для мене більш інтуїтивно зрозумілий спосіб, ніж через складний ієрархічний навігаційний контролер. Любіть це
— Елліот Яп

Привіт @followben, у моєму додатку є мій rootViewController у storyBoard, його tabBarController та всі пов'язані з ним VC з tabBar також розроблені в VC, тому зараз у мене є випадок, коли я хочу показати детальну інформацію про свою програму, тож Тепер, коли мій додаток запущено, я хочу зробити прохідний VC як кореневий VC замість tabBarcontroller, і коли моє проходження буде закінчено, я хочу зробити tabBarController як rootViewController. Як це зробити, я не розумію
— Ранджіт

1
Що робити, якщо запит на сервер асинхронний?
— Lior Burg

18

ЯКЩО вхідною точкою вашої розкадровки не є UINavigationController:

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


    //Your View Controller Identifiers defined in Interface Builder
    NSString *firstViewControllerIdentifier  = @"LoginViewController";
    NSString *secondViewControllerIdentifier = @"MainMenuViewController";

    //check if the key exists and its value
    BOOL appHasLaunchedOnce = [[NSUserDefaults standardUserDefaults] boolForKey:@"appHasLaunchedOnce"];

    //if the key doesn't exist or its value is NO
    if (!appHasLaunchedOnce) {
        //set its value to YES
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"appHasLaunchedOnce"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }

    //check which view controller identifier should be used
    NSString *viewControllerIdentifier = appHasLaunchedOnce ? secondViewControllerIdentifier : firstViewControllerIdentifier;

    //IF THE STORYBOARD EXISTS IN YOUR INFO.PLIST FILE AND YOU USE A SINGLE STORYBOARD
    UIStoryboard *storyboard = self.window.rootViewController.storyboard;

    //IF THE STORYBOARD DOESN'T EXIST IN YOUR INFO.PLIST FILE OR IF YOU USE MULTIPLE STORYBOARDS
    //UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"YOUR_STORYBOARD_FILE_NAME" bundle:nil];

    //instantiate the view controller
    UIViewController *presentedViewController = [storyboard instantiateViewControllerWithIdentifier:viewControllerIdentifier];

    //IF YOU DON'T USE A NAVIGATION CONTROLLER:
    [self.window setRootViewController:presentedViewController];

    return YES;
}

ЯКЩО точка входу вашої розкадровки UINavigationControllerзамінить:

//IF YOU DON'T USE A NAVIGATION CONTROLLER:
[self.window setRootViewController:presentedViewController];

з:

//IF YOU USE A NAVIGATION CONTROLLER AS THE ENTRY POINT IN YOUR STORYBOARD:
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
[navController pushViewController:presentedViewController animated:NO];

1
Добре працювали. Лише коментар, чи не відображається "firstViewControllerIdentifier" лише після того, як вони були введені спочатку? Так чи не слід його відміняти? appHasLaunchedOnce ? secondViewControllerIdentifier : firstViewControllerIdentifier;
— ammianus

@ammianus ти маєш рацію. Їх слід змінити, і я відредагував.
— Разван

9

У методі вашого AppDelegate application:didFinishLaunchingWithOptionsперед return YESрядком додайте:

UINavigationController *navigationController = (UINavigationController*) self.window.rootViewController;
YourStartingViewController *yourStartingViewController = [[navigationController viewControllers] objectAtIndex:0];
[yourStartingViewController performSegueWithIdentifier:@"YourSegueIdentifier" sender:self];

Замініть YourStartingViewController ім'я фактичного першого класу контролерів перегляду (той, який ви не бажаєте обов'язково відображати) таYourSegueIdentifier також власне ім'я каналу між тим початковим контролером і тим, з якого ви хочете насправді запустити (тим, що починається після сеги ).

Загорніть цей код ifумовно, якщо ви не завжди хочете, щоб це сталося.


6

Зважаючи на те, що ви вже використовуєте дошку розкадрувань, ви можете використовувати це, щоб представити користувачеві MyViewController, призначений для користувача контролер ( зведення відповіді followben трохи ).

В AppDelegate.m :

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    MyCustomViewController *controller = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:@"MyCustomViewController"];

    // now configure the controller with a model, etc.

    self.window.rootViewController = controller;

    return YES;
}

Рядок, переданий instantiateViewControllerWithIdentifier, посилається на ідентифікатор Storyboard, який можна встановити в конструкторі інтерфейсів:

введіть тут опис зображення

Просто зафіксуйте це в логіці за потребою.

Якщо ви починаєте з UINavigationController, однак цей підхід не дає вам керувати навігацією.

Щоб "перейти вперед" з початкової точки керування навігаційним контролером, створеним за допомогою конструктора інтерфейсів, використовуйте такий підхід:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UINavigationController *navigation = (UINavigationController *) self.window.rootViewController;

    [navigation.visibleViewController performSegueWithIdentifier:@"my-named-segue" sender:nil];

    return YES;
}

4

Чому б не з’явився екран входу, який з’явиться першим, перевірте, чи користувач уже увійшов у систему, і натисніть на наступний екран відразу? Все в ViewDidLoad.


2
Це справді працює, але мої цілі - показати образ запуску, поки додаток ще чекає відповіді сервера (чи вдалий вхід був чи ні). Так само, як додаток Facebook ...
— mmvie

2
Ви завжди можете мати свій перший перегляд лише UIImage, який використовує те саме зображення, що і ваш сплеск, а у фоновому режимі перевірте, чи ввійшли ви та відображаєте наступне подання.
— Даррен

3

Швидка реалізація того ж:

Якщо ви використовуєте UINavigationControllerяк вхідну точку в раскадровці

let storyboard = UIStoryboard(name: "Main", bundle: nil)

var rootViewController = self.window!.rootViewController as! UINavigationController;

    if(loginCondition == true){

         let profileController = storyboard.instantiateViewControllerWithIdentifier("ProfileController") as? ProfileController  
         rootViewController.pushViewController(profileController!, animated: true) 
    }
    else {

         let loginController =   storyboard.instantiateViewControllerWithIdentifier("LoginController") as? LoginController 
         rootViewController.pushViewController(loginController!, animated: true) 
    }

1

Це рішення, яке працювало n iOS7. Щоб пришвидшити початкове завантаження і не зробити зайвого завантаження, у мене в файлі розгортки є повністю порожній контролер UIView під назвою "DUMMY". Тоді я можу використовувати наступний код:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];

    NSString* controllerId = @"Publications";
    if (![NSUserDefaults.standardUserDefaults boolForKey:@"hasSeenIntroduction"])
    {
        controllerId = @"Introduction";
    }
    else if (![NSUserDefaults.standardUserDefaults boolForKey:@"hasDonePersonalizationOrLogin"])
    {
        controllerId = @"PersonalizeIntro";
    }

    if ([AppDelegate isLuc])
    {
        controllerId = @"LoginStart";
    }

    if ([AppDelegate isBart] || [AppDelegate isBartiPhone4])
    {
        controllerId = @"Publications";
    }

    UIViewController* controller = [storyboard instantiateViewControllerWithIdentifier:controllerId];
    self.window.rootViewController = controller;

    return YES;
}

0

Я пропоную створити новий MainViewController, який є Root View Controller of Navigation Controller. Для цього достатньо утримувати контроль, а потім перетягнути з'єднання між навігаційним контролером та MainViewController, виберіть "Зв'язок - Контролер перегляду корінців" із запиту.

У MainViewController:

- (void)viewDidLoad
{
    [super viewDidLoad];
    if (isLoggedIn) {
        [self performSegueWithIdentifier:@"HomeSegue" sender:nil];
    } else {
        [self performSegueWithIdentifier:@"LoginSegue" sender:nil];
    }
}

Не забудьте створити відокремлення між MainViewController за допомогою контролерів подання Home та Login. Сподіваюся, це допомагає. :)


0

Спробувавши багато різних методів, я зміг вирішити цю проблему за допомогою цього:

-(void)viewWillAppear:(BOOL)animated {

    // Check if user is already logged in
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    if ([[prefs objectForKey:@"log"] intValue] == 1) {
        self.view.hidden = YES;
    }
}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];

    // Check if user is already logged in
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    if ([[prefs objectForKey:@"log"] intValue] == 1) {
        [self performSegueWithIdentifier:@"homeSeg3" sender:self];
    }
}

-(void)viewDidUnload {
    self.view.hidden = NO;
}

Якщо ви не надто далеко вздовж Тейлора, ви можете захотіти повернутися до чогось більш простого. Детальніше дивіться у моїй відповіді :)
— followben
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.