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

多层导航层级下SongsTableViewController无法正常显示UISearchController

解决导航栈多层视图中UISearchController激活失败的问题

这个问题我之前也碰到过,本质是导航栈中视图控制器的搜索控制器状态冲突,加上系统对搜索控制器展示上下文的判断逻辑导致的。咱们一步步拆解原因和解决方案:

核心问题分析

你遇到的报错"试图在视图不在窗口层级的AlbumsCollectionViewController上展示UISearchController",根源在于:

  1. 导航栈中上层的AlbumsCollectionViewController的搜索控制器可能还残留激活状态或关联关系,导致系统误将SongsTableViewController的搜索操作关联到它;
  2. 默认的UISearchController属性obscuresBackgroundDuringPresentation是true,激活时会尝试隐藏导航栈中所有下层视图,这会让系统混淆当前应该展示搜索控制器的上下文;
  3. 直接压入SongsVC时没问题,是因为此时导航栈中没有其他带搜索控制器的VC,系统能正确识别上下文。

分步解决方案

1. 给SongsTableViewController配置独立且正确的搜索控制器

确保每个VC的搜索控制器是独立实例,并且设置关键属性:

import UIKit

class SongsTableViewController: UITableViewController, UISearchResultsUpdating, UISearchControllerDelegate {
    
    // 独立初始化搜索控制器
    private lazy var searchController: UISearchController = {
        let searchVC = UISearchController(searchResultsController: nil)
        searchVC.searchResultsUpdater = self
        // 必须设为false!避免干扰导航栈中其他VC的视图层级
        searchVC.obscuresBackgroundDuringPresentation = false
        searchVC.searchBar.placeholder = "搜索歌曲"
        // 设置代理以便处理取消事件
        searchVC.delegate = self
        return searchVC
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 初始设置普通标题
        title = "歌曲列表"
        // 添加搜索按钮到导航栏右侧
        navigationItem.rightBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .search,
            target: self,
            action: #selector(showSearchBar)
        )
    }
    
    // 点击按钮展示并激活搜索栏
    @objc private func showSearchBar() {
        // 先确保当前VC是导航栈的顶部,避免系统找错上下文
        guard navigationController?.topViewController === self else {
            return
        }
        // 将titleView替换为搜索栏
        navigationItem.titleView = searchController.searchBar
        // 激活搜索控制器
        searchController.isActive = true
        // 让搜索栏自动获取焦点
        searchController.searchBar.becomeFirstResponder()
    }
    
    // 实现搜索结果更新代理
    func updateSearchResults(for searchController: UISearchController) {
        // 这里写你的搜索逻辑,比如过滤歌曲列表
        guard let searchText = searchController.searchBar.text else { return }
        // ...
    }
    
    // 处理搜索控制器取消事件,恢复原标题
    func didDismissSearchController(_ searchController: UISearchController) {
        navigationItem.titleView = nil
        title = "歌曲列表"
    }
}

2. 清理AlbumsCollectionViewController的搜索控制器残留状态

在AlbumsVC被push到SongsVC之前,确保它的搜索控制器已经取消激活并恢复视图状态,避免干扰后续页面:

class AlbumsCollectionViewController: UICollectionViewController {
    // ... 你的其他代码
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // 离开页面时如果搜索控制器还激活,就取消并恢复视图
        if searchController.isActive {
            searchController.isActive = false
            navigationItem.titleView = nil
            title = "专辑列表"
        }
    }
}

关键注意点

  • 永远不要在多个VC之间共享同一个UISearchController实例,每个VC都要单独初始化;
  • obscuresBackgroundDuringPresentation = false是多层导航栈中使用搜索控制器的核心配置,一定要设置;
  • 激活搜索控制器前,先校验当前VC是导航栈的顶部,确保系统使用正确的上下文。

内容的提问来源于stack exchange,提问作者MrGreen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:27:36