MVVM架构下UITextField代理实现TableView搜索问题咨询
MVVM架构下TableView搜索功能完整实现指南
看起来你已经搭好了基础框架——ViewModel里写了搜索逻辑,ViewController也加了必要的代理,接下来咱们一步步把剩下的部分补全,确保功能稳定运行:
1. 先修复ViewModel里的潜在崩溃问题
你当前的search函数里直接强制解包searchText!,如果搜索文本为空或者nil,肯定会崩溃。咱们先把这个逻辑优化一下,同时处理空搜索的场景(展示全部数据):
func search(searchText: String?) { // 处理空搜索文本:直接返回全部数据 guard let searchQuery = searchText, !searchQuery.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { filteredListArray = datasourceModel.dataListArray return } // 非空时执行过滤,同时处理dataListArray为nil的情况 filteredListArray = datasourceModel.dataListArray?.filter { $0.restaurantname?.range(of: searchQuery, options: .caseInsensitive) != nil } ?? [] }
2. 给ViewModel添加UI刷新通知机制
MVVM里ViewModel不能直接操作View,所以需要把filteredListArray变成可观察的属性,让ViewController能监听它的变化来刷新TableView。这里推荐用Combine的@Published(如果你的项目支持Combine):
import Combine class YourViewModel { // 把filteredListArray改成@Published属性 @Published var filteredListArray: [YourRestaurantModel]? = [] var datasourceModel: YourDatasourceModel! // 初始化方法 init(datasourceModel: YourDatasourceModel) { self.datasourceModel = datasourceModel } // 上面优化后的search函数 func search(searchText: String?) { // ... 逻辑同上 } }
3. 在ViewController里绑定ViewModel并监听数据变化
首先确保ViewController持有ViewModel实例,然后在viewDidLoad里设置监听,当filteredListArray变化时自动刷新TableView:
class QM_SearchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate { @IBOutlet private weak var tableView: UITableView! @IBOutlet private weak var searchTextField: UITextField! // 持有ViewModel实例 private var viewModel: YourViewModel! // 用来存储Combine订阅,防止被释放 private var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() // 初始化ViewModel(这里假设你已经有datasourceModel的实例) let datasource = YourDatasourceModel() viewModel = YourViewModel(datasourceModel: datasource) // 绑定ViewModel的filteredListArray变化,刷新TableView viewModel.$filteredListArray .receive(on: DispatchQueue.main) // 确保在主线程刷新UI .sink { [weak self] _ in self?.tableView.reloadData() } .store(in: &cancellables) // 初始加载全部数据 viewModel.search(searchText: nil) // 设置代理 searchTextField.delegate = self tableView.delegate = self tableView.dataSource = self // 注册TableViewCell(如果没在Storyboard里注册的话) tableView.register(YourTableViewCell.self, forCellReuseIdentifier: "YourCellID") } }
4. 实现UITextField代理方法,触发搜索
我们需要监听搜索框的文本变化,调用ViewModel的search方法。这里用textFieldDidChangeSelection或者shouldChangeCharactersIn都可以,推荐前者更简洁:
// MARK: - UITextFieldDelegate extension QM_SearchViewController { func textFieldDidChangeSelection(_ textField: UITextField) { // 把当前搜索框的文本传给ViewModel执行搜索 viewModel.search(searchText: textField.text) } // 可选:处理搜索框的清除按钮点击 func textFieldShouldClear(_ textField: UITextField) -> Bool { viewModel.search(searchText: nil) return true } }
5. 让TableView使用ViewModel的过滤后数据
修改TableView的数据源方法,从viewModel.filteredListArray里取数据:
// MARK: - UITableViewDataSource & Delegate extension QM_SearchViewController { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return viewModel.filteredListArray?.count ?? 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellID", for: indexPath) as! YourTableViewCell // 取出对应的数据模型 guard let restaurant = viewModel.filteredListArray?[indexPath.row] else { return cell } // 给Cell赋值(假设你的Cell有configure方法) cell.configure(with: restaurant) return cell } // 可选:实现didSelectRowAt等代理方法 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) guard let selectedRestaurant = viewModel.filteredListArray?[indexPath.row] else { return } // 处理点击事件,比如跳转详情页 } }
额外优化建议
- 如果你的项目不支持Combine,可以用闭包回调或者自定义Delegate来替换
@Published的监听逻辑; - 可以给搜索添加防抖(比如延迟0.3秒再执行搜索),避免频繁调用过滤函数影响性能;
- 当
filteredListArray为空时,给TableView添加一个空状态占位视图,提升用户体验。
内容的提问来源于stack exchange,提问作者clydececiljohn
相关产品推荐
相关产品推荐

