最近在维护一个旧项目的时候偶然间发现在一个页面滑动返回的时候会出现一个空白页面,并且页面的网络请求会重复3次请求。看了下页面的源码发现VC是通过UINavigationController addChildViewController方式实现的左右两个页面。记录下这块整体逻辑和问题。
NavigationController addChildViewController
项目中在切换左右两个tab页面的时候是通过更改childViewController来实现的。以下是源码:
[self.navigationController addChildViewController:rootVC];
[self.navigationController setViewControllers:self.navigationController.viewControllers];
- navigationController addChildViewController 相当于给viewControllers添加了新的VC。
- navigationController addChildViewController 与直接操作viewControllers的区别是 :
1.navigationController 通过add会自动调整顺序,添加重复对象也不会crash。viewControllers需要手动判重,添加重复对象会crash。
2.navigationController 通过addChildViewController会走VC的viewWillAppear方法两次。直接操作viewControllers不会触发viewWillAppear方法。
NSMutableArray *array = [self.navigationController.viewControllers mutableCopy];
if ([array containsObject:rootVC]) {
[array removeObject:rootVC];
}
[array addObject:rootVC];
[self.navigationController setViewControllers:array];
- 通过navigationController addChildViewController 的方式添加的VC,在遇到手势滑动返回的时候会出现下面的页面,当然back按钮可以自行处理。手势这块就不好处理了。
结论:通过直接操作viewControllers虽然解决了网络请求重发的问题,但是滑动返回还是有问题。要处理滑动返回的逻辑设计到的改动太多,并且左右两个Tab切换本来就不应该通过操作viewControllers来实现。
UIViewController addChildViewController
直接给当前VC添加子VC,在切换要显示的VC的时候调用示例代码:
if (![self.childViewControllers containsObject:rootVC]) {
[self addChildViewController:rootVC];
}
[rootVC didMoveToParentViewController:self];
[self.view addSubview:rootVC.view];
[rootVC.view mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.top.equalTo(self.view);
}];
if (self.currentVC) {
[self.currentVC willMoveToParentViewController:nil];
[self.currentVC.view removeFromSuperview];
[self.currentVC removeFromParentViewController];
self.currentVC = rootVC;
} else {
self.currentVC = rootVC;
}
通过UIViewController addChildViewController可以直接解决重复网络请求以及滑动返回问题。
当然如果需要转场动画可以通过transitionFromViewController方法去处理
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion API_AVAILABLE(ios(5.0));
如果遇到VC的代理方法不走,可以通过下面的方法去调用处理。
- (void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
- (void)endAppearanceTransition __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
这两个方法一般成对出现
//触发rootVC的viewWillAppear
[rootVC beginAppearanceTransition:YES animated:animated];
//触发viewDidAppear
[rootVC endAppearanceTransition];
//触发rootVC的viewWillDisappear
[rootVC beginAppearanceTransition:NO animated:animated];
//触发viewDidDisappear
[rootVC endAppearanceTransition];
可以通过以下方法手动调用子视图的方法。
//设置为NO,屏蔽对childViewController的生命周期函数的自动调用,改为手动控制
- (BOOL)shouldAutomaticallyForwardAppearanceMethods {
return NO;
}