How search in UITableView with Array of model - ios

I want to search for some data in the table view using the search bar, but when I try to find data in my model, I'm not able to search that data.
I made a expand table view cell and created a search bar for searching data, but still I can't search the data in the model. How can I achieve that?
my code is here:
import UIKit
protocol SelectCurrencyViewControllerDelegate: AnyObject {
func selectedCurrency(_ curency: CurrencyModel)
}
final class SelectCurrencyViewController: UIViewController {
weak var delegate: SelectCurrencyViewControllerDelegate?
//MARK: - Private properties
private let currencyCellIdentifier = "currencyCell"
private let currencyArray = [CurrencyModel(shortName: "RUB", fullName: "Russian ruble"),
CurrencyModel(shortName: "USD", fullName: "US Dollar"),
CurrencyModel(shortName: "EUR", fullName: "Euro")]
//MARK: - UI elements
private let currencyTableView = UITableView()
private let searchBarCurrency = UISearchBar()
//MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
//MARK: - Private methods
private func setupUI() {
view.backgroundColor = .systemBackground
view.addSubview(searchBarCurrency)
view.addSubview(currencyTableView)
settingsSearchbar()
settingsCurrencyTableView()
setConstraints()
}
private func settingsSearchbar() {
searchBarCurrency.translatesAutoresizingMaskIntoConstraints = false
searchBarCurrency.delegate = self
}
private func settingsCurrencyTableView() {
currencyTableView.dataSource = self
currencyTableView.delegate = self
currencyTableView.register(CustomCurrencyCell.self, forCellReuseIdentifier: currencyCellIdentifier)
currencyTableView.translatesAutoresizingMaskIntoConstraints = false
}
private func setConstraints() {
NSLayoutConstraint.activate([
searchBarCurrency.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
searchBarCurrency.leadingAnchor.constraint(equalTo: view.leadingAnchor),
searchBarCurrency.trailingAnchor.constraint(equalTo: view.trailingAnchor),
currencyTableView.topAnchor.constraint(equalTo: searchBarCurrency.bottomAnchor),
currencyTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
currencyTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
currencyTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
}
//MARK: - UISearchBarDelegate
extension SelectCurrencyViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print(searchText)
}
}
//MARK: - UITableViewDelegate, UITableViewDataSource
extension SelectCurrencyViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
currencyArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: currencyCellIdentifier,
for: indexPath) as? CustomCurrencyCell
cell?.configure(shortName: currencyArray[indexPath.row].shortName,
fullName: currencyArray[indexPath.row].fullName)
return cell ?? UITableViewCell()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.selectedCurrency(currencyArray[indexPath.row])
navigationController?.popViewController(animated: true)
}
}
my code of CustomCell here:
import UIKit
final class CustomCurrencyCell: UITableViewCell {
//MARK: - UI Elements
private let currencyBoldLabel = UILabel()
private let currencyNormalFullLabel = UILabel()
//MARK: - Life Cycle
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Configure
func configure(shortName: String, fullName: String) {
currencyBoldLabel.text = shortName
currencyNormalFullLabel.text = fullName
}
//MARK: - Private methods
private func setupUI() {
addSubview(currencyBoldLabel)
addSubview(currencyNormalFullLabel)
settingsCurrencyBoldLabel()
setConstraints()
}
private func settingsCurrencyBoldLabel() {
currencyBoldLabel.translatesAutoresizingMaskIntoConstraints = false
currencyBoldLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold)
}
private func setConstraints() {
currencyNormalFullLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
currencyBoldLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
currencyBoldLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
currencyNormalFullLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
currencyNormalFullLabel.leadingAnchor.constraint(equalTo: currencyBoldLabel.trailingAnchor, constant: 8)
])
}
}
and code of my Model:
import Foundation
struct CurrencyModel {
let shortName: String
let fullName: String
}
I try this add search data
private var searchDate = [CurrencyModel]()
and in searchBarDelegate try this:
extension SelectCurrencyViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchDate = searchText.isEmpty ? currencyArray : currencyArray.filter {$0.fullName.contains(searchText)}
currencyTableView.reloadData()
}
}
Idk how cast my cell in cellForRowAt, cause I used configure
and if I trying
cell?.currencyNormalFullLabel.text = searchDate[indexPath.row].fullName
my app crashed

