iOS获取当前显示控制器

主要思路是通过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 *)vc_popToRootViewControllerAnimated:(BOOL)animated {
    [UIApplication sharedApplication].currentVC = self.viewControllers[0];
    return [self vc_popToRootViewControllerAnimated:animated];
}

- (NSArray *)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再进行回调,即,先结束该流程,在处理后续事情,防止上一个流程对后续流程有影响。而且通过运行时来获取栈顶控制器也必须这样做。

你可能感兴趣的:(iOS获取当前显示控制器)