Refactored to vanilla UIViewController
Rather than having the UITableView
toggle between active and inactive UISearchController
, I refactored to a vanilla UIViewController
with a UISearchBar
at the top and a UITableView
directly underneath it.
What I'm trying to do is toggle the fetchedResultsController
between having a predicate which is set by the text in the searchBar and one that grabs everything.
I found this answer, which describes a solution to a similar problem in Objective-C and served as the basis upon which I refactored my Swift project:
How to filter NSFetchedResultsController (CoreData) with UISearchDisplayController/UISearchBar https://stackoverflow.com/a/9512891/4475605
My Problem: How do I update my fetchedResultsController for the searchBar
text?
The tableView
populates correctly, but I can't figure out how to update the fetchedResultsController
for my searchPredicate
This is what I've tried.
Here's how I'm declaring my NSPredicate
& NSFetchedResultsController
at the top of the class:
var searchPredicate = NSPredicate()
// Create fetchedResultsController to store search results
lazy var fetchedResultsController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest(entityName: "Song")
let sortDescriptor = NSSortDescriptor(key: "songDescription", ascending: true);
// ** THESE TWO LINES CAUSE A CRASH, BUT NO COMPILER ERRORS **
// let predicate = self.searchPredicate
// fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchBatchSize = 20
let frc = NSFetchedResultsController (fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: "songDescription", cacheName: nil)
frc.delegate = self
return frc
}()
Here are my UISearchBarDelegate
methods:
// MARK: - UISearchBarDelegate methods
// called when text changes (including clear)
internal func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
print("searchText = \(searchBar.text)")
searchPredicate = NSPredicate(format: "(songDescription contains [cd] %@) || (songStar contains[cd] %@)", searchBar.text!, searchBar.text!)
// TODO: figure out how to update fetchedResultsController w/ predicate
}
// called when cancel button pressed
internal func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchBar.resignFirstResponder()
searchBar.text = ""
// TODO: flip back to normal fetchResultsController
}
Here are my NSFetchedResultsControllerDelegate
methods
internal func controllerWillChangeContent(controller: NSFetchedResultsController) {
print("controllerWillChangeContent")
tableView.beginUpdates()
}
internal func controllerDidChangeContent(controller: NSFetchedResultsController) {
print("controllerDidChangeContent")
tableView.reloadData()
tableView.endUpdates()
}
internal func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)
case .Delete:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
case .Update:
tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
case .Move:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
tableView.insertRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
}
}
Thank you for reading. I welcome your suggestions.
This is not-so-swift-pseudo-code... it most assuredly will not compile with any swift compiler.
Your FRC is not using a cache, so there is no cache to be deleted, and we can just assign the predicate to the existing FRC fetch request.
You may not want to do a complete new fetch on every character change in the search bar, or you may want to only do the search on the first character, and use subsets for subsequent characters.
// called when text changes (including clear)
internal func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
var predicate:NSPredicate = nil
if searchBar.text.length != 0 {
predicate = NSPredicate(format: "(songDescription contains [cd] %@) || (songStar contains[cd] %@)", searchBar.text!, searchBar.text!)
}
fetchedResultsController.fetchRequest.predicate = predicate
fetchedResultsController.performFetch()
tableView.reloadData()
}
// called when cancel button pressed
internal func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchBar.resignFirstResponder()
searchBar.text = ""
fetchedResultsController.fetchRequest.predicate = nil
fetchedResultsController.performFetch()
tableView.reloadData()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With