TabBar第二个标签页UISearchController键盘不弹出问题求助
这个问题我之前做类似架构时也碰到过,核心原因是Tab切换时导航栏的UISearchController没有完全与当前Tab的控制器绑定,导致点击第二个Tab的搜索栏时,实际触发的是第一个Tab的UISearchController逻辑——而第一个Tab的ViewController视图已经不在Window层级里,自然会崩溃。
为什么会出现这个崩溃?
你在tabBarController(_:didSelect:)里配置导航栏内容时,可能只是简单替换了navigationItem.searchController,但之前第一个Tab的UISearchController的关联关系没完全清理。当你点击第二个Tab的搜索栏时,系统还是尝试在第一个Tab的ViewController上present搜索界面,而它的view已经被移出Window层级,就会抛出Attempt to present UISearchController on <FirstTabViewController> whose view is not in the window hierarchy错误。
另外,不要在searchBarShouldBeginEditing(_:)里手动设置searchController.isActive = true——用户点击搜索栏时,系统会自动激活搜索控制器,手动调用会触发重复逻辑,反而加剧了关联混乱的问题。
修复步骤与代码示例
1. 每个Tab控制器独立初始化自己的UISearchController
在每个Tab的ViewController里单独创建并配置搜索控制器,不要提前绑定到导航项(因为导航栏是共享的,统一由TabBarController管理):
class FirstTabViewController: UIViewController, UISearchResultsUpdating { // 每个Tab专属的搜索控制器 let searchController = UISearchController(searchResultsController: nil) override func viewDidLoad() { super.viewDidLoad() setupSearchController() } private func setupSearchController() { searchController.searchResultsUpdater = self searchController.obscuresBackgroundDuringPresentation = false searchController.searchBar.placeholder = "搜索第一个Tab内容" // 这里不要设置navigationItem.searchController } func updateSearchResults(for searchController: UISearchController) { // 你的搜索逻辑 } } // 第二个Tab控制器同理 class SecondTabViewController: UIViewController, UISearchResultsUpdating { let searchController = UISearchController(searchResultsController: nil) override func viewDidLoad() { super.viewDidLoad() setupSearchController() } private func setupSearchController() { searchController.searchResultsUpdater = self searchController.obscuresBackgroundDuringPresentation = false searchController.searchBar.placeholder = "搜索第二个Tab内容" } func updateSearchResults(for searchController: UISearchController) { // 你的搜索逻辑 } }
2. 切换Tab时彻底替换并清理导航栏的搜索控制器
在tabBarController(_:didSelect:)方法里,先清理之前的搜索控制器关联,再绑定当前Tab的搜索控制器:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { guard let navController = tabBarController.navigationController else { return } // 1. 先清理之前的搜索控制器,避免残留关联 if let oldSearchController = navController.topViewController?.navigationItem.searchController { oldSearchController.dismiss(animated: false) navController.topViewController?.navigationItem.searchController = nil } // 2. 绑定当前Tab的搜索控制器到共享导航栏 if let firstVC = viewController as? FirstTabViewController { navController.topViewController?.navigationItem.searchController = firstVC.searchController } else if let secondVC = viewController as? SecondTabViewController { navController.topViewController?.navigationItem.searchController = secondVC.searchController } // 可选:控制搜索栏是否随滚动隐藏,根据需求调整 navController.topViewController?.navigationItem.hidesSearchBarWhenScrolling = false }
3. 移除searchBarShouldBeginEditing(_:)里的手动激活代码
删除searchController.isActive = true这行代码,让系统自动处理搜索控制器的激活逻辑,避免冲突。
额外注意点
- 确保每个Tab的
UISearchController是独立实例,不要复用同一个对象; - 如果你的Tab控制器是UINavigationController的子类(嵌套导航),需要调整代码获取到真正的内容控制器;
- 测试时可以在切换Tab前后打印
navigationItem.searchController的内存地址,确认是否正确替换。
内容的提问来源于stack exchange,提问作者Catalin Pomirleanu

