24

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

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

- (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 分钟后编辑。我找到了一种解决方法,但让我感到困扰的是 pop 然后 push 不起作用。

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

似乎不应该以这种方式规避导航控制器,我可能不得不释放原始堆栈中的所有视图控制器。然而,这确实适用于 iphone 3.0 模拟器。

如果我使用这段代码,应该如何处理内存释放?我应该得到视图控制器的原始 NSArray 并释放所有内容吗?

4

6 回答 6

86

这个问题的问题和解决方案其实非常简单。

调用[self.navigationController popToRootViewControllerAnimated:YES]设置self.navigationControllernil。当您随后打电话时[self.navigationController pushViewController:someOtherViewController],您实际上是在向 发送一条消息nil,但它什么也不做。

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

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

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

感谢Apple 论坛上的 jpimbert指出这一点。

于 2010-12-11T02:18:28.723 回答
11

I got a very similar problem (but without using tab).

I got three viewController : main(root), form and result. when the UINavigationController stack is

"main -> result"

on a btnClick I do a popToRootViewControllerAnimated then a push of the formViewCtrl. in order to have

"main -> form"

the navbar title and back button label are correct and the formViewCtrl's event are called. BUT, I still see the main view.

Here is my "solution"

After doing some test, I found out that without the animation to go to the rootViwCtrl this work fine. So I only use the animation to push viewCtrl.

iPhone 3.0, problem found on device & simulator.

If i got something new, i will update/comment my post.

于 2009-09-17T12:55:40.620 回答
4

我看到这个关于弹出到根目录然后推送新 ViewController 的问题非常普遍,并且这篇文章被浏览了很多,所以我想添加我的一点来帮助其他新人,尤其是那些使用 Xcode 4 和故事板的人.

在 Xcode 4 中,您有一个故事板。假设您有这些视图控制器:HomeViewController、FirstPageViewController、SecondPageViewController。确保单击它们中的每一个并通过转到 Utilities 窗格-> Attributes Inspector 来命名它们的标识符。我们会说它们被命名为 Home、First 和 Second。

你是 Home,然后你去 First,然后你希望能够去 Second,并且能够按返回按钮回到 Home。为此,您需要更改 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];

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

于 2012-05-10T05:47:57.713 回答
1

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

VC1 -> VC2 -> VC3:从 VC3 => VC2 点击返回按钮,然后是 VC1,这里 OK

但是,当 VC1 推送 VC2 时,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 好像没问题。

这对我来说非常有效。有点重,但我找不到任何可以解决问题的方法。

于 2011-10-10T13:58:36.810 回答
1

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

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

问候, 韦斯科·科列夫

于 2009-10-29T13:45:23.687 回答
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”完成时...然后推送下一个视图。我碰巧保留了一个变量来告诉您希望转换到哪个视图(@"" 表示在我想留在此屏幕上的情况下不进行转换)。

于 2012-03-22T19:16:19.750 回答