I have solved my problem:
In viewDidLoad I’m add
filtredData = currencyArray
and in numberOfRowsInSection change like this:
extension SelectCurrencyViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
filtredData.count
}
and cast my cell like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: currencyCellIdentifier,
for: indexPath) as? CustomCurrencyCell
cell?.configure(shortName: filtredData[indexPath.row].shortName,
fullName: filtredData[indexPath.row].fullName)
return cell ?? UITableViewCell()
}
and finally edit my filter method:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filtredData = []
if searchText == "" {
filtredData = currencyArray
}
for word in currencyArray {
if word.fullName.lowercased().contains(searchText.lowercased()) ||
word.shortName.lowercased().contains(searchText.lowercased()) {
filtredData.append(word)
}
}
self.currencyTableView.reloadData()
}

Related

UISearchBar to filter custom UITableViewCells

I am trying to filter custom UITableViewCells, based on one of the UILabels present in the cell.
All of the data is parsed from remote JSON, which has the model locally named Item and currently I am displaying the following in each cell successfully:
var titleLabel = UILabel()
var descriptionLabel = UILabel()
Also defined in my ItemTableViewCell class is:
func set(product: Item) {
DispatchQueue.main.async { [weak self] in
self?.titleLabel.text = item.title
self?.descriptionLabel.text = item.listPrice
}
}
These are called within my main View Controller, to which I have added a searchBar successfully and is visible within the app:
import Foundation
import UIKit
class ItemsListViewController: UIViewController {
var items = [Items]()
var itemsSearch: [Items] = []
var tableView = UITableView()
var searchController = UISearchController()
struct Cells {
static let itemCell = "ItemCell"
}
override func viewDidLoad() {
super.viewDidLoad()
configureTableView()
configureSearchController()
}
func configureSearchController() {
searchController = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
searchController.searchBar.placeholder = "Search items"
definesPresentationContext = true
var isSearchBarEmpty: Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
}
func configureTableView() {
view.addSubview(tableView)
setTableViewDelegates()
tableView.rowHeight = 100
tableView.pin(to: view)
tableView.register(itemTableViewCell.self, forCellReuseIdentifier: Cells.itemCell)
}
func setTableViewDelegates() {
tableView.delegate = self
tableView.dataSource = self
}
}
extension itemsListViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Cells.itemCell) as! itemTableViewCell
let item = items[indexPath.row]
cell.set(item: item)
return cell
}
}
extension ItemsListViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
}
}
How can I filter these cells so say for instance a user searches for "Food", the only items which return would be ones which have a cell titleLabel = "food"?
I've tried to implement a function similar to the below, however it fails to achieve what I am after:
func filterContentForSearchText(_ searchText: String, category: = nil) {
let cell = tableView.dequeueReusableCell(withIdentifier: Cells.productCell) as! ProductTableViewCell
productsSearch = products.filter { (cell: cell) -> Bool in
return products.name.lowercased().contains(searchText.lowercased())
}
tableView.reloadData()
}
Thanks in advance for any help here.
You can not filter TableViewCells. You have to filter your model data and instead of using UISearchResultsUpdating you should use UISearchBarDelegate
I have modified your code, check it.
class ItemsListViewController: UIViewController {
var items = [Items]()
var itemsSearch: [Items] = []
var filterActive = false
var tableView = UITableView()
var searchController = UISearchController()
struct Cells {
static let itemCell = "ItemCell"
}
override func viewDidLoad() {
super.viewDidLoad()
configureTableView()
configureSearchController()
}
func configureSearchController() {
searchController = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
searchController.searchBar.placeholder = "Search items"
searchController.searchBar.delegate = self
definesPresentationContext = true
}
func configureTableView() {
view.addSubview(tableView)
setTableViewDelegates()
tableView.rowHeight = 100
tableView.pin(to: view)
tableView.register(itemTableViewCell.self, forCellReuseIdentifier: Cells.itemCell)
}
func setTableViewDelegates() {
tableView.delegate = self
tableView.dataSource = self
}
}
extension ItemsListViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filterActive ? itemsSearch.count : items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Cells.itemCell) as! itemTableViewCell
let item = filterActive ? itemsSearch[indexPath.row] : items[indexPath.row]
cell.set(item: item)
return cell
}
}
extension ItemsListViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filterItems(text: searchController.searchBar.text)
}
func filterItems(text: String?) {
guard let text = text else {
filterActive = false
self.tableView.reloadData()
return
}
self.itemsSearch = self.items.filter({ (item) -> Bool in
return item.title.lowercased().contains(text.lowercased())
})
filterActive = true
self.tableView.reloadData()
}
// Edited Version
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = nil
filterActive = false
self.tableView.reloadData()
}
}
You are doing it the wrong way.
You should not filter the cells, but instead filter the model (e.g. add and remove entries to the table view data source)
Then call reloadData - this will then access the filtered model (in cellForRowAt and create only the filtered amount of (visible) cells
To improve, use a diffable data source

Create SearchController for reusable use

Is it possible to create UISearchController for reusable use in many classes?
To use a minimum of code in new classes.
Now I have code in all classes, but I want to use minimum code in all classes to call searchController:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet weak var tableView: UITableView!
var photoBook: [PhotoBooking] = []
var filteredBook: [PhotoBooking] = []
private var searchController = UISearchController(searchResultsController: nil)
private var searchBarIsEmpty: Bool {
guard let text = searchController.searchBar.text else { return false }
return text.isEmpty
}
private var isFiltering: Bool {
return searchController.isActive && !searchBarIsEmpty
}
override func viewDidLoad() {
super.viewDidLoad()
configureSearchController()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering {
return filteredBook.count
}
return photoBook.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
var pBook: PhotoBooking
if isFiltering {
pBook = filteredBook[indexPath.row]
} else {
pBook = photoBook[indexPath.row]
}
cell.textLabel?.text = pBook.invoiceID
cell.detailTextLabel?.text = pBook.contactInfo["surname"] as! String
return cell
}
private func configureSearchController() {
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.searchBar.barTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.delegate = self
tableView.tableHeaderView = searchController.searchBar
definesPresentationContext = true
}
}
extension AllPhotoBookingsViewController: UISearchResultsUpdating {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
filterContentForSearchText(searchBar.text!)
tableView.reloadData()
}
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
tableView.reloadData()
}
private func filterContentForSearchText(_ searchText: String) {
filteredBook = photoBook.filter({ (book: PhotoBooking) -> Bool in
let name = book.contactInfo["name"] as! String
let surname = book.contactInfo["surname"] as! String
let invoice = book.invoiceID
return invoice.localizedCaseInsensitiveContains(searchText) ||
name.localizedCaseInsensitiveContains(searchText) ||
surname.localizedCaseInsensitiveContains(searchText)
})
tableView.reloadData()
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
navigationController?.hidesBarsOnSwipe = false
}
}
Maybe need use protocol, but I don't understand how to use correctly protocol.
There are many ways of doing this, using protocols is a great approach, but you could also subclass UIViewController and make a BaseSearchableViewController where you would add all the functionality you need for your search, then, you just subclass from BaseSearchableViewController instead of UIViewController.

