UINavigationController popToRootViewController,然后立即推送一个新视图

时间:2009-06-05 09:17:59

标签: iphone uinavigationcontroller

我有一个带有两个选项卡的tabBarController,其中第一个包含NavigatorController的实例。 navigatorController是使用自定义viewController“peersViewController”启动的,它列出了tableView上的所有网络对等体。选择对等体后,“FilesListViewController”(将c:\目录中的列表文件)的实例推送到navigationController堆栈中。

在这个filesListViewController中我有一个按钮让它导航到说文档目录。为此,我将接口连接到rootViewController中调用gotoDirectory:(NSString *)路径方法:

- (void)gotoDirectory:(NSString*)path {
     [[self navigationController] popToRootViewControllerAnimated:YES];
     NSArray *files = [self getFilesFromPeerAtPath:path];
     FilesListViewController *filesVC = [[FilesListViewController alloc] initWithFiles:files];
     [[self navigationController] pushViewController:filesVC animated:YES];
     [filesVC release];
}

然而,当我按下该按钮时,navigationController确实将我的视图弹出到根视图控制器,但是我实例化的FilesListViewController没有出现。从日志中,我知道确实调用了自定义initWithFiles方法,并且确实发生了网络内容以获取文件名。

其他一些事情很棘手。我尝试点击第二个标签,然后点击返回第一个标签,然后点击!我需要的文件名就在那里。看起来数据和filesListViewController确实被推入了navigatorController堆栈,但显示没有刷新,而是停留在rootViewController(peersViewController)的屏幕上。

我做错了吗?

- 奔

- 发布问题后15分钟编辑。我找到了一个解决方法,但是让我困扰的是弹出然后推不起作用。

- (void)gotoDirectory:(NSString*)path {
     PeersListViewController *rootViewController = (PeersListViewController*)[[[self navigationController] viewControllers] objectAtIndex:0];
     [[self navigationController] setViewControllers:[NSArray arrayWithObject:rootViewController]];
     FilesListViewController *filesVC = [[FilesListViewController alloc] initWithFiles:files];
     [[self navigationController] pushViewController:filesVC animated:YES];
     [filesVC release];
}

看起来似乎不应该以这种方式规避navigationController,我可能不得不释放原始堆栈中的所有viewControllers。但这确实适用于iphone 3.0模拟器。

如果我正在使用此代码,应如何处理内存释放?我应该获得原始的NSArray视图控制器并发布所有内容吗?

6 个答案:

答案 0 :(得分:83)

这个问题和解决方案实际上非常简单。

致电[self.navigationController popToRootViewControllerAnimated:YES]self.navigationController设为nil。当您随后致电[self.navigationController pushViewController:someOtherViewController]时,您实际上正在向nil发送消息,该消息不执行任何操作。

要解决此问题,只需设置对navigationController的本地引用,然后使用它:

UINavigationController * navigationController = self.navigationController;
[navigationController popToRootViewControllerAnimated:NO];
[navigationController pushViewController:someOtherViewController animated:YES];

正如Jason所说,popToRootViewController必须在没有动画的情况下执行才能正常工作。

感谢jpimbert on the Apple forums指出这一点。

答案 1 :(得分:10)

我遇到了一个非常类似的问题(但没有使用标签)。

我得到了三个viewController : main(root),表格和结果。 当UINavigationController堆栈

"main -> result"

在btnClick上我做popToRootViewControllerAnimated然后按一下formViewCtrl。 为了拥有

"main -> form"

导航栏标题和后退按钮标签是正确的,并调用formViewCtrl的事件。 但是,我仍然看到主要观点。

这是我的“解决方案”

经过一些测试后,我发现没有动画的转到rootViwCtrl这个工作正常。所以我只使用动画来推动viewCtrl。

iPhone 3.0,在设备上发现问题&模拟器。

如果我有新的东西,我会更新/评论我的帖子。

答案 2 :(得分:4)

我看到这个关于弹出到根然后再推出一个新的ViewController的问题非常普遍,这个帖子被大量浏览了,所以我想添加一点来帮助其他新人,特别是那些使用Xcode 4的人和故事板。

在Xcode 4中,您有一个故事板。假设你有这些视图控制器:HomeViewController,FirstPageViewController,SecondPageViewController。确保单击其中每个标识并通过转到“实用工具”面板>“属性”检查器来命名其标识符。我们会说他们被命名为Home,First和Second。

你是家,然后你去第一,然后你想能够去第二,并能够按后退按钮回到家。为此,您需要在FirstPageViewController中更改代码。

