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

RxSwift中SearchBar绑定TableView报错:无法转为UISearchBarDelegate

解决RxSwift结合UISearchDisplayController的致命错误问题

嘿,这个错误我之前帮朋友排查过,本质是RxSwift和旧的UISearchDisplayController的委托机制冲突了,咱们一步步来解决:

错误原因分析

你遇到的Thread 1: Fatal error: Failure converting from Optional() to UISearchBarDelegate,核心问题是:

  • RxSwift在绑定UISearchBar时,会自动接管它的delegate属性,替换成自己的内部代理对象
  • 而UISearchDisplayController(这个组件其实iOS8之后就被苹果废弃了)依赖UISearchBar的原生delegate来同步搜索状态,两者的委托逻辑打架,导致类型转换失败,触发了强制解包的致命错误

解决方案

方案一:推荐升级到UISearchController(更稳妥)

既然UISearchDisplayController已经被废弃,不如直接换成苹果推荐的UISearchController,RxSwift对它的支持更友好,也不会有委托冲突问题。具体步骤:

  1. 在Storyboard中移除UISearchDisplayController,给UITableView添加UISearchController并关联到导航栏
  2. 用RxSwift监听搜索输入并绑定数据,示例代码:
import RxSwift
import RxCocoa

class YourViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    private let searchController = UISearchController(searchResultsController: nil)
    private let originalData = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]
    private let filteredData = BehaviorRelay<[String]>(value: [])
    private let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 配置搜索控制器
        searchController.searchResultsUpdater = self
        searchController.obscuresBackgroundDuringPresentation = false
        searchController.searchBar.placeholder = "搜索水果"
        navigationItem.searchController = searchController
        definesPresentationContext = true
        
        // 监听搜索框输入(用RxSwift替代原生的updateSearchResults方法)
        searchController.searchBar.rx.text.orEmpty
            .debounce(.milliseconds(300), scheduler: MainScheduler.instance) // 防抖,避免频繁过滤
            .distinctUntilChanged() // 输入不变时不重复处理
            .subscribe(onNext: { [weak self] searchText in
                guard let self = self else { return }
                // 过滤数据
                let results = searchText.isEmpty ? self.originalData : self.originalData.filter {
                    $0.lowercased().contains(searchText.lowercased())
                }
                self.filteredData.accept(results)
            })
            .disposed(by: disposeBag)
        
        // 绑定过滤后的数据到tableView
        filteredData.asObservable()
            .bind(to: tableView.rx.items(cellIdentifier: "FruitCell", cellType: UITableViewCell.self)) { _, item, cell in
                cell.textLabel?.text = item
            }
            .disposed(by: disposeBag)
    }
}

// 这里只是为了满足UISearchController的协议要求,实际逻辑已经用Rx处理了
extension YourViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {}
}

方案二:坚持使用UISearchDisplayController(临时修复)

如果必须保留旧组件,需要避免RxSwift和原生委托的冲突,关键是不让Rx接管UISearchBar的delegate,而是用Rx的其他方式监听输入:

import RxSwift
import RxCocoa

class YourViewController: UIViewController, UISearchBarDelegate {
    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var tableView: UITableView!
    private let originalData = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]
    private let filteredData = BehaviorRelay<[String]>(value: [])
    private let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 手动设置SearchDisplayController的委托,避免Rx接管
        searchDisplayController?.delegate = self
        searchBar.delegate = self
        
        // 用rx.text监听输入,但不要让Rx替换delegate(这里用orEmpty避免可选值问题)
        searchBar.rx.text.orEmpty
            .debounce(.milliseconds(300), scheduler: MainScheduler.instance)
            .distinctUntilChanged()
            .subscribe(onNext: { [weak self] searchText in
                guard let self = self else { return }
                let results = searchText.isEmpty ? self.originalData : self.originalData.filter {
                    $0.lowercased().contains(searchText.lowercased())
                }
                self.filteredData.accept(results)
                // 手动通知SearchDisplayController更新结果
                self.searchDisplayController?.searchResultsTableView.reloadData()
            })
            .disposed(by: disposeBag)
        
        // 绑定数据到主tableView和搜索结果tableView
        filteredData.asObservable()
            .bind(to: tableView.rx.items(cellIdentifier: "FruitCell", cellType: UITableViewCell.self)) { _, item, cell in
                cell.textLabel?.text = item
            }
            .disposed(by: disposeBag)
        
        filteredData.asObservable()
            .bind(to: searchDisplayController?.searchResultsTableView.rx.items(cellIdentifier: "FruitCell", cellType: UITableViewCell.self) ?? .empty()) { _, item, cell in
                cell.textLabel?.text = item
            }
            .disposed(by: disposeBag)
    }
}

关键注意点

  • 绝对不要同时让RxSwift和原生代码设置UISearchBar的delegate,这是冲突的根源
  • 防抖(debounce)和去重(distinctUntilChanged)是搜索自动补全的必备优化,避免频繁触发过滤逻辑
  • 优先选择UISearchController,因为UISearchDisplayController已经被苹果标记为废弃,后续iOS版本可能不再兼容

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:23:44