DidSelectRowAt won't return desired item

I have dynamic table which displays a number of MKMapItems - these items are being displayed by cellForRowAt. But somehow if I click on the cell didSelectRow won't return the map item if I want to print it:
class SearchResultTableViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var visualEffectView: UIVisualEffectView!
private enum SegueID: String {
case showDetail
case showAll
}
private enum CellReuseID: String {
case resultCell
}
private var places: [MKMapItem]? {
didSet {
tableView.reloadData()
}
}
private var suggestionController: SuggestionsTableTableViewController!
var searchController: UISearchController!
private var localSearch: MKLocalSearch? {
willSet {
places = nil
localSearch?.cancel()
}
}
private var boundingRegion: MKCoordinateRegion?
override func awakeFromNib() {
super.awakeFromNib()
...
}
override func viewDidLoad() {
super.viewDidLoad()
...
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if #available(iOS 10, *) {
visualEffectView.layer.cornerRadius = 9.0
visualEffectView.clipsToBounds = true
}
}
...
}
extension SearchResultTableViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return places?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseID.resultCell.rawValue, for: indexPath)
if let mapItem = places?[indexPath.row] {
cell.textLabel?.text = mapItem.name
cell.detailTextLabel?.text = mapItem.placemark.formattedAddress
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
print(indexPath)
...
guard let mapItem = places?[indexPath.row] else { return }
print(mapItem.name)
...
if tableView == suggestionController.tableView, let suggestion = suggestionController.completerResults?[indexPath.row] {
searchController.isActive = false
searchController.searchBar.text = suggestion.title
search(for: suggestion)
}
}
}
guard let mapItem = places?[indexPath.row] else { return }
- are you sure this guard statement isn't returning?
also, I'm not seeing where you're setting the VC as the tableView delegate/datasource.

