Search code examples
iosswiftxcodeuisearchbaruisearchbardelegate

UISearchBar: passing data; Title that is clicked on does not correspond with data that is shown in ListViewController - Swift


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
}

Solution

  • 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