-2

I have a searchResultsViewController in my iOS application that displays an array of data for the user to be able to search through. When I try to search a random letter, lets say P for instance it does not show any of the words containing P.

enter image description here enter image description here

the code that I used to create this searchResults is,

    var array = ["Assembly", "Auto Care", "Electronic Help", "Item Delivery", "Handyman", "House Chores", "Junk Removal", "Lawn & Yard Care", "Moving", "Painting", "Pet Care", "Seasonal Work"]
var selectedItems = [String]()
var searchController = UISearchController()
var filteredArray = [String]()
var resultsController = UITableViewController()


override func viewDidLoad() {
    super.viewDidLoad()
    
    searchController = UISearchController(searchResultsController: resultsController)
    tableView.tableHeaderView = searchController.searchBar
    searchController.searchResultsUpdater = self
    resultsController.tableView.delegate = self
    resultsController.tableView.dataSource = self
    searchController.searchBar.showsCancelButton = true
    searchController.searchBar.showsScopeBar = true
    searchController.searchBar.delegate = self
    
  let attributes = [NSAttributedString.Key.foregroundColor: GREEN_Theme]
  UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes(attributes, for: UIControl.State.normal)
  UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Done"


}


func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
    done()
}

func updateSearchResults(for searchController: UISearchController) {
    filteredArray = array.filter({ (array:String) -> Bool in
        if array.contains(searchController.searchBar.text!) {
             return true
        } else {
        return false
        }
    })
    resultsController.tableView.reloadData()
    searchController.automaticallyShowsCancelButton = true
 }

  override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

if selectedItems.contains(array[indexPath.row]) {
selectedItems.remove(at: selectedItems.firstIndex(of: array[indexPath.row])!)
tableView.cellForRow(at: indexPath)?.accessoryType = .none
} else {
selectedItems.append(array[indexPath.row])
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
tableView.reloadData()
print(selectedItems)
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if tableView == resultsController.tableView {
        return filteredArray.count
        
    } else {
        return array.count
    }
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = array[indexPath.row]
if selectedItems.contains(array[indexPath.row]) {
cell.accessoryType = .checkmark
}else{
cell.accessoryType = .none
}
return cell
}

any thoughts?

zach wilcox
  • 45
  • 14
  • You aren't explaining the relationship between the two arrays. – El Tomato Dec 29 '20 at 05:30
  • @ElTomato doesnt the updateSearchResults show the relationship to the two? – zach wilcox Dec 29 '20 at 05:33
  • It pretty confusing that you name both the data source array and the `filter` closure parameter `array`. And what's in the data source methods `numberOfRows` and `cellForRow`? – vadian Dec 29 '20 at 05:35
  • data source methods have been added. – zach wilcox Dec 29 '20 at 05:38
  • 1
    It's obvious. Your cellForRowAt is totally wrong. It takes no account of the filtered array. So, since _two_ filtered array items contain P, you are showing the first _two_ items of the unfiltered `array`. – matt Dec 29 '20 at 05:45
  • Not to mention the fact that you are not even dequeuing cells! Basically _everything_ about cellForRowAt is wrong. – matt Dec 29 '20 at 05:49
  • And – however unrelated to the issue – your way to maintain the selected rows is horrible. Use a struct as data source and add an `isSelected` struct member. In `cellForRow` set the `accessoryType` according to `isSelected`. And in `didSelect` toggle `isSelected` in the model and reload the row. – vadian Dec 29 '20 at 05:52
  • thanks for the criticism and feedback! – zach wilcox Dec 29 '20 at 09:14

1 Answers1

2

Use this function to filter stuff:

extension SearchVC: UISearchBarDelegate{
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        fetchedData = []
        if searchText == ""{
            fetchedData = items
        } else {
            for words in items{
                if
                    words.item.lowercased().contains(searchText.lowercased()){
                    filteredData.append(words)
                }
            }
        }
        table.reloadData()
    }
}

Where fetchedData is an empty string array and items is your array.
If the search bar is empty fetchedData will be filled with all of your items, else just with the matched ones.

Now, the most important thing to do is to use fetchedData instead of items to display the results and the count properly. So, for instance:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return filteredData.count
}

Furthermore, as other users pointed out in the comments, you should really check your cellForRowAt. Try this link: https://stackoverflow.com/a/34345245/10408872