主要思路是通过runtime拦截系统三个转场
1.拦截tab的setSelectedViewController
@implementation UITabBarController (VCChangeMethod)
+ (void)load {
[self exchangeInstanceMethod1:@selector(setSelectedViewController:) method2:@selector(vc_setSelectedViewController:)];
}
- (void)vc_setSelectedViewController:(__kindof UIViewController *)selectedViewController {
[UIApplication sharedApplication].currentVC = selectedViewController;
[self vc_setSelectedViewController:selectedViewController];
}
@end
2.拦截ctr的present和dismiss
@implementation UIViewController (VCChangeMethod)
+ (void)load {
[self exchangeInstanceMethod1:@selector(presentViewController:animated:completion:) method2:@selector(vc_presentViewController:animated:completion:)];
[self exchangeInstanceMethod1:@selector(dismissViewControllerAnimated:completion:) method2:@selector(vc_dismissViewControllerAnimated:completion:)];
}
- (void)vc_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
[UIApplication sharedApplication].currentVC = viewControllerToPresent;
[self vc_presentViewController:viewControllerToPresent animated:flag completion:completion];
}
- (void)vc_dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
[UIApplication sharedApplication].currentVC = self.presentingViewController;
[self vc_dismissViewControllerAnimated:flag completion:completion];
}
@end
3.拦截navc的push和pop
@implementation UINavigationController (VCChangeMethod)
+ (void)load {
[self exchangeInstanceMethod1:@selector(pushViewController:animated:) method2:@selector(vc_pushViewController:animated:)];
[self exchangeInstanceMethod1:@selector(popViewControllerAnimated:) method2:@selector(vc_popViewControllerAnimated:)];
[self exchangeInstanceMethod1:@selector(popToRootViewControllerAnimated:) method2:@selector(vc_popToRootViewControllerAnimated:)];
[self exchangeInstanceMethod1:@selector(popToViewController:animated:) method2:@selector(vc_popToViewController:animated:)];
}
- (void)vc_pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
[UIApplication sharedApplication].currentVC = viewController;
[self vc_pushViewController:viewController animated:animated];
}
- (UIViewController *)vc_popViewControllerAnimated:(BOOL)animated {
if (self.viewControllers.count >= 2) {
[UIApplication sharedApplication].currentVC = self.viewControllers[self.viewControllers.count - 2];
} else
[UIApplication sharedApplication].currentVC = self.viewControllers[0];
return [self vc_popViewControllerAnimated:animated];
}
- (NSArray<UIViewController *> *)vc_popToRootViewControllerAnimated:(BOOL)animated {
[UIApplication sharedApplication].currentVC = self.viewControllers[0];
return [self vc_popToRootViewControllerAnimated:animated];
}
- (NSArray<UIViewController *> *)vc_popToViewController:(UIViewController *)viewController animated:(BOOL)animated {
[UIApplication sharedApplication].currentVC = viewController;
return [self vc_popToViewController:viewController animated:animated];
}
@end
currentVC通过关联对象注入到UIApplication中
项目地址:https://github.com/jsonsnow/CLCurrentVC.git
另一种方法是通过递归当前控制器层级结构获得顶层控制器,该方法存在一个问题,主要体现在dismiss转场上,向一个控制器发送dismiss消息,当前控制器并不会立马从当前层级移除,是一个异步过程,即使不给动画一样存在,dismiss后通过递归获取顶层控制器会获得的是一个将要被dismiss的,拿该控制器去推或present将获得一个错误。
题外话,一个业务原则当一个控制器销毁的时候先调用其dismiss或pop再进行回调,即,先结束该流程,在处理后续事情,防止上一个流程对后续流程有影响。而且通过运行时来获取栈顶控制器也必须这样做。
网友评论