You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:09:32