objective-c segue - Démarrer conditionnellement à différents endroits du storyboard depuis AppDelegate




ios unwind (9)

Mise en œuvre rapide de même:

Si vous utilisez UINavigationController comme point d'entrée dans le storyboard

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

J'ai un storyboard mis en place avec le contrôleur de connexion et de la vue principale de travail, ce dernier est le contrôleur de vue vers lequel l'utilisateur est navigué lorsque la connexion est réussie. Mon objectif est d'afficher le contrôleur de vue principal immédiatement si l'authentification (stockée dans le trousseau de clés) est réussie et d'afficher le contrôleur de vue de connexion si l'authentification a échoué. Fondamentalement, je veux le faire dans mon 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
}

Je connais la méthode performSegueWithIdentifier: mais cette méthode est une méthode d'instance de UIViewController, donc non appelable depuis AppDelegate. Comment est-ce que je fais cela en utilisant mon storyboard existant?

MODIFIER:

Le contrôleur de vue initial du Storyboard est maintenant un contrôleur de navigation qui n'est connecté à rien. J'ai utilisé le setRootViewController: distinction parce que MainIdentifier est un UITabBarController. Alors voici à quoi ressemblent mes lignes:

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

Suggestions / améliorations sont les bienvenues!


SI le point d'entrée de votre storyboard n'est pas un 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;
}

SI le point d'entrée de votre storyboard est un UINavigationController remplacez:

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

avec:

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

Pourquoi ne pas avoir l'écran de connexion qui apparaît en premier, vérifier si l'utilisateur est déjà connecté et appuyer directement sur l'écran suivant? Tout dans ViewDidLoad.


Après avoir essayé différentes méthodes, j'ai pu résoudre ce problème avec ceci:

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

Etant donné que vous utilisez déjà un Storyboard, vous pouvez l'utiliser pour présenter l'utilisateur avec MyViewController, un contrôleur personnalisé ( ébauche de la réponse de followben ).

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

La chaîne passée à instantiateViewControllerWithIdentifier fait référence à l'ID Storyboard, qui peut être défini dans le constructeur d'interface:

Juste envelopper dans la logique au besoin.

Si vous commencez avec un UINavigationController, cette approche ne vous donnera pas de contrôle de navigation.

Pour «sauter en avant» à partir du point de départ d'un contrôleur de navigation configuré via le constructeur d'interface, utilisez cette approche:

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

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

    return YES;
}

Dans l' application:didFinishLaunchingWithOptions , avant la ligne return YES , ajoutez:

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

Remplacez YourStartingViewController par le nom de votre classe de contrôleur de première vue (celle que vous ne voulez pas nécessairement afficher) et YourSegueIdentifier par le nom réel de la séquence entre ce contrôleur de départ et celui que vous voulez réellement démarrer (celui après la segue).

Envelopper ce code dans un if conditionnel si vous ne le voulez pas toujours.


Je suis surpris par certaines des solutions proposées ici.

Il n'y a vraiment pas besoin de contrôleurs de navigation factices dans votre storyboard, cachant des vues et des seguings sur viewDidAppear: ou tout autre hacks.

Si vous n'avez pas configuré le storyboard dans votre fichier plist, vous devez créer vous-même la fenêtre et le contrôleur de vue racine :

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

Si le storyboard est configuré dans le plist de l'application, le contrôleur de vue de la fenêtre et de la racine sera déjà configuré par l'application de temps: didFinishLaunching: est appelé, et makeKeyAndVisible sera appelé sur la fenêtre pour vous.

Dans ce cas, c'est encore plus simple:

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

C'est la solution qui a fonctionné n iOS7. Pour accélérer le chargement initial et ne pas faire de chargement inutile, j'ai un UIViewcontroller complètement vide appelé "DUMMY" dans mon fichier Storyboard. Ensuite, je peux utiliser le code suivant:

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

Le code de nall ci-dessus m'a orienté dans la bonne direction, mais je pense qu'il y a des erreurs dans le code affiché ci-dessus. Par exemple:

  1. Pourquoi les filesAndProperties sont- filesAndProperties alloués à l'aide de NMutableDictonary plutôt que de NSMutableArray ?

  2. 
    NSDictionary* properties = [[NSFileManager defaultManager]
                                            attributesOfItemAtPath:NSFileModificationDate
                                            error:&error];
    
    Le code ci-dessus transmet le mauvais paramètre pour attributesOfItemAtPath - il devrait s'agir d' attributesOfItemAtPath:path

  3. Vous filesAndProperties le tableau de files , mais vous devriez trier filesAndProperties .


J'ai mis en œuvre la même chose, avec des corrections, et en utilisant des blocs et posté ci-dessous:


    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* documentsPath = [searchPaths objectAtIndex: 0]; 

    NSError* error = nil;
    NSArray* filesArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsPath error:&error];
    if(error != nil) {
        NSLog(@"Error in reading files: %@", [error localizedDescription]);
        return;
    }

    // sort by creation date
    NSMutableArray* filesAndProperties = [NSMutableArray arrayWithCapacity:[filesArray count]];
    for(NSString* file in filesArray) {
        NSString* filePath = [iMgr.documentsPath stringByAppendingPathComponent:file];
        NSDictionary* properties = [[NSFileManager defaultManager]
                                    attributesOfItemAtPath:filePath
                                    error:&error];
        NSDate* modDate = [properties objectForKey:NSFileModificationDate];

        if(error == nil)
        {
            [filesAndProperties addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                           file, @"path",
                                           modDate, @"lastModDate",
                                           nil]];                 
        }
    }

        // sort using a block
        // order inverted as we want latest date first
    NSArray* sortedFiles = [filesAndProperties sortedArrayUsingComparator:
                            ^(id path1, id path2)
                            {                               
                                // compare 
                                NSComparisonResult comp = [[path1 objectForKey:@"lastModDate"] compare:
                                                           [path2 objectForKey:@"lastModDate"]];
                                // invert ordering
                                if (comp == NSOrderedDescending) {
                                    comp = NSOrderedAscending;
                                }
                                else if(comp == NSOrderedAscending){
                                    comp = NSOrderedDescending;
                                }
                                return comp;                                
                            }];




objective-c storyboard uiapplicationdelegate segue