A specialized view for receiving search-related information from the user.

Declaration

@MainActor class UISearchBar : [UIView](<https://developer.apple.com/documentation/uikit/uiview>)

Overview

UISearchBar provides a text field for entering text, a search button, a bookmark button, and a cancel button. A search bar doesn’t actually perform any searches. You use a delegate, an object conforming to the UISearchBarDelegate protocol, to implement the actions when the user enters text or clicks buttons. For details about interacting with the text field, accessing its content, and using tokens, see UISearchTextField and UISearchToken.

Customize appearance

You can customize the appearance of search bars one at a time, or you can use the appearance proxy ([UISearchBar appearance]) to customize the appearance of all search bars in an app.

In general, you should specify a value for the normal state to be used by other states which don’t have a custom value set. Similarly, when a property is dependent on the bar metrics (on iPhone, in landscape orientation bars have a different height from standard), you should specify a value for UIBarMetricsDefault.


import UIKit

final class DiaryListViewController: UIViewController {
    typealias DataSource = UITableViewDiffableDataSource<Section, DiaryContent>
    typealias Snapshot = NSDiffableDataSourceSnapshot<Section, DiaryContent>
    
    enum Section {
        case main
    }
    
    private lazy var dataSource = self.configureDataSource()
    
    private var diaryListViewModel = DiaryViewModel()
    private var fileteredData = [DiaryContent]()

    private var searchController: UISearchController = {
        let searchController = UISearchController(searchResultsController: nil)
        return searchController
    }()

    private let diaryListTableView: UITableView = {
        let tableView = UITableView()
        tableView.register(DiaryTableViewCell.self, forCellReuseIdentifier: DiaryTableViewCell.identifier)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        return tableView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupDefault()
        configureLayout()
        updateDataSource(data: diaryListViewModel.diaryContents)
        registerNotificationForTableView()
    }
    
    private func setupDefault() {
        self.view.backgroundColor = .white
        self.diaryListTableView.delegate = self
        
        diaryListTableView.tableHeaderView = self.searchController.searchBar
        self.definesPresentationContext = true
        searchController.searchResultsUpdater = self
        searchController.searchBar.delegate = self
        searchController.delegate = self
    }
    
    private func configureDataSource() -> DataSource {
        dataSource = DataSource(tableView: diaryListTableView, cellProvider: { tableView, indexPath, content -> UITableViewCell? in
            guard let cell = tableView.dequeueReusableCell(withIdentifier: DiaryTableViewCell.identifier, for: indexPath) as? DiaryTableViewCell else {
                return UITableViewCell()
            }
            
            cell.configureUI(data: content)
            cell.accessoryType = .disclosureIndicator
            
            return cell
        })
        
        return dataSource
    }
    
    private func updateDataSource(data: [DiaryContent]) {
        var snapshot = Snapshot()
        snapshot.appendSections([.main])
        snapshot.appendItems(data)
    
        dataSource.apply(snapshot, animatingDifferences: false, completion: nil)
    }
    
    private func configureLayout() {
        self.view.addSubview(diaryListTableView)
        
        NSLayoutConstraint.activate([
            diaryListTableView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
            diaryListTableView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
            diaryListTableView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
            diaryListTableView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor)
        ])
    }
}

extension DiaryListViewController: UISearchBarDelegate, UISearchResultsUpdating, UISearchControllerDelegate {
    func updateSearchResults(for searchController: UISearchController) {
        let filter = diaryListViewModel.diaryContents.filter({ (data: DiaryContent) -> Bool in
            return data.title.uppercased().contains((searchController.searchBar.text?.uppercased())!)
        })

        fileteredData = filter.isEmpty ? diaryListViewModel.diaryContents : filter
        
        self.updateDataSource(data: fileteredData)
        diaryListTableView.reloadData()
    }
}