Умовно запускайте в різних місцях розкадровки від 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.