iOS Objective-C获取最上层可见视图控制器异常排查
解决获取最上层可见视图控制器的问题
我看了你的代码,问题出在没有处理传入的rootViewController本身就是导航控制器或标签栏控制器的情况——当这类容器控制器没有弹出其他控制器时,你的代码直接返回了控制器本身,而不是它内部当前显示的子控制器。
举个例子:如果传入的root是一个UINavigationController,且它没有presented任何控制器,你的第一个判断presentedViewController == nil就会直接返回这个导航控制器,但实际上真正可见的是它的topViewController。
下面是修正后的完整代码,覆盖了所有常见场景:
+ (UIViewController *)visibleViewController:(UIViewController *)rootViewController { // 先处理当前控制器是导航控制器的情况 if ([rootViewController isKindOfClass:[UINavigationController class]]) { UINavigationController *navController = (UINavigationController *)rootViewController; return [self visibleViewController:navController.topViewController]; } // 处理当前控制器是标签栏控制器的情况 if ([rootViewController isKindOfClass:[UITabBarController class]]) { UITabBarController *tabController = (UITabBarController *)rootViewController; return [self visibleViewController:tabController.selectedViewController]; } // 处理有弹出控制器的情况 if (rootViewController.presentedViewController) { return [self visibleViewController:rootViewController.presentedViewController]; } // 没有子控制器或弹出控制器,返回自身 return rootViewController; }
关键改进点:
- 优先判断当前控制器是否是导航/标签栏控制器,递归获取它们内部的可见子控制器
- 调整逻辑顺序:先处理容器控制器本身,再处理弹出的控制器
- 用
topViewController替代lastObject(两者效果一致,但topViewController是UINavigationController专门提供的API,语义更清晰)
测试场景覆盖:
- 普通ViewController直接显示
- UINavigationController栈中的任意页面
- UITabBarController切换标签后的页面
- 从任意控制器present出来的页面(包括嵌套present的情况)
- 嵌套的容器控制器(比如导航控制器在标签栏里,再弹出新控制器)
你可以直接替换这段代码测试,应该就能正确返回当前屏幕最上层的可见视图控制器了。
内容的提问来源于stack exchange,提问作者Shahbaz Akram




