Beginnen Sie bedingt an verschiedenen Stellen im Storyboard von AppDelegate

107

Ich habe ein Storyboard mit funktionierendem Login und Hauptansichts-Controller eingerichtet. Letzteres ist der View-Controller, zu dem der Benutzer navigiert, wenn die Anmeldung erfolgreich ist. Mein Ziel ist es, den Hauptansichts-Controller sofort anzuzeigen, wenn die Authentifizierung (im Schlüsselbund gespeichert) erfolgreich ist, und den Anmeldeansichts-Controller anzuzeigen, wenn die Authentifizierung fehlgeschlagen ist. Grundsätzlich möchte ich dies in meinem AppDelegate tun:

// 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
}

Ich kenne die Methode performSegueWithIdentifier: Diese Methode ist jedoch eine Instanzmethode von UIViewController und kann daher nicht in AppDelegate aufgerufen werden. Wie mache ich das mit meinem vorhandenen Storyboard?

BEARBEITEN:

Der anfängliche Ansichts-Controller des Storyboards ist jetzt ein Navigations-Controller, der mit nichts verbunden ist. Ich habe den Unterschied setRootViewController: verwendet, da MainIdentifier ein UITabBarController ist. Dann sehen meine Zeilen so aus:

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

Vorschläge / Verbesserungen sind willkommen!

mmvie
quelle

Antworten:

25

Ich UIMainStoryboardFilegehe davon aus, dass Ihr Storyboard als "Haupt-Storyboard" festgelegt ist (geben Sie Ihre Info.plist ein). In diesem Fall lädt UIKit das Storyboard und legt seinen anfänglichen Ansichts-Controller als Root-Ansichts-Controller Ihres Fensters fest, bevor es application:didFinishLaunchingWithOptions:an Ihr AppDelegate gesendet wird.

Ich gehe auch davon aus, dass der anfängliche Ansichts-Controller in Ihrem Storyboard der Navigations-Controller ist, auf den Sie Ihren Haupt- oder Anmeldeansichts-Controller übertragen möchten.

Sie können Ihr Fenster nach seinem Root-View-Controller fragen und die folgende performSegueWithIdentifier:sender:Nachricht senden :

NSString *segueId = success ? @"pushMain" : @"pushLogin";
[self.window.rootViewController performSegueWithIdentifier:segueId sender:self];
Rob Mayoff
quelle
1
Ich habe Ihre Codezeilen in meiner Anwendung implementiert: didFinishLaunchingWithOptions: method. Das Debuggen zeigt, dass der rootViewController tatsächlich der anfängliche Navigationscontroller ist, die Übergabe wird jedoch nicht durchgeführt (die Navigationsleiste wird angezeigt, der Rest ist schwarz). Ich muss sagen, dass der ursprüngliche Navigationscontroller keinen rootViewController mehr hat, sondern nur noch 2 Segmente (StartLoginSegue und StartMainSegue).
mmvie
3
Ja, ich arbeite auch nicht für mich. Warum hast du es als beantwortet markiert, wenn es bei dir nicht funktioniert?
Daidai
3
Ich glaube das ist die richtige Antwort. Sie müssen 1. eine Fenstereigenschaft für Ihren App-Delegaten haben und 2. die [[self window] makeKeyAndVisible]Anwendung aufrufen: didFinishLaunchingWithOptions: Bevor Sie versuchen, die bedingten Segmente auszuführen. UIApplicationMain () soll makeKeyAndVisible melden, dies jedoch erst nach didFinish ... Optionen: Beendet. Weitere Informationen finden Sie in den Apple-Dokumenten unter "Koordinierungsbemühungen zwischen Ansichtscontrollern".
edelaney05
Das ist die richtige Idee, funktioniert aber nicht ganz. Siehe meine Antwort für eine funktionierende Lösung.
Matthew Frederick
@MatthewFrederick Ihre Lösung funktioniert, wenn der ursprüngliche Controller ein Navigationscontroller ist, nicht jedoch, wenn es sich um einen Nur-Ansicht-Controller handelt. Die eigentliche Antwort besteht darin, das Fenster und den Root-View-Controller selbst zu erstellen. Dies wird von Apple im View Controller-Programmierhandbuch empfohlen. Siehe meine Antwort unten für Details.
Followben
170

Ich bin überrascht über einige der hier vorgeschlagenen Lösungen.

Es sind wirklich keine Dummy-Navigations-Controller in Ihrem Storyboard erforderlich, um Ansichten auszublenden und Segues auf viewDidAppear: oder anderen Hacks auszulösen.

Wenn Sie das Storyboard nicht in Ihrer Plist-Datei konfiguriert haben, müssen Sie sowohl das Fenster als auch den Root-View-Controller selbst erstellen :

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

Wenn das Storyboard in der App-Liste konfiguriert ist , werden das Fenster und der Root-View-Controller bereits von der Zeitanwendung eingerichtet: didFinishLaunching: wird aufgerufen, und makeKeyAndVisible wird im Fenster für Sie aufgerufen.

In diesem Fall ist es noch einfacher:

