我有一个带有两个选项卡的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视图控制器并发布所有内容吗?
答案 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)
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”结束时...然后按下一个视图。我碰巧保留了一个变量来告诉你你希望转换到哪个视图(@“”意味着在我希望保留在这个屏幕上的情况下不进行转换。)