Swift中3D Touch快捷操作:TabBarController非根视图时的响应问题
解决3D Touch快捷项无法响应动作的问题
你的问题核心很明确:你直接实例化了一个HomeVC对象,但这个对象并没有被添加到当前应用的视图控制器栈或窗口中,所以修改它的tabBarController?.selectedIndex完全不会影响到屏幕上显示的内容——因为这个新建的HomeVC根本没被展示出来,它的tabBarController属性甚至都是nil。
下面是具体的解决思路和代码示例:
核心思路
要操作当前屏幕上真正存在的视图控制器实例,而不是新建一个。你需要先获取应用的根视图控制器,再根据你的视图层级结构找到对应的UITabBarController或者HomeVC实例,然后再执行切换索引的操作。
代码实现
根据常见的几种视图层级结构,这里提供几种适配方案:
方案1:根控制器就是UITabBarController
如果你的应用根视图控制器直接是UITabBarController,可以直接获取它并修改选中索引:
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { var actionSuccess = false // 获取当前窗口的根视图控制器 guard let rootVC = application.keyWindow?.rootViewController else { completionHandler(actionSuccess) return } // 根控制器是UITabBarController,直接操作 if let tabBarController = rootVC as? UITabBarController { switch shortcutItem.type { case "com.appName.actionType1": tabBarController.selectedIndex = 1 actionSuccess = true case "com.appName.actionType2": tabBarController.selectedIndex = 2 actionSuccess = true default: break } } // 必须调用completionHandler告知系统动作执行结果 completionHandler(actionSuccess) }
方案2:根控制器是UINavigationController,内部包含UITabBarController
如果你的根控制器是导航控制器,而导航栈的第一个控制器是UITabBarController:
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { var actionSuccess = false guard let rootVC = application.keyWindow?.rootViewController else { completionHandler(actionSuccess) return } // 从导航控制器中取出内部的TabBarController if let navController = rootVC as? UINavigationController, let tabBarController = navController.viewControllers.first as? UITabBarController { switch shortcutItem.type { case "com.appName.actionType1": tabBarController.selectedIndex = 1 actionSuccess = true case "com.appName.actionType2": tabBarController.selectedIndex = 2 actionSuccess = true default: break } } completionHandler(actionSuccess) }
方案3:需要先跳转到HomeVC再切换标签
如果HomeVC不在当前视图栈中,需要先展示它再切换标签:
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { var actionSuccess = false guard let rootVC = application.keyWindow?.rootViewController else { completionHandler(actionSuccess) return } // 检查HomeVC是否已经在栈中 if let existingHomeVC = rootVC.findViewController(ofType: HomeVC.self), let tabBarController = existingHomeVC.tabBarController { // 已存在,直接切换标签 tabBarController.selectedIndex = shortcutItem.type == "com.appName.actionType1" ? 1 : 2 actionSuccess = true completionHandler(actionSuccess) return } // 不存在,先present或push HomeVC,再切换标签 let homeVC = HomeVC() let navController = UINavigationController(rootViewController: homeVC) rootVC.present(navController, animated: true) { if let tabBarController = homeVC.tabBarController { tabBarController.selectedIndex = shortcutItem.type == "com.appName.actionType1" ? 1 : 2 actionSuccess = true } completionHandler(actionSuccess) } } // 扩展:快速查找指定类型的视图控制器 extension UIViewController { func findViewController<T: UIViewController>(ofType type: T.Type) -> T? { if let selfAsT = self as? T { return selfAsT } for child in children { if let found = child.findViewController(ofType: type) { return found } } if let parent = parent { return parent.findViewController(ofType: type) } return nil } }
关键注意点
- 一定要调用
completionHandler:系统要求你必须在动作执行完成后调用这个闭包,告知系统动作是否成功执行。 - 操作真实存在的实例:永远不要直接新建一个控制器来修改它的属性,除非你打算把它添加到视图栈中展示。
内容的提问来源于stack exchange,提问作者user9052417
相关产品推荐
相关产品推荐

