Table View搜索功能失效问题求助
搜索栏无法过滤数据的问题修复
问题描述
已通过代码创建搜索栏并定义搜索功能,但在搜索栏输入文本时无法过滤数据,已在viewDidLoad方法中设置filteredData。
ViewModel 代码
enum MoviesViewModelState { case loading case loaded([Movie]) case error var movies: [Movie] { switch self { case .loaded(let movies): return movies case .loading, .error: return [] } } } final class MoviesViewModel { private let apiManager: APIManaging init(apiManager: APIManaging = APIManager()) { self.apiManager = apiManager } var updatedState: (() -> Void)? var state: MoviesViewModelState = .loading { didSet { updatedState?() } } func fetchData() { apiManager.execute(Movie.topRated) { [weak self] result in switch result { case .success(let page): self?.state = .loaded(page.results) case .failure: self?.state = .error } } } }
ViewController 代码
import UIKit final class MoviesViewController: UITableViewController, UISearchResultsUpdating { private let viewModel: MoviesViewModel private var filteredData: [Movie] = [] var searchViewController: UISearchController! init(viewModel: MoviesViewModel) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() title = LocalizedString(key: "movies.title") NotificationCenter.default.addObserver(self, selector: #selector(textSizeChanged), name: UIContentSizeCategory.didChangeNotification, object: nil) filteredData = viewModel.state.movies searchViewController = UISearchController(searchResultsController: nil) searchViewController.searchResultsUpdater = self configureSearchBar() configureTableView() updateFromViewModel() bindViewModel() viewModel.fetchData() } override func viewDidAppear(_ animated: Bool) { searchViewController.searchResultsUpdater = self navigationItem.searchController = searchViewController navigationItem.hidesSearchBarWhenScrolling = false definesPresentationContext = true } func updateSearchResults(for searchController: UISearchController) { let data = viewModel.state.movies if let searchText = searchController.searchBar.text { filteredData = searchText.isEmpty ? data : filteredData tableView.reloadData() } } private func configureTableView() { tableView.dm_registerClassWithDefaultIdentifier(cellClass: MovieCell.self) tableView.rowHeight = UITableView.automaticDimension refreshControl = UIRefreshControl() refreshControl?.addTarget(self, action: #selector(refreshData), for: .valueChanged) } private func bindViewModel() { viewModel.updatedState = { [weak self] in guard let self else { return } DispatchQueue.main.async { self.updateFromViewModel() } } } private func updateFromViewModel() { switch viewModel.state { case .loading, .loaded: tableView.reloadData() case .error: showError() } refreshControl?.endRefreshing() } private func showError() { let alertController = UIAlertController(title: "", message: LocalizedString(key: "movies.load.error.body"), preferredStyle: .alert) let alertAction = UIAlertAction(title: LocalizedString(key: "movies.load.error.actionButton"), style: .default, handler: nil) alertController.addAction(alertAction) present(alertController, animated: true, completion: nil) } @objc private func refreshData() { viewModel.fetchData() } @objc private func textSizeChanged() { tableView.reloadData() } private func configureSearchBar() { let searchTextField = searchViewController.searchBar.searchTextField searchTextField.attributedPlaceholder = NSAttributedString(string: "Search", attributes: [.font: UIFont.Body.medium, .foregroundColor: UIColor.Text.charcoal]) searchTextField.font = UIFont(name: "Poppins-Regular", size: 16) searchTextField.backgroundColor = UIColor(red: 248 / 255.0, green: 248 / 255.0, blue: 248 / 255.0, alpha: 1) searchTextField.borderStyle = .none searchTextField.layer.borderColor = UIColor.black.withAlphaComponent(0.08).cgColor searchTextField.layer.borderWidth = 1.0 searchTextField.layer.cornerRadius = 8 searchViewController.searchBar.setLeftImage(UIImage(named: "Search")) searchViewController.searchBar.barTintColor = .clear searchViewController.searchBar.setImage(UIImage(named: "Filter"), for: .bookmark, state: .normal) searchViewController.searchBar.showsBookmarkButton = true searchViewController.searchBar.delegate = self searchViewController.searchBar.tintColor = UIColor.Brand.popsicle40 } } // MARK: - UITableViewDataSource extension MoviesViewController { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return filteredData.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell: MovieCell = tableView.dm_dequeueReusableCellWithDefaultIdentifier() let movie = filteredData[indexPath.row] cell.configure(movie) return cell } } // MARK: - UITableViewControllerDelegate extension MoviesViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let movie = viewModel.state.movies[indexPath.row] let viewModel = MoviesDetailsViewModel(movie: movie, apiManager: APIManager()) let viewController = MovieDetailsViewController(viewModel: viewModel) self.navigationController?.pushViewController(viewController, animated: true) } } extension MoviesViewController: UISearchBarDelegate { func searchBarBookmarkButtonClicked(_ searchBar: UISearchBar) { } }
问题截图

问题原因及修复方案
核心问题分析
- 搜索逻辑缺失:
updateSearchResults方法中,当搜索文本不为空时,filteredData直接赋值为自身,没有执行任何过滤操作,导致输入文本后数据无变化。 - 数据同步问题:
viewDidLoad初始化时viewModel.state为.loading,filteredData被设为空数组;接口返回数据后,updateFromViewModel未更新filteredData,导致列表显示的初始数据为空或不同步。 - 跳转数据源不匹配:点击列表项时使用
viewModel.state.movies获取数据,但列表展示的是filteredData,搜索过滤后会出现索引不匹配,导致跳转错误。
修复步骤
1. 完善搜索过滤逻辑
修改updateSearchResults方法,添加基于电影标题的过滤逻辑(可根据需求调整过滤字段):
func updateSearchResults(for searchController: UISearchController) { let data = viewModel.state.movies // 去除首尾空格,避免空字符干扰 if let searchText = searchController.searchBar.text?.trimmingCharacters(in: .whitespacesAndNewlines), !searchText.isEmpty { filteredData = data.filter { movie in // 忽略大小写匹配标题 movie.title.lowercased().contains(searchText.lowercased()) } } else { filteredData = data } tableView.reloadData() }
2. 同步接口返回后的filteredData
修改updateFromViewModel方法,确保数据加载完成后更新filteredData:
private func updateFromViewModel() { switch viewModel.state { case .loaded(let movies): filteredData = movies tableView.reloadData() case .loading: tableView.reloadData() case .error: showError() } refreshControl?.endRefreshing() }
3. 修复跳转数据源不匹配问题
修改didSelectRowAt方法,使用filteredData获取当前点击的电影数据:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let movie = filteredData[indexPath.row] let viewModel = MoviesDetailsViewModel(movie: movie, apiManager: APIManager()) let viewController = MovieDetailsViewController(viewModel: viewModel) navigationController?.pushViewController(viewController, animated: true) }
4. 移除冗余设置
删除viewDidAppear中重复的searchViewController.searchResultsUpdater = self,该设置在viewDidLoad中已完成。
内容的提问来源于stack exchange,提问作者user21666271
相关产品推荐
相关产品推荐

