Reputation: 1463
I have a UISearchBar thats hooked up to a table view thats populated by Core Data. I've been having a lot of trouble getting filtering to work. Most tutorials on this are extremely old and haven't worked for me. I was thinking about converting the entity to an array and doing the filtering on the array but I've read that that's inefficient. I'm thinking I should use NSPredicate, but I honestly don't know what to do. Any ideas? Thanks.
Upvotes: 2
Views: 3073
Reputation: 12768
I've actually been working on a demo of this and I have just put it up on GitHub. It can be found here. As far as the implementation of it goes, you have to set up UITableViewController
, NSFetchedResultsController
, and UISearchDisplayDelegate
for your class:
class ContactsViewController: UITableViewController, NSFetchedResultsController, UISearchDisplayDelegate {
}
And with this there will be two table views:
self.tableView
)
searchDisplayController?.searchResultsTableView
)
You also make class variables for fetchedResultsController
, searchResultsController
, appDelegate
, and managedObjectContext
:
let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
var managedObjectContext: NSManagedObjectContext? {
get {
if let delegate = appDelegate {
return delegate.managedObjectContext
}
return nil
}
}
var fetchedResultsController: NSFetchedResultsController?
var searchResultsController: NSFetchedResultsController?
In the viewDidLoad()
you must register your cell for your searchResultsTableView
because it does not exist in interface:
searchDisplayController?.searchResultsTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "CellID")
It is also here that you setup your fetchRequest:
fetchedResultsController = NSFetchedResultsController(fetchRequest: someFetchRequest, managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController?.delegate = self
fetchedResultsController?.performFetch(nil)
You will need a function that returns which FRC you will need depending on the tableView. You make this function return a NSFetchedResultsController
and you will use this in all of the table view functions where you pull data from the FRC such as cellForRowAtIndexPath:
because it will get you the correct set of data
func fetchedResultsControllerForTableView(tableView: UITableView) -> NSFetchedResultsController? {
return tableView == searchDisplayController?.searchResultsTableView ? searchResultsController? : fetchedResultsController?
}
Finally, you need to implement searchDisplayControllerWillUnloadSearchResults
and searchDisplayControllerShouldReloadTableForSearchString
:
func searchDisplayController(controller: UISearchDisplayController, willUnloadSearchResultsTableView tableView: UITableView) {
searchResultsController?.delegate = nil
searchResultsController = nil
}
func searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
let firstNamePredicate = NSPredicate(format: "nameFirst CONTAINS[cd] %@", searchString.lowercaseString)
let lastNamePredicate = NSPredicate(format: "nameLast CONTAINS[cd] %@", searchString.lowercaseString)
let predicate = NSCompoundPredicate.orPredicateWithSubpredicates([firstNamePredicate!, lastNamePredicate!])
searchResultsController = NSFetchedResultsController(fetchRequest: someOtherFetchRequestWithPredicate(predicate), managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
searchResultsController?.performFetch(nil)
return true
}
If you have any trouble filling in the other stuff, such as the data model or the NSFetchedResultsControllerDelegate methods, check out the demo!
Upvotes: 2