1.遇到一个需求
使用UINavigationController push进入多个页面后,不再显示部分页面,直接返回到指定的页面。
根据描述,第一个想到的方案就是:
1
[self.navigationController popToViewController:toVC animated:YES];
通过遍历UINavigationController的viewControllers找到要跳转的页面,直接pop过去。
遇到的问题就是,iOS 触发返回页面有两种交互方式:
- 点击左上角的返回按钮
- 使用手势,在屏幕边缘从左往右滑动
要想使用pop的API,就需要拦截边缘滑动的交互事件,重写UINavigationController的手势,(有的朋友说可以屏蔽右滑手势…)
由于不想改动 UINavigationController的手势事件,遂有了下一个方案。
2.UINavigationController实现原理
在iOS开发过程中,UINavigationController 是常用的容器类Controller,UINavigationController 交互方式如下图,

根据官方文档说明,UINavigationController通过管理一个用数组实现的栈结构来管理childControllers 。 将UIViewController依次压入栈中,记录了页面的先后顺序,同时栈持有子视图,实现了内存的管理。

3.解决方案
于是想到,通过修改UINavigationController栈内元素来控制页面的跳转。
1
2
3
4
5
6
NSMutableArray *vcStack = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
if (vcStack.count > 2 && vcStack.count > index) {
//将 元素直接从 数组中 移除
[vcStack removeObjectAtIndex:index];
self.navigationController.viewControllers = vcStack;
}
这样,在进入新的页面时,将不再显示的页面从栈中移除掉,便可实现跨页面的跳转。
需要注意的是:
- 当把一个
UIViewController的实例push进栈后,该实例的parentViewController就是UINavigationController - 调用
UIViewController的- (void)removeFromParentViewController方法,会将该实例从栈中移除,但会发生异常,具体表现为点击返回,navigationBar有动画,但不会有页面跳转 - 注意内存的引用,如果要移除的
UIViewController在其他地方存在引用,将不会释放内存,发生内存泄露。
附:UI结构图

参考资料:
UINavigationController