如何检测UINavigationItem中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
相关产品推荐
相关产品推荐

