0

I'm currently having an issue with my UISearchBar. So when I search for a word, the UISearchBar returns the data that matches the letter(s)/word that I typed. However, when I click one of the tableview cells, the (title) data does not correspond to the (items) data that is shown in the ListViewController. What it does display, (in the ListViewController) is the (items) data that corresponds to the original order/layout (index) of the tableView. So, after searching if I click on the (title) for the first tableView cell that matches what I searched, it displays the (items) data for the first (string) in the (titledata) array instead of corresponding to the title that is shown.

For example: If I type "New" into my SearchBar New Haven (index 1) & New York (index 2) pop up. But, when I click on the (title) New Haven tableView cell, it brings me to the (items) data of Ann Arbor (the first index of the original layout)

Thanks!

Struct Code:

struct Category {
   let title: String
   let items: [String]
}

Data (Array: derived from Struct) Code:

let data: [Category] = [
   Category(title: "Ann Arbor", items: ["Ann Arbor is a city located in Michigan.", "Population: 119,000","University: University of Michigan is located in Ann Arbor.",]),
   Category(title: "Los Angeles", items: ["Los Angeles is a city located in California.", "Population: 3,900,000,", "University: University of California of Los Angeles",]),
   Category(title: "New Haven", items: ["New Haven is a city located in Connecticut.", "Population: 130,000 ","University: Yale University",]),
   Category(title: "New York City", items: ["New York City is located in New York.", "Population: 8,300,000","University: Columbia University",]),
   Category(title: "San Fransisco", items: ["Ann Arbor is a city located in Michigan.", "Population: 881,000","University: University of San Francisco",]),
]

titledata (Array) Code:

let titledata = ["Ann Arbor", "Los Angeles","New Haven", "New York City", "San Fransisco"]

filteredData var:

 var filteredData: [String]!

in viewDidLoad func:

filteredData = titledata

Note: SearchBar Delegate linked in Storyboard.

UISearchBar Code:

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    
    filteredData = []
    
    if searchText == "" {
        
        filteredData = titledata
    }
    
    else {
    for info in titledata {
        
        if info.lowercased().contains(searchText.lowercased()) {
            
            
            self.tableView.reloadData()
            filteredData.append(info)
            
            self.tableView.reloadData()
            
            }
        }
    }
    self.tableView.reloadData()
}

didSelectRowAt Func:

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

    let category = data[indexPath.row]
    let vc = ListViewController(items: category.items)
    vc.title = category.title
    self.navigationController?.pushViewController(vc, animated: true)
  
}

numberOfRowsInSection func:

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

cellForRowAt func:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
   cell.myLabel.text = filteredData[indexPath.row]
   cell.myImg.image = UIImage(named: filteredData[indexPath.row] + ".png")
   return cell
}
Stamenkovski
  • 959
  • 1
  • 6
  • 10
Nicolas G
  • 15
  • 7

2 Answers2

0

The issue is in your method didSelectRowAt, you are using filteredData array to show items but when your performing tap on cell you are using different array:

let category = data[indexPath.row]

you need to use filtered array also when you are performing tap on cell:

let category = filteredData[indexPath.row]
Stamenkovski
  • 959
  • 1
  • 6
  • 10
  • As stated above, now that I have changed let category = data[indexPath.row] to: let category = filteredData[indexPath.row] I am getting the error, "Value of type 'String' has no member 'items'" on the "let vc = ListViewController(items: category.items)" line of code (in the didSelectRowAt func). Thanks! – Nicolas G Oct 18 '20 at 19:13
0

First of all declare a data source array always as non-optional empty array.

Edit: Your data source is supposed to be [Category] rather than [String]

var filteredData = [Category]()

In viewDidLoad assign data, the titledata array is not needed. Delete it.

filteredData = data

As filteredData is the data source array you have to get the item from this array in didSelectRowAt

let category = filteredData[indexPath.row]

There are some other things which can be optimized. In textDidChange the way to filter the data is very expensive because of the loop and – worse – by reloading the table view in each iteration.

This is much more efficient

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    if searchText.isEmpty {
        filteredData = data
    } else {
        filteredData = data.filter{$0.title.range(of: searchText, options: .caseInsensitive) != nil }
    }
    self.tableView.reloadData()
}

And replace cellForRowAt with

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
   let title = filteredData[indexPath.row].title
   cell.myLabel.text = title
   cell.myImg.image = UIImage(named: title + ".png")
   return cell
}

If you like a nice animation use UITableViewDiffableDataSource which animates the table view on change of the data source array

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Hi, Thank you for the response. Now that I have changed let category = data[indexPath.row] to: let category = filteredData[indexPath.row] I am getting the error, "Value of type 'String' has no member 'items'" on the "let vc = ListViewController(items: category.items)" line of code (in the didSelectRowAt func). Thanks again. – Nicolas G Oct 18 '20 at 19:06
  • Your design is confusing. If you need the categories you data source array must be `var filteredData = [Category]()` – vadian Oct 18 '20 at 19:47
  • Hi, thanks for the response again. I've since tried making the edit above, and now there I'm getting the same error in 3 lines of code:"Cannot assign value of type '[String]' to type '[Category]' (Note: I've changed my searchBar config to yours). The whole goal of my app is to display (title) data in a tableView and to be able to click on the tableView cell and for it to display (item) data in the ListViewController which I have setup. If there's a more efficient way to do this, I'd be happy to adapt. Thanks! – Nicolas G Oct 18 '20 at 20:07
  • Hi vadian, I really appreciate you working with me here. I've since made those edits and now receive the following errors in the searchBar config: Cannot infer contextual base in reference to member 'caseInsensitive' & Value of type 'Category' has no member 'range' – Nicolas G Oct 18 '20 at 20:21
  • Both errors are in the following line of code: filteredData = data.filter{$0.range(of searchText, options: .caseInsensitive) !=nil } – Nicolas G Oct 18 '20 at 20:23
  • Please read my edited answer carefully. It's `data.filter{$0.title.range(...` Note `$0.title` – vadian Oct 18 '20 at 20:24
  • Got it! Thank you, vadian. I've been trying to solve this problem for a while now, I can't thank you enough! Hope you have a great day. – Nicolas G Oct 18 '20 at 20:27