过去几天我一直在为使用Core Data的TableViewController在
Swift项目中创建一个过滤器而摔跤.我终于想通了我需要使用UISearchController,为searchController.searchBar创建一个NSPredicate等.
我找到了this post EXTREMELY helpful,但在完成这个项目后我的TVC建模后,我发现“所有的灯都亮着,但没有人在家”.我可以搜索,在searchBar中创建谓词,添加,删除等,但单元格不会更新以进行搜索.我错过了什么,但我不知道是什么.
以下是我的代码的相关部分.
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 } }
我认为我的问题在这一部分“存在”. Autocomplete一直是我的朋友,但我没有看到AutoComplete中引用的“searchIndex”.我想我错过了一些东西,但我不确定是什么或如何.
如果你已经做到这一点,感谢阅读. Here’s the GitHub repo为我正在研究的分支机构.
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() } }
编辑:每个@pbasdf,我正在添加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 }
问题出在tableView数据源方法中:numberOfSectionsInTableview:,tableView:numberOfRowsInSection:和tableView:cellForRowAtIndexPath:.如果searchPredicate不是nil,则需要每个方法返回不同的结果 – 就像你的tableView:commitEditingStyle:method一样.我会使filteredObjects成为一个实例属性(在类的开头定义),以便所有这些方法都可以访问它:
原文链接:https://www.f2er.com/swift/319443.htmlvar filteredObjects : [Note]? = nil
现在,当搜索文本更改时,您希望重建filteredObjects数组.因此,在updateSearchResultsForSearchController中,添加一行以根据新谓词重新计算它:
if let searchText = searchText { searchPredicate = NSPredicate(format: "noteBody contains[c] %@",searchText) filteredObjects = self.fetchedResultsController.fetchedObjects?.filter() { return self.searchPredicate!.evaluateWithObject($0) } as [Note]? self.tableView.reloadData() println(searchPredicate) }
我还建议(为简单起见),当您激活搜索时,结果将显示在一个部分中(否则您必须计算出过滤结果的多少部分 – 可能但很烦人):
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { if searchPredicate == nil { return self.fetchedResultsController.sections?.count ?? 0 } else { return 1 } }
接下来,如果searchPredicate不是nil,则该节中的行数将是filteredObjects的计数:
override func tableView(tableView: UITableView,numberOfRowsInSection section: Int) -> Int { if self.searchPredicate == nil { let sectionInfo = self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo return sectionInfo.numberOfObjects } else { return filteredObjects?.count ?? 0 } }
最后,则需要使用filteredObjects数据而不是fetchedResultsController构建单元格:
override func tableView(tableView: UITableView,forIndexPath: indexPath) as! UITableViewCell if searchPredicate == nil { self.configureCell(cell,atIndexPath: indexPath) return cell } else { // configure the cell based on filteredObjects data ... return cell } }
不知道你的细胞中有哪些标签,所以我留给你排序.