使用导航控制器时如何跳过2个视图

时间:2014-02-03 21:06:44

标签: ios iphone objective-c uinavigationcontroller

在Xcode 5.0.2中,我创建了一个iPhone应用程序(完整的应用程序code is on GitHub,请运行pod install一次 - SDWebImage),其中显示社交网络列表( Facebook,Google +等),然后 - 在另一个视图中 - UIWebView显示OAuth2网页,然后是第三个包含用户头像和详细信息(名字,姓氏等)的视图:

Xcode screenshot

(此处显示the fullscreen screenshot of the storyboard

enter image description here

在我在上一个视图中显示用户头像和详细信息之前,我将用户数据保存到NSUserDefaults

我的问题是:当用户下次启动应用时,NSUserDefaults中已存在用户数据 - 如何跳过前2个视图并显示(没有任何动画) )具有这些用户详细信息的最后一个视图?

我已将以下代码添加到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;
}

并且可以看到,用户数据正在加载好,但我不明白如何跳转到最后一个视图(特别是因为大多数UI都是在故事板中定义的)?

更新:我已尝试使用AppDelegate.m中的以下代码(并在最后一个视图中分配Details StoryboardID)来关注Tommy Alexander的建议(谢谢!):< / p>

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

虽然最后一个视图(带有用户头像和详细信息的UserViewController)似乎可以实例化,但是我收到以下警告并且应用程序没有跳到上一个视图:

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!

更新2 :当我将上述代码移至第一个视图的viewDidLoadMasterViewController.m,用户可以选择社交网络时),然后我得到另一个警告:

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>.

更新3:感谢您的宝贵答案!我最终使用了Leonty Deriglazov的建议:

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

10 个答案:

答案 0 :(得分:14)

简而言之,最直接的解决方案是使用-[UINavigationController setViewControllers:animated:]

您需要执行以下操作才能达到您想要的效果:

  1. 将故事板ID分配给所有控制器,
  2. 从故事板中实例化所有三个视图控制器(只要你不在控制器的构造函数中完成大部分初始化工作,这是轻量级的)
  3. 按照故事板中的顺序将它们放入数组中,
  4. 以数组作为第一个参数调用-[UINavigationController setViewControllers:]
  5. 就是这样。 -[AppDelegate application:didFinishLaunchingWithOptions:]中的代码可能如下所示(当然,在检查跳过条件后):

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

    如果您只将其粘贴到-[AppDelegate application:didFinishLaunchingWithOptions:],您会发现它立即生效。

答案 1 :(得分:4)

打破视图层次结构总会产生奇怪的行为。

通过使用下面提到的方法,用户将无法注意到LoginViewController已呈现,然后我们已推送到详细信息屏幕。

这种方法对我来说一直很有用

在LoginViewController.m文件中

- (void)viewDidLoad
{

if (user) {
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UserViewController *userViewController = [storyboard instantiateViewControllerWithIdentifier:@"Details"];


[self.navigationController pushViewController:listViewVC animated:YES];

    }
}

在UserViewController.m文件中

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:YES];

if (user) {

    self.navigationItem.hidesBackButton = YES;

}

}

答案 2 :(得分:3)

我认为您的问题始于您最初的Storyboard设置。我会使用类似下图的内容。

我会创建User View controller我的根视图控制器,并在有或没有动画的情况下以模态方式显示登录视图控制器。

一旦登录成功,解除视图控制器将非常方便。如果用户稍后注销,您只需要再次显示登录视图控制器。

enter image description here

答案 3 :(得分:3)

我认为你可以这样做:

if (hasUser) {
    MyFirstViewController *firstViewController = [myStoryboard instantiateViewControllerWithIdentifier:@"FirstViewControllerStoryboardId"];
    SecondViewController *secondViewController = [myStoryboard instantiateViewControllerWithIdentifier:@"SecondViewControllerStoryboardId"];

    [myNavigationController pushViewController:firstViewController animated:NO];
    [myNavigationController pushViewController:secondViewController animated:YES];
}

