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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:38:01