基于MVVM架构实现TableView搜索功能的技术问询(附代码片段)
基于MVVM架构实现iOS TableView搜索功能(点击TextField触发)
一、先补全你的ViewModel及相关依赖
首先,你提到的SearchDataSourceModel应该是用来管理数据源状态的,咱们先完善它,然后补全SearchViewModel的搜索逻辑:
// 定义SearchDataSourceModel,用来管理原始数据和搜索结果数据 class SearchDataSourceModel { var originalRestaurants: [SearchModel] = [] var filteredRestaurants: [SearchModel] = [] } // 补全你的SearchViewModel class SearchViewModel: NSObject { var datasourceModel: SearchDataSourceModel // 定义回调,用来通知ViewController更新UI var updateUIHandler: (() -> Void)? init(withdatasource newDatasourceModel: SearchDataSourceModel) { self.datasourceModel = newDatasourceModel super.init() } // 搜索方法,根据关键词过滤数据 func performSearch(with keyword: String?) { guard let keyword = keyword, !keyword.isEmpty else { // 如果关键词为空,显示全部数据 datasourceModel.filteredRestaurants = datasourceModel.originalRestaurants updateUIHandler?() return } // 过滤包含关键词的餐厅名称(忽略大小写) datasourceModel.filteredRestaurants = datasourceModel.originalRestaurants.filter { $0.restaurantname.lowercased().contains(keyword.lowercased()) } updateUIHandler?() } // 提供给TableView的数据源方法 func numberOfRows() -> Int { return datasourceModel.filteredRestaurants.count } func modelForRow(at indexPath: IndexPath) -> SearchModel? { guard indexPath.row < datasourceModel.filteredRestaurants.count else { return nil } return datasourceModel.filteredRestaurants[indexPath.row] } }
二、ViewController层实现(绑定TextField与TableView)
接下来在ViewController里处理TextField的点击事件,绑定ViewModel,实现TableView的代理和数据源:
import UIKit class SearchViewController: UIViewController { @IBOutlet weak var searchTextField: UITextField! @IBOutlet weak var restaurantsTableView: UITableView! // 初始化ViewModel,这里假设你已经有原始数据,比如从接口或本地加载 private let dataSourceModel = SearchDataSourceModel() private lazy var searchViewModel: SearchViewModel = { let vm = SearchViewModel(withdatasource: dataSourceModel) // 绑定UI更新回调 vm.updateUIHandler = { [weak self] in self?.restaurantsTableView.reloadData() } return vm }() override func viewDidLoad() { super.viewDidLoad() setupUI() loadOriginalData() } private func setupUI() { restaurantsTableView.dataSource = self // 设置TextField的点击事件,确保点击就触发搜索 searchTextField.addTarget(self, action: #selector(textFieldTapped), for: .touchDown) // 可选:编辑开始时也触发搜索,避免用户点击后无反应 searchTextField.addTarget(self, action: #selector(textFieldDidBeginEditing), for: .editingDidBegin) } // 加载原始数据(示例数据,你可以替换成实际的接口请求或本地数据) private func loadOriginalData() { let sampleData = [ ["name": "KFC"], ["name": "McDonald's"], ["name": "Starbucks"], ["name": "Subway"] ] dataSourceModel.originalRestaurants = sampleData.compactMap { SearchModel(dictionary: $0) } // 初始显示全部数据 searchViewModel.performSearch(with: nil) } // TextField点击触发搜索 @objc private func textFieldTapped() { // 如果TextField已有文本,直接搜索;如果为空,显示全部 searchViewModel.performSearch(with: searchTextField.text) } // 编辑开始时触发搜索(可选) @objc private func textFieldDidBeginEditing() { textFieldTapped() } } // TableView数据源实现 extension SearchViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return searchViewModel.numberOfRows() } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "RestaurantCell", for: indexPath) guard let model = searchViewModel.modelForRow(at: indexPath) else { cell.textLabel?.text = "" return cell } cell.textLabel?.text = model.restaurantname return cell } }
三、关键注意点
- MVVM职责划分:ViewModel负责所有业务逻辑(数据过滤、状态管理),ViewController只负责UI渲染和事件转发,不处理数据逻辑
- 回调闭包的弱引用:在ViewModel的
updateUIHandler里用[weak self]避免循环引用 - 搜索逻辑优化:如果数据量很大,可以考虑异步过滤(比如用
DispatchQueue.global()),避免阻塞主线程 - TextField事件选择:用
.touchDown确保点击就触发,也可以结合.editingChanged实现实时搜索(如果需要的话)
内容的提问来源于stack exchange,提问作者robbieroberts
相关产品推荐
相关产品推荐

