I’ve created a search bar programmatically and I additionally outlined the search operate as effectively. However the issue is after I enter the textual content into the search bar, it isn’t filtering the info. I’ve set the filtered knowledge into the viewDidLoad
technique.
Right here is the view mannequin code.
enum MoviesViewModelState {
case loading
case loaded([Movie])
case error
var motion pictures: [Movie] {
swap self {
case .loaded(let motion pictures):
return motion pictures
case .loading, .error:
return []
}
}
}
closing class MoviesViewModel {
non-public let apiManager: APIManaging
init(apiManager: APIManaging = APIManager()) {
self.apiManager = apiManager
}
var updatedState: (() -> Void)?
var state: MoviesViewModelState = .loading {
didSet {
updatedState?()
}
}
func fetchData() {
apiManager.execute(Film.topRated) { [weak self] lead to
swap end result {
case .success(let web page):
self?.state = .loaded(web page.outcomes)
case .failure:
self?.state = .error
}
}
}
}
Right here is the view controller code.
import UIKit
closing class MoviesViewController: UITableViewController, UISearchResultsUpdating {
non-public let viewModel: MoviesViewModel
non-public var filteredData: [Movie] = []
var searchViewController: UISearchController!
init(viewModel: MoviesViewModel) {
self.viewModel = viewModel
tremendous.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been carried out")
}
override func viewDidLoad() {
tremendous.viewDidLoad()
title = LocalizedString(key: "motion pictures.title")
NotificationCenter.default.addObserver(self, selector: #selector(textSizeChanged), identify: UIContentSizeCategory.didChangeNotification, object: nil)
filteredData = viewModel.state.motion pictures
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 knowledge = viewModel.state.motion pictures
if let searchText = searchController.searchBar.textual content {
filteredData = searchText.isEmpty ? knowledge : filteredData
tableView.reloadData()
}
}
non-public func configureTableView() {
tableView.dm_registerClassWithDefaultIdentifier(cellClass: MovieCell.self)
tableView.rowHeight = UITableView.automaticDimension
refreshControl = UIRefreshControl()
refreshControl?.addTarget(self, motion: #selector(refreshData), for: .valueChanged)
}
non-public func bindViewModel() {
viewModel.updatedState = { [weak self] in
guard let self else { return }
DispatchQueue.essential.async {
self.updateFromViewModel()
}
}
}
non-public func updateFromViewModel() {
swap viewModel.state {
case .loading, .loaded:
tableView.reloadData()
case .error:
showError()
}
refreshControl?.endRefreshing()
}
non-public func showError() {
let alertController = UIAlertController(title: "", message: LocalizedString(key: "motion pictures.load.error.physique"), preferredStyle: .alert)
let alertAction = UIAlertAction(title: LocalizedString(key: "motion pictures.load.error.actionButton"), fashion: .default, handler: nil)
alertController.addAction(alertAction)
current(alertController, animated: true, completion: nil)
}
@objc non-public func refreshData() {
viewModel.fetchData()
}
@objc non-public func textSizeChanged() {
tableView.reloadData()
}
non-public func configureSearchBar() {
let searchTextField = searchViewController.searchBar.searchTextField
searchTextField.attributedPlaceholder = NSAttributedString(string: "Search", attributes: [.font: UIFont.Body.medium, .foregroundColor: UIColor.Text.charcoal])
searchTextField.font = UIFont(identify: "Poppins-Common", measurement: 16)
searchTextField.backgroundColor = UIColor(pink: 248 / 255.0, inexperienced: 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: .regular)
searchViewController.searchBar.showsBookmarkButton = true
searchViewController.searchBar.delegate = self
searchViewController.searchBar.tintColor = UIColor.Model.popsicle40
}
}
// MARK: - UITableViewDataSource
extension MoviesViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection part: Int) -> Int {
return filteredData.depend
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: MovieCell = tableView.dm_dequeueReusableCellWithDefaultIdentifier()
let film = filteredData[indexPath.row]
cell.configure(film)
return cell
}
}
// MARK: - UITableViewControllerDelegate
extension MoviesViewController {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let film = viewModel.state.motion pictures[indexPath.row]
let viewModel = MoviesDetailsViewModel(film: film, apiManager: APIManager())
let viewController = MovieDetailsViewController(viewModel: viewModel)
self.navigationController?.pushViewController(viewController, animated: true)
}
}
extension MoviesViewController: UISearchBarDelegate {
func searchBarBookmarkButtonClicked(_ searchBar: UISearchBar) {
}
}
Right here is the screenshot of the end result.