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

如何检测UINavigationItem中SearchController的呈现时机?现有方法失效求助

解决NavigationController与SearchController的呈现检测及动画问题

嘿,我刚好碰到过类似的NavigationController和SearchController的坑,来给你一步步捋清楚怎么解决这两个问题:

一、如何检测SearchController何时被呈现

你试过的isBeingPresented和isActive没起作用,是因为UISearchController并不是以常规ViewController的方式被present的——它是嵌入到navigationItem的搜索栏里,所以这两个属性没法正确反映它的呈现状态。给你几个靠谱的方案:

1. 用UISearchBar代理方法

让你的ViewController遵守UISearchBarDelegate,通过以下两个代理方法就能精准捕捉搜索栏的激活/关闭状态:

class YourViewController: UIViewController, UISearchBarDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        let searchController = UISearchController(searchResultsController: nil)
        searchController.searchBar.delegate = self
        navigationItem.searchController = searchController
    }

    // 搜索栏开始编辑(SearchController呈现)
    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
        print("SearchController 已呈现")
        // 这里写你需要触发的逻辑
    }

    // 搜索栏结束编辑(SearchController关闭)
    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
        print("SearchController 已关闭")
        // 这里写你需要触发的逻辑
    }
}

2. 监听isActive属性的变化(KVO/Combine)

如果你更倾向于监听属性变化,可以用KVO(兼容iOS全版本)或者Combine(iOS13+):

KVO方式:

class YourViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let searchController = UISearchController(searchResultsController: nil)
        navigationItem.searchController = searchController
        // 添加监听
        searchController.addObserver(self, forKeyPath: #keyPath(UISearchController.isActive), options: [.initial, .new], context: nil)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard keyPath == #keyPath(UISearchController.isActive),
              let isActive = change?[.newKey] as? Bool else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            return
        }
        
        if isActive {
            print("SearchController 刚被激活")
        } else {
            print("SearchController 已关闭")
        }
    }

    deinit {
        // 记得移除监听,避免内存泄漏
        navigationItem.searchController?.removeObserver(self, forKeyPath: #keyPath(UISearchController.isActive))
    }
}

Combine方式(更简洁):

import Combine

class YourViewController: UIViewController {
    private var cancellables = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()
        let searchController = UISearchController(searchResultsController: nil)
        navigationItem.searchController = searchController
        
        // 监听isActive的变化
        searchController.publisher(for: \.isActive)
            .sink { isActive in
                if isActive {
                    print("SearchController 呈现了")
                } else {
                    print("SearchController 关闭了")
                }
            }
            .store(in: &cancellables)
    }
}

二、解决Push新ViewController时的难看隐藏动画

这个问题的核心是:两个ViewController都有自己的SearchController,push时系统会先隐藏当前VC的搜索栏,再显示新VC的,导致动画冲突、视觉卡顿。给你两个高效的解决方案:

1. Push前手动关闭SearchController

在触发push的代码里,先检查当前SearchController是否处于激活状态,如果是,先关闭它,等关闭动画完成后再push新VC:

func didTapSearchResultItem() {
    // 检查搜索控制器是否激活
    guard let searchController = navigationItem.searchController else {
        pushNewVC()
        return
    }
    
    if searchController.isActive {
        // 先关闭搜索控制器,动画完成后再push
        searchController.dismiss(animated: true) { [weak self] in
            self?.pushNewVC()
        }
    } else {
        pushNewVC()
    }
}

private func pushNewVC() {
    let newVC = YourTargetViewController()
    // 给新VC设置搜索控制器
    newVC.navigationItem.searchController = UISearchController(searchResultsController: nil)
    navigationController?.pushViewController(newVC, animated: true)
}

2. 让NavigationController共享SearchController

如果两个VC的搜索逻辑类似,可以让NavigationController持有一个全局的SearchController,所有子VC共用它,这样push时就不会有搜索栏的隐藏/显示动画切换:

class YourNavController: UINavigationController {
    private let sharedSearchController = UISearchController(searchResultsController: nil)

    override func viewDidLoad() {
        super.viewDidLoad()
        // 给根VC设置共享搜索控制器
        topViewController?.navigationItem.searchController = sharedSearchController
    }

    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        // 给即将push的VC设置同一个搜索控制器
        viewController.navigationItem.searchController = sharedSearchController
        super.pushViewController(viewController, animated: animated)
    }
}

这样处理后,push过程中搜索栏会平滑过渡,不会出现突兀的隐藏动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:22:27