Home > Blockchain >  set up UISearchController for tableView cells for data coming from API
set up UISearchController for tableView cells for data coming from API

Time:02-17

I am really having difficulties implementing the UISearchController for my tableView, I followed Sean Allen's video explaining and did it like him but i guess the problem is at me and I can't figure it out.

I want when a user wants to search through the cells to be updated live time when he is typing and the main thing he will search to be the cell.veturaOutlet.text = currentVechicle.Plate

Please if you have time to help please do so since I am new to Swift and I am really enjoying it just these little problems that came in everybody's way at the begining

this is a pic of how the cells looks like!

class MonitorimiViewController: UIViewController {


@IBOutlet weak var monitorimiTableView: UITableView!
@IBOutlet weak var loaderMonitorimi: UIActivityIndicatorView!


var listOfVechicle: [Vehicles] = []
var filteredVehicles: [Vehicles] = []
var listOfVehicles = [Vehicles]()    


override func viewDidLoad() {
    super.viewDidLoad()
    fetchAndReloadData()
    configureSearchController()
    
    loaderMonitorimi.hidesWhenStopped = true
    loaderMonitorimi.startAnimating()
    
    Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { [self] _ in
        monitorimiTableView.reloadData()
        configureTableView()
        loaderMonitorimi.stopAnimating()
    }
}


func configureTableView() {
    monitorimiTableView.delegate = self
    monitorimiTableView.dataSource = self
    monitorimiTableView.rowHeight = 225
}

func configureSearchController() {
    let searchController = UISearchController()
    searchController.searchResultsUpdater = self
    searchController.searchBar.delegate = self
    searchController.searchBar.placeholder = "Search"
    navigationItem.searchController = searchController
}


func fetchAndReloadData(){
    APICaller.shared.getVehicles(for: APICaller.shared.id!) {[weak self] (result) in
        guard let self = self else { return }
        
        switch result {
        case .success(let vehicle):
            self.listOfVechicle = vehicle
            DispatchQueue.main.async {
                self.monitorimiTableView.reloadData()
            }
        case .failure(let error):
            print(error)
      }
    }
  }
}


    extension MonitorimiViewController: UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating, UISearchBarDelegate{
func updateSearchResults(for searchController: UISearchController) {
    guard let filter = searchController.searchBar.text, !filter.isEmpty else { return 
    }
    
    filteredVehicles = filteredVehicles.filter { $0.Plate!.lowercased().contains(filter.lowercased()) }
    monitorimiTableView.reloadData()
    }


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


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "monitorimiCell", for: indexPath) as? MyCustomCell1 else {return UITableViewCell()}
    let currentVechicle = listOfVechicle[indexPath.row]
    
    
    cell.veturaOutlet.text = currentVechicle.Plate
    cell.shpejtsiaAktualeOutlet.text = currentVechicle.Speed!
    cell.perditsuarOutlet.text = "\(currentVechicle.LastCommunicationDate!.convertToDisplayForm())"
    cell.pasagjereOutlet.text = "\(currentVechicle.Passengers!) Person/a"
    cell.vozitesiOutlet.text = "\(currentVechicle.Driver!)"

    
    return cell
  }
}

CodePudding user response:

You can use this:

var initialListOfVechicles: [Vehicles] = []
var vehicles: [Vehicles] = []

Keep one with all the data (initialListOfVechicles), and another one (vehicles) to manipulate the list (ie filterinig) and populate the tableView.

APICaller.shared.getVehicles(for: APICaller.shared.id!) {[weak self] (result) in
    guard let self = self else { return }
        
    switch result {
    case .success(let vehicle):
        self.initialListOfVechicles = vehicle
        self.vehicles = self.initialListOfVechicles
        ...
}



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


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "monitorimiCell", for: indexPath) as? MyCustomCell1 else {return UITableViewCell()}
    let currentVechicle = vehicles[indexPath.row]
    ...
}

And when you want to filter:

func updateSearchResults(for searchController: UISearchController) {
    guard let filter = searchController.searchBar.text, !filter.isEmpty else { 
        vehicles = initialListOfVechicles
        monitorimiTableView.reloadData()
        return
    }
    
    vehicles = initialListOfVechicles.filter { $0.Plate!.lowercased().contains(filter.lowercased()) }
    monitorimiTableView.reloadData()
}

Side note:
$0.Plate! this will causes a crash if Plate is nil. ie, why is it optional in the fist place?
It's recommended to name variable starting with a lowercase, so it should be plate instead of Plate. If it's from JSON, you can use CodingKeys to match them correctly (ie, plate is for key Plate), and use a keyDecodingStrategy.

  • Related