I have a UISearchController that is configured to search a very large array of data. Thus, when I type a search string, it takes a very long time to enter my search. It compares the search with all the characters in the search field, which is very slow.
I am wondering how to fix this. My thoughts:
- performs a search only after the user completes the entry and presses the search button.
- perform a search in the background thread, freeing up the main thread for keyboard input.
I would like to use method 1 but I cannot figure out how to do this with the new UISearchController.
Below is my relevant project code:
class AirportSearchTBVC: UITableViewController, UISearchResultsUpdating{ var airportData = [Dictionary<String, String>]() var filteredData = [Dictionary<String, String>]() var searchController = UISearchController() override func viewDidLoad() { super.viewDidLoad() searchController = UISearchController(searchResultsController: nil) searchController.searchResultsUpdater = self searchController.dimsBackgroundDuringPresentation = false searchController.searchBar.sizeToFit() tableView.tableHeaderView = searchController.searchBar definesPresentationContext = true searchController.hidesNavigationBarDuringPresentation = false self.tableView.reloadData() } func updateSearchResultsForSearchController(searchController: UISearchController) { filteredData = [] let searchPredicate = NSPredicate(format: "SELF CONTAINS[cd] %@", searchController.searchBar.text!) let array = (airportData as NSArray).filteredArrayUsingPredicate(searchPredicate) filteredData = array as! [Dictionary<String, String>] self.tableView.reloadData() }
Bonus question If I search for a string, it does not seem to return any results if the string does not fit perfectly. For example: "orida" does not find "Florida." Shouldn't my search predicate find this with CONTAINS?
Update This code almost works, but it basically throws a bunch of stuff into the background thread and then iterates over it. Now the keyboard is lively, but it seems to crash if I quickly change things in the text box, rejecting and entering the search bar again ...
func updateSearchResultsForSearchController(searchController: UISearchController) { appDel.backgroundThread(background: { self.filteredData.removeAll(keepCapacity: false) let searchPredicate = NSPredicate(format: "SELF CONTAINS[cd] %@", searchController.searchBar.text!) let array = (self.airportData as NSArray).filteredArrayUsingPredicate(searchPredicate) self.filteredData = array as! [Dictionary<String, String>] }, completion: { dispatch_async(dispatch_get_main_queue()) { self.tableView.reloadData() } }); }
Update 2 After playing a little with this, I was able to make it work pretty well, expecting the searchBar.text> = 3 line to look, and also make sure that the number of characters has not changed in 1 second from updateSearchResultsForSearchController: called. The combination of these functions, as well as the integration of the search button execution command, should solve my problem.
func updateSearchResultsForSearchController(searchController: UISearchController) { let startCount = searchController.searchBar.text!.length delay(1) { if searchController.searchBar.text!.length >= 3 && searchController.searchBar.text!.length == startCount{ self.view.addSubview(self.progressHud) self.appDel.backgroundThread(background: { self.filteredData.removeAll(keepCapacity: false) let searchPredicate = NSPredicate(format: "SELF CONTAINS[cd] %@", searchController.searchBar.text!) let array = (self.airportData as NSArray).filteredArrayUsingPredicate(searchPredicate) self.filteredData = array as! [Dictionary<String, String>] }, completion: { dispatch_async(dispatch_get_main_queue()) { self.tableView.reloadData() self.progressHud.removeFromSuperview() } }); } } } func delay(delay:Double, closure:()->()) { dispatch_after( dispatch_time( DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)) ), dispatch_get_main_queue(), closure) }