主要思路是通過runtime攔截系統(tǒng)三個轉(zhuǎn)場
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通過關(guān)聯(lián)對象注入到UIApplication中
項目地址:https://github.com/jsonsnow/CLCurrentVC.git
另一種方法是通過遞歸當(dāng)前控制器層級結(jié)構(gòu)獲得頂層控制器,該方法存在一個問題,主要體現(xiàn)在dismiss轉(zhuǎn)場上,向一個控制器發(fā)送dismiss消息,當(dāng)前控制器并不會立馬從當(dāng)前層級移除,是一個異步過程,即使不給動畫一樣存在,dismiss后通過遞歸獲取頂層控制器會獲得的是一個將要被dismiss的,拿該控制器去推或present將獲得一個錯誤。
題外話,一個業(yè)務(wù)原則當(dāng)一個控制器銷毀的時候先調(diào)用其dismiss或pop再進行回調(diào),即,先結(jié)束該流程,在處理后續(xù)事情,防止上一個流程對后續(xù)流程有影響。而且通過運行時來獲取棧頂控制器也必須這樣做。