Inserting table view row by pressing the button

I created a UITabBarController with two controllers , as first I have a
ViewController with UIButton on it and the second is UITableViewController, how can I by pressing on btn in ViewController insert new row in tableView?
Should it be done with custom delegation ?
Can someone please show quick example
here is my code
import UIKit
class Home: UIViewController {
var delegate: TableViewDelegate?
lazy var addButton : UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "plus_photo"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(setupRows), for: .touchUpInside)
return button
}()
#objc func setupRows() {
delegate?.insertingRows()
}
override func viewDidLoad() {
super.viewDidLoad()
delegate = self as? TableViewDelegate
setupRows()
navigationItem.title = "Test"
view.backgroundColor = .white
view.addSubview(addButton)
addButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
addButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
addButton.heightAnchor.constraint(equalToConstant: 150).isActive = true
addButton.widthAnchor.constraint(equalToConstant: 150).isActive = true
}
}
import UIKit
protocol TableViewDelegate {
func insertingRows()
}
class TableViewTest: UITableViewController , TableViewDelegate {
var items = ["abc", "abcd", "abcde"]
let cellid = "cellid"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomCell.self, forCellReuseIdentifier: cellid)
insertingRows()
}
func insertingRows() {
let indexPath = IndexPath(row: items.count + 1, section: 0)
tableView.insertRows(at: [indexPath], with: .left)
self.tableView.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellid, for: indexPath) as! CustomCell
cell.textLabel?.text = items[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
class CustomCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
}
}
You can use tabBar(_:didSelect:) delegate method in your Home viewController.
Your Home VC :
class Home: UIViewController, UITabBarControllerDelegate {
private var selectedIndex: Int?
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if tabBarController.selectedIndex == 1 {
if let secondViewController = viewController as? TableViewTest, let index = selectedIndex {
secondViewController.insertingRows(index: index)
}
}
}
#objc func setupRows() {
selectedIndex = 3
}
}
Your TableViewTest TableViewController :
class TableViewTest: UITableViewController {
func insertingRows(index: Int) {
let indexPath = IndexPath(row: index, section: 0)
tableView.beginUpdates()
tableView.insertRows(at: [indexPath], with: .none)
tableView.endUpdates()
}
}

Add a search bar to custom Table View