要扩展示例,请在故事板中的FirstPageViewController中创建一个按钮。按住Ctrl键并将其拖动到FirstPageViewController.m中。在那里,以下代码将达到预期的结果:

    // Remember to add #import "SecondPageViewController.h" at the top
    SecondPageViewController *secondView = [self.storyboard instantiateViewContorllerWithIdentifier:@"Second"];
    UINavigationController *navigationController = self.navigationController;
    NSArray *array = [navigationController viewControllers];
    // [array objectAtIndex:0] is the root view controller
    NSArray *viewControllersStack = [NSArray arrayWithObjects:[array objectAtIndex:0], secondView, nil];
    [navigationController setViewControllers:viewControllersStack animated:YES];

基本上,您正在抓取视图控制器,按照您想要的顺序将它们排列在堆栈中,然后让导航控制器使用该堆栈进行导航。它是推动和弹出的替代方案。

答案 3 :(得分:1)

我找到了解决方法,但我无法解释它为什么有效: 1.首先推动所需的控制器。 2.然后弹出你想要的那个。

这完全不合逻辑,但它适合我的情况。 为了清楚起见,我在以下场景中使用它: 第一个屏幕 - >转到加载屏幕 - >第二个屏幕 当我在第二个屏幕上时,我不想在堆栈中放置加载屏幕,当点击返回时我应该转到第一个屏幕。

此致 Vesko Kolev

答案 4 :(得分:1)

如果您想要popToRootViewController并随后推送另一个VC,那么Nick Street的答案非常有用。

VC1 - > VC2 - > VC3:从VC3 =>点击后退按钮VC2,然后是VC1,这里确定

然而,当VC1推送VC2时,后者又推送VC3,然后直接从VC3返回VC1并不能正常工作:

我已经在VC3的-(void)viewWillDisappear:(BOOL)animated

中实现了
-(void)viewWillDisappear:(BOOL)animated{

    ...
    [self.navigationController popToRootViewControllerAnimated:YES];
}

我也尝试在“后退按钮”中实现它,结果相同:当从VC3点击后退按钮返回到VC1时:它会中断。实际的VC是VC1,但导航栏仍然是VC2。玩其他组合,我在VC2上获得VC1的navBar。总乱。

Loda提到了关于计时的事情。我认为这是主要问题。我尝试过一些东西,所以也许我错过了一些东西,但这最终对我有用:

在VC3中:

-(void)viewWillDisappear:(BOOL)animated {

    [super viewWillDisappear:animated];
    // notify VC2
    [[NSNotificationCenter defaultCenter] postNotificationName:backFromV3 object:self];
}

在VC2中:

-(void)viewDidLoad {

    ...

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(backFromV3)
                                                 name:@"BackFromV3"
                                               object:nil];
}

-(void)backFromV3{
    [NSTimer scheduledTimerWithTimeInterval:0.5 
                                 target:self
                               selector:@selector(backToRootViewController)
                               userInfo:nil
                                repeats:NO];
}

-(void)backToVC1 {
    self.navigationItem.rightBarButtonItem = nil;
    [self.navigationController popToRootViewControllerAnimated:YES];
}

当然,做必要的清洁。

计时器在这里至关重要。如果为0,则会中断。 0.5似乎没问题。

这对我很有用。有点沉重,但我找不到任何有用的技巧。

答案 5 :(得分:1)

你可以实际保留“返回”动画,然后通过基本延迟推动画直到“前进”动画直到流行动画完成。这是一个例子:

(注意:我的appDelegate中有一个名为“transitionTo”的NSString变量,最初设置为@“”)...首先,将该变量设置为您可以在以后检测到的NSString。然后,弹出控制器,为您提供一个漂亮的屏幕转换回根:

appDelegate.transitionTo = @"Another";
[detailNavigationController popToRootViewControllerAnimated:YES];

然后在rootviewcontroller的类中,使用viewDidAppear方法:

-(void)viewDidAppear:(BOOL)animated
{
    AppDelegate *appDelegate =(AppDelegate*) [UIApplication sharedApplication].delegate;
    if([appDelegate.transitionTo isEqualToString:@"Another"])
    {
        [self transitionToAnotherView];
        appDelegate.transitionTo = @"";
    }
}

-(void)transitionToAnotherView
{
    // Create and push new view controller here
    AnotherViewController *controller = [[AnotherViewController alloc] init];

    UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Home" style:UIBarButtonItemStyleBordered target:nil action:nil];
    [self.navigationItem setBackBarButtonItem:backButton];

    [[self navigationController] pushViewController:controller animated:YES];
}

所以基本上,弹出到根...当转换在“viewDidAppear”结束时...然后按下一个视图。我碰巧保留了一个变量来告诉你你希望转换到哪个视图(@“”意味着在我希望保留在这个屏幕上的情况下不进行转换。)