Od kilku dni zmagam się z tworzeniem filtru w projekcie Swift dla TableViewController
, który wykorzystuje Core Data. I w końcu zorientowali się, że trzeba użyć UISearchController
utwórz NSPredicate
dla searchController.searchBar
itpSwift UISearchController podłączony do Core Data Project, aplikacje działa, ale wyszukiwanie nie aktualizuje
znalazłem this post EXTREMELY helpful, ale po modelowania mój TVC
po tym projekcie, znaleźć „wszystkie światła są włączone, ale home niczyja” . Mogę wyszukiwać, predykat jest tworzony w searchBar
, dodawać, usuwać itd., Ale komórki nie aktualizują się w celu wyszukiwania. Brakuje mi czegoś, ale nie wiem co.
Oto odpowiednie fragmenty mojego kodu.
class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate, UISearchControllerDelegate, UISearchResultsUpdating
// Properties that get instantiated later
var detailViewController: DetailViewController? = nil
var addNoteViewController:AddNoteViewController? = nil // I added this
var managedObjectContext: NSManagedObjectContext? = nil
// Added variable for UISearchController
var searchController: UISearchController!
var searchPredicate: NSPredicate? // I added this. It's optional on and gets set later
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = self.editButtonItem()
if let split = self.splitViewController {
let controllers = split.viewControllers
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
self.detailViewController = controllers[controllers.count-1].topViewController as? DetailViewController
}
// UISearchController setup
searchController = UISearchController(searchResultsController: nil)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchResultsUpdater = self
searchController.searchBar.sizeToFit()
self.tableView.tableHeaderView = searchController?.searchBar
self.tableView.delegate = self
self.definesPresentationContext = true
}
// MARK: - UISearchResultsUpdating Delegate Method
// Called when the search bar's text or scope has changed or when the search bar becomes first responder.
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchText = self.searchController?.searchBar.text // steve put breakpoint
println(searchController.searchBar.text)
if let searchText = searchText {
searchPredicate = NSPredicate(format: "noteBody contains[c] %@", searchText)
self.tableView.reloadData()
println(searchPredicate)
}
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
var note: Note
if searchPredicate == nil {
note = self.fetchedResultsController.objectAtIndexPath(indexPath) as! Note
} else {
let filteredObjects = self.fetchedResultsController.fetchedObjects?.filter() {
return self.searchPredicate!.evaluateWithObject($0)
}
note = filteredObjects![indexPath.row] as! Note
}
let context = self.fetchedResultsController.managedObjectContext
context.deleteObject(note)
var error: NSError? = nil
if !context.save(&error) {
abort()
}
}
}
// MARK: - Fetched results controller
var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("Note", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "noteTitle", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptor]
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !_fetchedResultsController!.performFetch(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
println("Unresolved error \(error), \(error?.userInfo)")
abort()
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil
func controllerWillChangeContent(controller: NSFetchedResultsController) {
// self.tableView.beginUpdates() // ** original code, change if doesn't work** steve put breakpoint here
// ANSWER said this section is redundant, but keeping it b/c it doesn't crash
if searchPredicate == nil {
tableView.beginUpdates()
} else {
(searchController.searchResultsUpdater as! MasterViewController).tableView.beginUpdates()
}
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
var tableView = UITableView()
if searchPredicate == nil {
tableView = self.tableView
} else {
tableView = (searchController.searchResultsUpdater as! MasterViewController).tableView
}
switch type {
case .Insert:
self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
case .Delete:
self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
default:
return
}
}
Myślę, że mój problem "żyje" tu, w tej sekcji. Autouzupełnianie jest moim przyjacielem, ale nie widzę "searchIndex
", o którym mowa w autouzupełnianiu. Myślę, że czegoś brakuje, ale nie jestem pewien, co i jak.
Jeśli dotarłeś tak daleko, dziękuję za przeczytanie. Here's the GitHub repo dla oddziału, nad którym pracuję.
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
var tableView = UITableView()
if self.searchPredicate == nil {
tableView = self.tableView
} else {
tableView = (self.searchController.searchResultsUpdater as! MasterViewController).tableView
}
switch type {
case .Insert:
println("*** NSFetchedResultsChangeInsert (object)")
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
case .Delete:
println("*** NSFetchedResultsChangeDelete (object)")
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
case .Update:
println("*** NSFetchedResultsChangeUpdate (object)")
// TODO: need fix this
// ORIGINAL CODE
// self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, atIndexPath: indexPath!) // original code
// PROSPECTIVE SOLUTION CODE
println("*** NSFetchedResultsChangeUpdate (object)")
if searchPredicate == nil {
self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, atIndexPath: indexPath!) // original code
} else {
// Should search the do something w/ the UISearchControllerDelegate or UISearchResultsUpdating
// Instead of "indexPath", it should be "searchIndexPath"--How?
let cell = tableView.cellForRowAtIndexPath(searchIndexPath) as LocationCell // My cell is a vanilla cell, not a xib
let location = controller.objectAtIndexPath(searchIndexPath) as Location // My object is a "Note"
cell.configureForLocation(location) // This is from the other guy's code, don't think it's applicable to me
}
case .Move:
println("*** NSFetchedResultsChangeMove (object)")
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
default:
return
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
if self.searchPredicate == nil {
self.tableView.endUpdates()
} else {
println("controllerDidChangeContent")
(self.searchController.searchResultsUpdater as! MasterViewController).tableView.endUpdates()
}
}
Edit: Per @pbasdf, Dodaję metody Tableview.
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.fetchedResultsController.sections?.count ?? 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.searchPredicate == nil {
let sectionInfo = self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
}
let sectionInfo = self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
self.configureCell(cell, atIndexPath: indexPath)
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
Czy delegat wyszukiwarki SearchController jest ustawiony na własny? W 'viewDidLoad':' searchController.delegate = self' i 'searchController.searchResultsUpdater = self' – Ian
To nic nie dało, ale aplikacja nie zawiesza się po uruchomieniu.Mój mózg jest smażony na cały dzień, ale mam zamiar wykopać się i zobaczyć, czy brakuje mi metody delegatów gdzieś, co powstrzymuje ją od aktualizacji. Dziękuję za patrzenie. – Adrian
Link do repozytorium GitHub jest zepsuty. Ale możesz zamiast tego dodać metody tableView dataSource (numberOfSectionsInTableview, numberOfRowsInSection, cellForRowAtIndexPath, itp.) Do swojego posta. Dzięki. – pbasdf