Условно начать в разных местах в раскадровке из AppDelegate

У меня есть раскадровка, настроенная с рабочим контроллером входа и основного вида, последний является контроллером представления, к которому пользователь перемещается, когда логин успешно завершен. Моя цель состоит в том, чтобы показать главный контроллер представления сразу, если аутентификация (сохраненная в цепочке ключей) прошла успешно и покажет контроллер входа в систему, если аутентификация завершилась неудачно. В принципе, я хочу сделать это в своем 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
}

Я знаю о методе executeSegueWithIdentifier: но этот метод является методом экземпляра UIViewController, поэтому он не может быть вызван из AppDelegate. Как мне это сделать, используя существующую раскадровку?

EDIT:

Контроллер начального представления 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;
}

Предложения/улучшения приветствуются!

Ответ 1

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

Я также предполагаю, что контроллер начального представления в вашем раскадровке является навигационным контроллером, на который вы хотите нажать главный контроллер входа или входа.

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

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

Ответ 2

Я удивлен некоторыми предлагаемыми здесь решениями.

На самом деле нет необходимости в фиктивных навигационных контроллерах в вашем раскадровке, скрывая взгляды и снимая segues на viewDidAppear: или любые другие хаки.

Если у вас нет раскадровки, настроенной в вашем файле plist, вы должны сами создать окно и контроллер корневого представления:

- (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;
}

Если раскадровка настроена на панели приложений, контроллер окна и корневого представления уже будет настроен с помощью приложения времени: вызывается функция "Вызов по умолчанию: вызывается", а makeceyAndVisible вызывается в окне для вас.

В этом случае это еще проще:

- (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;
}

Ответ 3

Если ваша точка входа в раскадровку не является 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 replace:

//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];

Ответ 4

В вашем методе AppDelegate application:didFinishLaunchingWithOptions перед строкой return YES добавьте:

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

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

Оберните этот код в if условный, если вы не всегда хотите, чтобы это произошло.

Ответ 5

Учитывая, что вы уже используете раскадровку, вы можете использовать ее, чтобы представить пользователю с помощью MyViewController, настраиваемого контроллера ( "Boiling down followben answer).

В 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, относится к идентификатору раскадровки, который может быть установлен в конструкторе интерфейса:

enter image description here

Просто оберните это логическим при необходимости.

Если вы начинаете с UINavigationController, этот подход не даст вам элементов управления навигацией.

Чтобы перепрыгнуть вперед с начальной точки навигационного контроллера, настроенного через построитель интерфейсов, используйте этот подход:

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

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

    return YES;
}

Ответ 6

Почему бы не отобразить первый экран входа в систему, проверьте, был ли пользователь уже зарегистрирован и сразу же нажмите следующий экран? Все в ViewDidLoad.

Ответ 7

Быстрая реализация:

Если вы используете 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) 
    }

Ответ 8

Это решение, которое работает n iOS7. Чтобы ускорить первоначальную загрузку и не выполнять ненужную загрузку, у меня есть полностью пустой UIViewcontroller под названием "DUMMY" в моем файле Storyboard. Затем я могу использовать следующий код:

- (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;
}

Ответ 9

Я предлагаю создать новый MainViewController, который является контроллером Root View контроллера навигации. Для этого просто держите управление, затем перетащите соединение между контроллером навигации и MainViewController, выберите "Связь - контроллер корневого представления" из приглашения.

В MainViewController:

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

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

Ответ 10

Попробовав много разных методов, я смог решить эту проблему следующим образом:

-(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;
}