这样您就不会有任何动画,并且会遵守导航堆栈。 更改“动画”布尔值以播放动画。

希望它有所帮助!

答案 4 :(得分:2)

根据您的屏幕工作流程,您有一个导航控制器,其他视图控制器被推入其中。

因此,逻辑很简单:如果用户被缓存将详细信息视图控制器设置为导航视图控制器的根,则不执行任何操作(将使用故事板的默认设置)。

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

    if (hasUser) {
        UIViewController *vc = [nc.storyboard instantiateViewControllerWithIdentifier:@"DetailsViewController"];
        nc.viewControllers = @[vc];
    }

    return YES;
}

更新

如果您需要返回上一个视图控制器(例如从详细信息到表格),您可以在导航控制器中设置整个导航层次结构:

UINavigationController *nc = (UINavigationController*)self.window.rootViewController;
if (hasUser) {
    UIViewController *vc = [nc.storyboard instantiateViewControllerWithIdentifier:@"DetailsViewController"];
    [nc pushViewController:vc animated:NO];
}

答案 5 :(得分:1)

尝试在IB的Attributes Inspector中为最后一个UIViewController提供一个StoryBoardID。然后,一旦确定您是否在NSUSerDefault中有用户:

- 获取对故事板的引用:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];

- 启用最后一个视图控制器的实例(记得导入上一个视图控制器.h文件):

UIViewController *myController = [storyboard instantiateViewControllerWithIdentifier:@"StoryBoardID"];

- 现在VC:

[self presentViewController:myController animated:YES completion:nil]; 

答案 6 :(得分:1)

使用此:

[self.presentingViewController dismissViewControllerAnimated:NO completion:^{
    [self.presentingViewController dismissViewControllerAnimated:NO completion:nil];
}];

最好的方法是使用UnwindSegue ..

What are Unwind segues for and how do you use them?

如果评论不清晰则更新

我不知道..

也许试试这个

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *uvc = [storyboard instantiateViewControllerWithIdentifier:@"Details"];
[self.window setRootViewController:uvc];

如果没有,那么将UINavigationController设为rootViewController并将ivc作为其rootViewController ..

希望有所帮助

答案 7 :(得分:1)

我在其中一个应用中执行的操作是,我从rootViewController到我要跳过的屏幕(在您的示例中,从MyAuth到详细信息)中有一个额外的segue。然后,当我显示UINavigationController时,我还在rootViewController中设置了一个变量,指定我是否要跳到另一个屏幕。最后,在视图控制器的viewDidLoad:中,我做了一个检查:

// Check if we want to skip to another screen
if (self.skipToDetails == YES) {
    [self performSegueWithIdentifier:@"detailsView" sender:nil];
}

如果在故事板文件中关闭此segue的动画,则用户根本看不到第一个屏幕。

此外,您需要在详细信息屏幕中将hidesBackButton设置为YES

修改当您从"unbalanced calls to begin/end"中的原始(rootViewController)VC转移到另一个VC(推送或模式)时,会发生viewDidLoad:。将代码移至viewDidAppear:应该会有所帮助。

答案 8 :(得分:1)

@Alexander请查看截图,你可以像这样管理控制器:

enter image description here

在用户视图控制器中使用以下代码:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *key = [defaults objectForKey:@"key"];
User *user = [User loadForKey:key];

if (!user) {
 MasterViewController *loginController = [self.storyboard instantiateViewControllerWithIdentifier:@"MasterViewController"];
[self presentViewController:masterController animated:YES completion:nil];
}

答案 9 :(得分:1)

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

  UINavigationController *navcon = (UINavigationController *)self.window.rootViewController;

    if (hasUser) {
        UIViewController *viewcon = [navcon.storyboard instantiateViewControllerWithIdentifier:@"DetailsViewController"];
        [navcon pushViewController:viewcon animated:NO]; }   
}
else
   {
       UIViewController *viewcon = [navcon.storyboard instantiateViewControllerWithIdentifier:@"UserViewController"];
       [navcon pushViewController:viewcon animated:NO];}
}