- (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;
}
followben
quelle
@AdamRabung Spot on - Ich habe gerade die Variablennamen des OP kopiert, aber meine Antwort wurde aus Gründen der Klarheit aktualisiert. Prost.
Followben
für den Storyboard-Fall: Wenn Sie UINavigationViewcontroller als Root-View-Controller verwenden, müssen Sie den nächsten View-Controller drücken.
Shirish Kumar
Dies ist für mich intuitiver, als einen Navigationscontroller mit komplexer Hierarchie zu durchlaufen. Ich liebe das
Elliot Yap
Hallo @followben, in meiner App habe ich meinen rootViewController in storyBoard, es ist ein tabBarController, und alle mit tabBar verknüpften VCs sind auch in VC entworfen. Jetzt habe ich einen Fall, in dem ich eine exemplarische Vorgehensweise für meine App zeigen möchte Wenn meine App zum ersten Mal gestartet wird, möchte ich Walkthrough-VC als Root-VC anstelle von tabBarcontroller erstellen. Wenn meine exemplarische Vorgehensweise abgeschlossen ist, möchte ich tabBarController als rootViewController erstellen. Wie es geht, verstehe ich nicht
Ranjit
1
Was ist, wenn die Anforderung an den Server asynchron ist?
Lior Burg
18

WENN der Einstiegspunkt Ihres Storyboards kein 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;
}

WENN der Einstiegspunkt Ihres Storyboards ein UINavigationControllerErsatz ist:

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

mit:

//IF YOU USE A NAVIGATION CONTROLLER AS THE ENTRY POINT IN YOUR STORYBOARD:
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
[navController pushViewController:presentedViewController animated:NO];
Razvan
quelle
1
Gut gearbeitet. Nur ein Kommentar, zeigt dies nicht "firstViewControllerIdentifier" erst, nachdem sie ursprünglich eingegeben wurden? Sollte es also nicht umgekehrt werden? appHasLaunchedOnce ? secondViewControllerIdentifier : firstViewControllerIdentifier;
Ammianus
@ammianus du bist richtig. Sie sollten umgekehrt und ich bearbeitet werden.
Razvan
9

Fügen Sie in der AppDelegate- application:didFinishLaunchingWithOptionsMethode vor der return YESZeile Folgendes hinzu:

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

Ersetzen Sie YourStartingViewControllerdurch den Namen Ihrer tatsächlichen First-View-Controller-Klasse (diejenige, die nicht unbedingt angezeigt werden soll) und YourSegueIdentifierdurch den tatsächlichen Namen des Segues zwischen diesem Start-Controller und dem Namen, mit dem Sie tatsächlich beginnen möchten (den nach dem Segue) ).

Schließen Sie diesen Code in eine ifBedingung ein , wenn Sie nicht immer möchten, dass dies geschieht.

Matthew Frederick
quelle
6

Da Sie bereits ein Storyboard verwenden, können Sie dem Benutzer MyViewController, einen benutzerdefinierten Controller, präsentieren ( die Antwort von followben wird etwas heruntergekocht ).

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

Die an instantiateViewControllerWithIdentifier übergebene Zeichenfolge bezieht sich auf die Storyboard-ID, die im Interface Builder festgelegt werden kann:

Geben Sie hier die Bildbeschreibung ein

Wickeln Sie dies einfach nach Bedarf in Logik ein.

Wenn Sie jedoch mit einem UINavigationController beginnen, erhalten Sie mit diesem Ansatz keine Navigationssteuerung.

Verwenden Sie diesen Ansatz, um vom Startpunkt eines Navigationscontrollers, der über den Interface Builder eingerichtet wurde, vorwärts zu springen:

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

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

    return YES;
}
Reiches Apodaca
quelle
4

Lassen Sie den zuerst angezeigten Anmeldebildschirm überprüfen, ob der Benutzer bereits angemeldet ist, und drücken Sie sofort den nächsten Bildschirm. Alles in ViewDidLoad.

Darren
quelle
2
Dies funktioniert zwar, aber mein Ziel ist es, das Startbild anzuzeigen, solange die App noch auf die Serverantwort wartet (unabhängig davon, ob die Anmeldung erfolgreich war oder nicht). Genau wie die Facebook-App ...
mmvie
2
Sie können Ihre erste Ansicht immer nur als UIImage verwenden, das dasselbe Bild wie Ihr Splash verwendet und im Hintergrund überprüft, ob Sie angemeldet sind, und die nächste Ansicht anzeigen.
Darren
3

Schnelle Implementierung derselben:

Wenn du benutzt UINavigationController als Einstiegspunkt im Storyboard verwenden

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) 
    }
Dashrath
quelle
1

Dies ist die Lösung, die unter iOS7 funktioniert hat. Um das anfängliche Laden zu beschleunigen und kein unnötiges Laden durchzuführen, habe ich einen vollständig leeren UIViewcontroller namens "DUMMY" in meiner Storyboard-Datei. Dann kann ich folgenden Code verwenden:

- (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;
}
Luc Bloom
quelle
0

Ich schlage vor, einen neuen MainViewController zu erstellen, der Root View Controller von Navigation Controller ist. Halten Sie dazu einfach die Steuerung gedrückt und ziehen Sie die Verbindung zwischen Navigation Controller und MainViewController. Wählen Sie an der Eingabeaufforderung "Beziehung - Root View Controller".

In MainViewController:

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

Denken Sie daran, Segues zwischen MainViewController mit Home- und Login-View-Controllern zu erstellen. Hoffe das hilft. :) :)

thanhbinh84
quelle
0

Nachdem ich viele verschiedene Methoden ausprobiert hatte, konnte ich dieses Problem folgendermaßen lösen:

-(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;
}
AddisDev
quelle
Wenn Sie nicht weit von Taylor entfernt sind, möchten Sie vielleicht etwas Einfacheres umgestalten. Siehe meine Antwort für Details :)
followben