Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to skip 2 views when using navigation controller

In Xcode 5.0.2 I've created an iPhone app (the complete app code is on GitHub, please run pod install once - for SDWebImage), which displays a list of social networks (Facebook, Google+, etc.), then - in another view - an UIWebView displaying OAuth2 web page and then a third view with user avatar and details (first name, last name, etc.):

Xcode screenshot

(Here the fullscreen screenshot of the storyboard shown above)

enter image description here

Before I display the user avatar and details in the last view, I save the user data to NSUserDefaults.

My question is: when the user starts the app the next time and there is user data in NSUserDefaults already - how can I skip the first 2 views and display (without any animation) the last view with those user details?

I've put the following code to the AppDelegate.m:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *key = [defaults objectForKey:@"key"];
    User *user = [User loadForKey:key];

    if (user) {
        UINavigationController *nc = (UINavigationController*)self.window.rootViewController;
        // XXX how to load the last view here? XXX
    }

    return YES;
}

and can see, that the user data is being loaded okay, but I don't understand how to jump to the last view (esp. because most of the UI is defined in the storyboard)?

UPDATE: I have tried following Tommy Alexander's suggestion (thanks!) with the following code in AppDelegate.m (and also assigned Details StoryboardID to the last view):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *key = [defaults objectForKey:@"key"];
    User *user = [User loadForKey:key];

    if (user) {
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UIViewController *uvc = [storyboard instantiateViewControllerWithIdentifier:@"Details"];
        NSLog(@"XXX uvc=%@ user=%@", uvc, user);
        [self.window.rootViewController presentViewController:uvc animated:YES completion:nil];
    }

    return YES;
}

and while the last view (the UserViewController with user avatar and details) seems to be instantiated okay, I get the following warning and the app doesn't jump to the last view:

MyAuth[3917:70b] XXX uvc=<UserViewController: 0x8acbac0> user=
VK
59751265
Alexander
Farber
1945522
http://cs319319.vk.me/v319319265/b7df/TnyKeffL_mU.jpg
0
MyAuth[3917:70b] Warning: Attempt to present <UserViewController: 0x8acbac0> on <UINavigationController: 0x8bb3a30> whose view is not in the window hierarchy!

UPDATE 2: When I move the above code to the viewDidLoad of the 1st view (the MasterViewController.m, where the user can select a social network), then I get another warning:

MyAuth[1358:70b] XXX uvc=<UserViewController: 0x8a97430> user=
FB
597287941
Alexander
Farber
Bochum, Germany
http://graph.facebook.com/597287941/picture?type=large
0
MyAuth[1358:70b] Unbalanced calls to begin/end appearance transitions for <UINavigationController: 0x8a893e0>.

UPDATE 3: Thanks for the valuable answers! I've ended up using Leonty Deriglazov suggestion:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *vc1 = [storyboard instantiateViewControllerWithIdentifier:@"Master"];
    //UIViewController *vc2 = [storyboard instantiateViewControllerWithIdentifier:@"Login"];
    UIViewController *vc3 = [storyboard instantiateViewControllerWithIdentifier:@"User"];

    User *user = [User load];
    NSArray *controllers = (user ? @[vc1, vc3] : @[vc1]);

    UINavigationController *nc = (UINavigationController *)self.window.rootViewController;
    [nc setViewControllers:controllers];

    return YES;
}
like image 412
Alexander Farber Avatar asked Feb 03 '14 21:02

Alexander Farber


People also ask

How do I switch between view controllers?

From the Library, add a button to the first View Controller and name the button, for example: Show Second View . Select the button, press and hold the Control key on the keyboard and drag from the button to the second View Controller.

Is pushing the same view controller instance more than once?

the error is Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Pushing the same view controller instance more than once is not supported. Answer: A: Answer: A: You have already pushed one of those three viewControllers somewhere else in your code previous to these lines.


1 Answers

In a nutshell, the most straightforward solution is to use -[UINavigationController setViewControllers:animated:].

You need to do the following to achieve what you want:

  1. assign storyboard IDs to all your controllers,
  2. instantiate all three view controller from the story board (this is lightweight as long as you don't do most of the initialisation work in controller's constructors)
  3. put them into an array in the same order you have them in storyboard,
  4. call -[UINavigationController setViewControllers:] with the array as the first argument.

That's it. The code in your -[AppDelegate application:didFinishLaunchingWithOptions:] might look like this (after checking your skipping condition of course):

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *vc1 = [storyboard instantiateViewControllerWithIdentifier:@"MyAuth"]; //if you assigned this ID is storyboard
UIViewController *vc2 = [storyboard instantiateViewControllerWithIdentifier:@"Login"];  //if you assigned this ID is storyboard
UIViewController *vc3 = [storyboard instantiateViewControllerWithIdentifier:@"Details"];
NSArray *controllers = @[vc1, vc2, vc3];
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
[navController setViewControllers:controllers];

If you paste just this to -[AppDelegate application:didFinishLaunchingWithOptions:], you'll see that it works immediately.

like image 187
Leon Deriglazov Avatar answered Oct 11 '22 11:10

Leon Deriglazov