I am creating a custom tableViewController with searchBar. I created a custom cell class "DataCell".
With following code searchBar is not shown and array of label is not displayed.
How can I solve this problem?
import UIKit
class DataCell: UITableViewCell{
#IBOutlet weak var label: UILabel!
}
class SearchController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var isSearching = false
var data = ["a","b","c","d","e"]
var filterData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
searchBar.delegate = self
searchBar.returnKeyType = UIReturnKeyType.done
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearching{
return filterData.count
}
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath) as? DataCell {
let text: String!
if isSearching {
text = filterData[indexPath.row]
} else {
text = data[indexPath.row]
}
return cell
} else {
return UITableViewCell()
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == ""{
isSearching = false
view.endEditing(true)
tableView.reloadData()
}else {
isSearching = true
filterData = data.filter({$0 == searchBar.text})
tableView.reloadData()
}
}
}
As of iOS 8, you should be using the [UISearchController][1] for which you need to create 2 classes. A SearchController and a ResultsController. We start by creating a common UITableView class:
import UIKit
class DataCell: UITableViewCell {
#IBOutlet weak var label: UILabel!
func configureCell(_ text: String) {
label.text = text
}
}
Then, for the search class:
class SearchController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var searchController: UISearchController!
var resultController: ResultController?
var data = ["a","b","c","d","e"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(TableCell.self, forCellReuseIdentifier: "DataCell")
tableView.register(UINib(nibName: "DataCell", bundle: Bundle.main), forCellReuseIdentifier: "DataCell")
// Search Results
resultController = ResultController()
setupSearchControllerWith(resultController!)
if #available(iOS 9.0, *) {
searchController?.loadViewIfNeeded()
}
tableView.delegate = self
tableView.dataSource = self
resultController.tableView.delegate = self
}
}
extension: SearchController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath) as? DataCell {
return cell.configureCell(data[indexPath.row])
} else {
return UITableViewCell()
}
}
}
extension SearchController: UITableViewDelegate {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView == resultController?.tableView {
performSegue(withIdentifier: "DetailView", sender: resultController?.filterData[indexPath.row])
} else {
performSegue(withIdentifier: "DetailView", sender: data[indexPath.row])
}
}
}
extension SearchController: UISearchResultsUpdating,
UISearchControllerDelegate,
UISearchBarDelegate {
func updateSearchResults(for searchController: UISearchController) {
let resultsTable = searchController.searchResultsController as! ResultVC
// resultsTable.query = searchController.searchBar.text!
resultsTable.filterData = data.filter({
$0 == searchController.searchBar.text!
})
resultsTable.tableView.reloadData()
}
func setupSearchControllerWith(_ results: ResultVC) {
// Register Cells
results.tableView.register(TableCell.self, forCellReuseIdentifier: "DataCell")
results.tableView.register(UINib(nibName: "DataCell", bundle: Bundle.main), forCellReuseIdentifier: "DataCell")
// We want to be the delegate for our filtered table so didSelectRowAtIndexPath(_:) is called for both tables.
results.tableView.delegate = self
searchController = UISearchController(searchResultsController: results)
// Set Search Bar
searchController.searchResultsUpdater = self
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
// Set delegates
searchController.delegate = self
searchController.searchBar.delegate = self
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == ""{
isSearching = false
view.endEditing(true)
tableView.reloadData()
} else {
isSearching = true
filterData = data.filter({$0 == searchBar.text})
tableView.reloadData()
}
}
}
Then, for the ResultsController class:
class ResultController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var filterData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(TableCell.self, forCellReuseIdentifier: "DataCell")
tableView.register(UINib(nibName: "DataCell", bundle: Bundle.main), forCellReuseIdentifier: "DataCell")
tableView.dataSource = self
}
}
extension ResultController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let rowCount = filterData.count
// When no data insert centered label
if rowCount == 0 {
blankView(with: textForEmptyLabel)
} else {
// Remove empty table label
tableView.backgroundView = nil
tableView.separatorStyle = .singleLine
}
return rowCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath) as? DataCell {
return cell.configureCell(filterData[indexPath.row])
} else {
return UITableViewCell()
}
}
}

Resources