Swift 使用 NSFetchedResultsController 和 UISearchBarDelegate
Swift Using NSFetchedResultsController and UISearchBarDelegate
我正在寻找解决此问题的合适方法。我想在我拥有的 TableView 上实现一些简单的搜索功能。
我发现的所有示例要么使用已弃用的 UISearchDisplayController
,要么使用新的 UISearchController
但不使用 NSFetchedResultsController
目前这是使用 Core Data
/ NSFetchedResultsController
填充的
到目前为止,我已经成功地收集了用户的搜索字符串(哇!)。我知道我可能需要一个单独的 FRC
来执行搜索,但如上所述,到目前为止所有尝试都失败了。
我的 class 符合以下协议:
class JobListController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate, UISearchBarDelegate{
我不能使用 UITableViewController
,因为我已经编写了大量依赖此 class 作为 UIViewController
的现有功能
我有两个 IBOutlets
:
@IBOutlet var tblJobs : UITableView!
@IBOutlet weak var searchBar: UISearchBar!
和我的空数组来保存我的各种 Core Data
零碎东西:
var workItems = [Work]()
var filteredWorkItems = [Work]()
这是我初始化我的 FRC
以及我的 MOC
的方式,我已经离开了空的第二个 FRC
,因为我很确定它会在某点:
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
lazy var fetchedResultsController: NSFetchedResultsController = {
let workFetchRequest = NSFetchRequest(entityName: "Work")
let primarySortDescriptor = NSSortDescriptor(key: "createdDate", ascending: true)
let secondarySortDescriptor = NSSortDescriptor(key: "town", ascending: true)
workFetchRequest.sortDescriptors = [primarySortDescriptor, secondarySortDescriptor]
let frc = NSFetchedResultsController(
fetchRequest: workFetchRequest,
managedObjectContext: self.managedObjectContext!,
sectionNameKeyPath: "createdDate",
cacheName: nil)
frc.delegate = self
return frc
}()
var searchResultsController: NSFetchedResultsController?
在我的 viewDidLoad
函数中,我正在为我的 table 和搜索栏设置委托/数据源:
tblJobs.delegate = self
tblJobs.dataSource = self
searchBar.delegate = self
这里是 searchBar
函数,这是我要做的。 stringMatch
变量是上一次尝试遗留下来的,我希望能够在这里通过多种不同的参数进行搜索,但如果我能只使用一个参数,那将是一个坚实的开始。
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
println("Search text is \(searchText)")
self.filteredWorkItems = self.workItems.filter({( work: Work) -> Bool in
//
let stringMatch = work.postcode.rangeOfString(searchText)
return stringMatch != nil
})
if(filteredWorkItems.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.tblJobs.reloadData()
}
这是我的 cellForRowAtIndexPath
函数,展示了我如何从 fetchedResultsController
中提取数据
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = self.tblJobs.dequeueReusableCellWithIdentifier(
"JobCell", forIndexPath: indexPath)
as! JobTableViewCell
let workItem = fetchedResultsController.objectAtIndexPath(indexPath) as! Work
//...
return cell
}
所以你可以看到我在这里发生了一些事情,最终我想弄清楚我如何使用我新获得的 searchText
字符串来查询我的 FRC
,并且然后在视图中正确过滤结果。
更新:
我试图将搜索字符串添加到 FRC
中的 NSPredicate
,如下所示:
lazy var fetchedResultsController: NSFetchedResultsController = {
../
workFetchRequest.predicate = NSPredicate(format:"title contains[cd] %@", savedSearchTerm!)
//...
return frc
}()
这导致 'JobListController.Type' does not have a member named 'savedSearchTerm'
在我的 class 顶部,我是这样设置的:
var savedSearchTerm: NSString?
所以不确定我做错了什么?
根据您的代码,我假设您想要使用 same table 视图来显示结果。因此,您只需要使用基于搜索词的新过滤器更新您的 FRC。
将搜索词存储在变量中。在 FRC 工厂函数中,包含谓词,如下所示:
request.predicate = searchText?.characters.count > 0 ?
NSPredicate(format:"title contains[cd] %@", searchText!) : nil
当文本更改时,重置 FRC 并重新加载。
fetchedResultsController = nil
tableView.reloadData()
如果您有其他过滤器(例如范围按钮),请向谓词添加其他术语。
Swift 4.2
这是我的一个应用程序的有效解决方案。我已对其进行了缩减,以便于展示其工作原理。
我有一个大约 6000 行的数据库,我在其中通过 3 个不同的范围进行搜索:注释、作者和关键字。我使用默认值在 viewDidLoad
函数中调用 initializeFetchedResultsController
。稍后,当用户开始在“搜索”字段中键入内容时,开始使用所需的值再次调用它。
获取部分:
let EMPTY_STRING = "" // I don't like string literals in my code so define them as static variables separately
// Giving two default values
func initializeFetchedResultsController(_ text: String: EMPTY_STRING, _ scope: Int = 0) {
fetchRequest.sortDescriptors = [NSSortDescriptor(key: NotesAttributes.author.rawValue, ascending: true)]
if searchedStringTemp != EMPTY_STRING { // Whatever conditions you want to pass on
let p0 = NSPredicate(format: NotesAttributes.scope.rawValue + " != \(scope)")
let p1 = NSPredicate(format: "\(column) CONTAINS[cd] %@", "\(text)")
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p0, p1])
}
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: AppDelegate().sharedInstance().persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
do {
try self.fetchedResultsController.performFetch()
} catch {
let fetchError = error as NSError
print("Error 12312: Unable to Perform Fetch Request")
print("\(fetchError), \(fetchError.localizedDescription)")
}
}
搜索控制器:
// MARK: - UISearchBar Delegate
extension AllNotesVC: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
// This is my function which I call to start search
notesSearched(searchBar.text!, searchBar.scopeButtonTitles![selectedScope])
}
}
// MARK: - UISearchResultsUpdating Delegate
extension AllNotesVC: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let text = searchController.searchBar.text!
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
// This is my function which I call to start search
notesSearched(text, scope)
}
}
这在我的 notesSearched
方法中,每次重新初始化获取结果控制器并重新加载 table。
// MARK: - Private instance methods
private func notesSearched(_ text: String, _ scope: Int) {
initializeFetchedResultsController(text, scope)
tableView.reloadData()
}
虽然调用如此多次 table 重新加载可能不是执行此操作的最有效方法,但它非常快,并且由于这会根据用户的需要实时更新 table输入它可以提供美妙的用户体验。
我正在寻找解决此问题的合适方法。我想在我拥有的 TableView 上实现一些简单的搜索功能。
我发现的所有示例要么使用已弃用的 UISearchDisplayController
,要么使用新的 UISearchController
但不使用 NSFetchedResultsController
目前这是使用 Core Data
/ NSFetchedResultsController
到目前为止,我已经成功地收集了用户的搜索字符串(哇!)。我知道我可能需要一个单独的 FRC
来执行搜索,但如上所述,到目前为止所有尝试都失败了。
我的 class 符合以下协议:
class JobListController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate, UISearchBarDelegate{
我不能使用 UITableViewController
,因为我已经编写了大量依赖此 class 作为 UIViewController
的现有功能
我有两个 IBOutlets
:
@IBOutlet var tblJobs : UITableView!
@IBOutlet weak var searchBar: UISearchBar!
和我的空数组来保存我的各种 Core Data
零碎东西:
var workItems = [Work]()
var filteredWorkItems = [Work]()
这是我初始化我的 FRC
以及我的 MOC
的方式,我已经离开了空的第二个 FRC
,因为我很确定它会在某点:
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
lazy var fetchedResultsController: NSFetchedResultsController = {
let workFetchRequest = NSFetchRequest(entityName: "Work")
let primarySortDescriptor = NSSortDescriptor(key: "createdDate", ascending: true)
let secondarySortDescriptor = NSSortDescriptor(key: "town", ascending: true)
workFetchRequest.sortDescriptors = [primarySortDescriptor, secondarySortDescriptor]
let frc = NSFetchedResultsController(
fetchRequest: workFetchRequest,
managedObjectContext: self.managedObjectContext!,
sectionNameKeyPath: "createdDate",
cacheName: nil)
frc.delegate = self
return frc
}()
var searchResultsController: NSFetchedResultsController?
在我的 viewDidLoad
函数中,我正在为我的 table 和搜索栏设置委托/数据源:
tblJobs.delegate = self
tblJobs.dataSource = self
searchBar.delegate = self
这里是 searchBar
函数,这是我要做的。 stringMatch
变量是上一次尝试遗留下来的,我希望能够在这里通过多种不同的参数进行搜索,但如果我能只使用一个参数,那将是一个坚实的开始。
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
println("Search text is \(searchText)")
self.filteredWorkItems = self.workItems.filter({( work: Work) -> Bool in
//
let stringMatch = work.postcode.rangeOfString(searchText)
return stringMatch != nil
})
if(filteredWorkItems.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.tblJobs.reloadData()
}
这是我的 cellForRowAtIndexPath
函数,展示了我如何从 fetchedResultsController
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = self.tblJobs.dequeueReusableCellWithIdentifier(
"JobCell", forIndexPath: indexPath)
as! JobTableViewCell
let workItem = fetchedResultsController.objectAtIndexPath(indexPath) as! Work
//...
return cell
}
所以你可以看到我在这里发生了一些事情,最终我想弄清楚我如何使用我新获得的 searchText
字符串来查询我的 FRC
,并且然后在视图中正确过滤结果。
更新:
我试图将搜索字符串添加到 FRC
中的 NSPredicate
,如下所示:
lazy var fetchedResultsController: NSFetchedResultsController = {
../
workFetchRequest.predicate = NSPredicate(format:"title contains[cd] %@", savedSearchTerm!)
//...
return frc
}()
这导致 'JobListController.Type' does not have a member named 'savedSearchTerm'
在我的 class 顶部,我是这样设置的:
var savedSearchTerm: NSString?
所以不确定我做错了什么?
根据您的代码,我假设您想要使用 same table 视图来显示结果。因此,您只需要使用基于搜索词的新过滤器更新您的 FRC。
将搜索词存储在变量中。在 FRC 工厂函数中,包含谓词,如下所示:
request.predicate = searchText?.characters.count > 0 ?
NSPredicate(format:"title contains[cd] %@", searchText!) : nil
当文本更改时,重置 FRC 并重新加载。
fetchedResultsController = nil
tableView.reloadData()
如果您有其他过滤器(例如范围按钮),请向谓词添加其他术语。
Swift 4.2
这是我的一个应用程序的有效解决方案。我已对其进行了缩减,以便于展示其工作原理。
我有一个大约 6000 行的数据库,我在其中通过 3 个不同的范围进行搜索:注释、作者和关键字。我使用默认值在 viewDidLoad
函数中调用 initializeFetchedResultsController
。稍后,当用户开始在“搜索”字段中键入内容时,开始使用所需的值再次调用它。
获取部分:
let EMPTY_STRING = "" // I don't like string literals in my code so define them as static variables separately
// Giving two default values
func initializeFetchedResultsController(_ text: String: EMPTY_STRING, _ scope: Int = 0) {
fetchRequest.sortDescriptors = [NSSortDescriptor(key: NotesAttributes.author.rawValue, ascending: true)]
if searchedStringTemp != EMPTY_STRING { // Whatever conditions you want to pass on
let p0 = NSPredicate(format: NotesAttributes.scope.rawValue + " != \(scope)")
let p1 = NSPredicate(format: "\(column) CONTAINS[cd] %@", "\(text)")
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p0, p1])
}
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: AppDelegate().sharedInstance().persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
do {
try self.fetchedResultsController.performFetch()
} catch {
let fetchError = error as NSError
print("Error 12312: Unable to Perform Fetch Request")
print("\(fetchError), \(fetchError.localizedDescription)")
}
}
搜索控制器:
// MARK: - UISearchBar Delegate
extension AllNotesVC: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
// This is my function which I call to start search
notesSearched(searchBar.text!, searchBar.scopeButtonTitles![selectedScope])
}
}
// MARK: - UISearchResultsUpdating Delegate
extension AllNotesVC: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let text = searchController.searchBar.text!
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
// This is my function which I call to start search
notesSearched(text, scope)
}
}
这在我的 notesSearched
方法中,每次重新初始化获取结果控制器并重新加载 table。
// MARK: - Private instance methods
private func notesSearched(_ text: String, _ scope: Int) {
initializeFetchedResultsController(text, scope)
tableView.reloadData()
}
虽然调用如此多次 table 重新加载可能不是执行此操作的最有效方法,但它非常快,并且由于这会根据用户的需要实时更新 table输入它可以提供美妙的用户体验。