I’ve created the search bar programatically and I’m anticipating to filter the info when the textual content is enter into search bar. Drawback is just not filtering the info though I’ve information already downloaded kind api name and tried to look the textual content primarily based on the title.
Right here is the view mannequin code together with the search perform..
import Basis
import UIKit
enum MoviesViewModelState {
case loading
case loaded([Movie])
case error
var films: [Movie] {
change self {
case .loaded(let films):
return films
case .loading, .error:
return []
}
}
}
ultimate class MoviesViewModel {
non-public let apiManager: APIManaging
var filteredMovie: [Movie] = []
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
change outcome {
case .success(let web page):
self?.state = .loaded(web page.outcomes)
case .failure:
self?.state = .error
}
}
}
}
// MARK: - Search perform.
extension MoviesViewModel {
public func inSearchMode(_ searchController: UISearchController) -> Bool {
let isActive = searchController.isActive
let searchText = searchController.searchBar.textual content ?? ""
return isActive && !searchText.isEmpty
}
public func updateSearchController(searchBarText: String?) {
self.filteredMovie = state.films
if let searchText = searchBarText?.lowercased() {
guard !searchText.isEmpty else { return }
self.filteredMovie = self.filteredMovie.filter({ $0.title.lowercased().incorporates(searchText) })
}
}
}
Right here is the view controller code.
import UIKit
ultimate class MoviesViewController: UITableViewController {
non-public let viewModel: MoviesViewModel
// MARK: - UI Parts
non-public let searchController = UISearchController(searchResultsController: nil)
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: "films.title")
NotificationCenter.default.addObserver(self, selector: #selector(textSizeChanged), title: UIContentSizeCategory.didChangeNotification, object: nil)
setupSearchController()
configureTableView()
updateFromViewModel()
bindViewModel()
viewModel.fetchData()
}
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.major.async {
self.updateFromViewModel()
}
}
}
non-public func updateFromViewModel() {
change viewModel.state {
case .loading, .loaded:
tableView.reloadData()
case .error:
showError()
}
refreshControl?.endRefreshing()
}
// MARK: setUpSearch Property.
non-public func setupSearchController() {
self.searchController.searchResultsUpdater = self
self.searchController.obscuresBackgroundDuringPresentation = false
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.searchBar.placeholder = "Search Film"
self.navigationItem.searchController = searchController
self.definesPresentationContext = false
self.navigationItem.hidesSearchBarWhenScrolling = false
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchBar.showsBookmarkButton = true
searchController.searchBar.setImage(UIImage(systemName: "line.horizontal.3.lower"), for: .bookmark, state: .regular)
}
non-public func showError() {
let alertController = UIAlertController(title: "", message: LocalizedString(key: "films.load.error.physique"), preferredStyle: .alert)
let alertAction = UIAlertAction(title: LocalizedString(key: "films.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()
}
}
// MARK: - UITableViewDataSource
extension MoviesViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection part: Int) -> Int {
let inSearchMode = self.viewModel.inSearchMode(searchController)
return inSearchMode ? self.viewModel.filteredMovie.depend : self.viewModel.state.films.depend }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: MovieCell = tableView.dm_dequeueReusableCellWithDefaultIdentifier()
let inSearchMode = self.viewModel.inSearchMode(searchController)
let film = inSearchMode ? self.viewModel.filteredMovie[indexPath.row] : self.viewModel.state.films[indexPath.row]
cell.configure(film)
return cell
}
}
// MARK: - UITableViewControllerDelegate
extension MoviesViewController {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let film = viewModel.state.films[indexPath.row]
let viewModel = MoviesDetailsViewModel(film: film, apiManager: APIManager())
let viewController = MovieDetailsViewController(viewModel: viewModel)
self.navigationController?.pushViewController(viewController, animated: true)
}
}
// MARK: - Search Controller Capabilities
extension MoviesViewController: UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate {
func updateSearchResults(for searchController: UISearchController) {
self.viewModel.updateSearchController(searchBarText: searchController.searchBar.textual content)
}
}
Right here is the screenshot ..