【问题标题】:Swift Using NSFetchedResultsController and UISearchBarDelegateSwift 使用 NSFetchedResultsController 和 UISearchBarDelegate
【发布时间】:2015-08-18 12:41:08
【问题描述】:

我正在寻找一个体面的解决方案来解决这个问题。我想在我拥有的 TableView 上实现一些简单的搜索功能。

我发现的所有示例要么使用已弃用的UISearchDisplayController,要么使用新的UISearchController,但不使用NSFetchedResultsController 目前这是使用 Core Data / NSFetchedResultsController 填充的

到目前为止,我已经成功地达到了可以收集用户搜索字符串的程度(哇!)。我知道我可能需要一个单独的FRC 来执行搜索,但如上所述,到目前为止所有尝试都失败了。

我的班级符合以下协议:

class JobListController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate, UISearchBarDelegate{

我不能使用UITableViewController,因为我已经编写了大量现有功能,这些功能依赖于这个类是UIViewController 我有我的两个IBOutlets

@IBOutlet var tblJobs : UITableView!
@IBOutlet weak var searchBar: UISearchBar!

还有我的空数组来保存我的各种Core Data 比特和鲍勃:

var workItems = [Work]()
var filteredWorkItems = [Work]()

这是我初始化我的FRCMOC 的方式,我已经在我的空的第二个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 函数中,我正在为我的表和 searchBar 设置委托/数据源:

  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,然后过滤结果正确地在视图中。

更新:

我尝试将搜索字符串添加到NSPredicate 中的FRC,如下所示:

lazy var fetchedResultsController: NSFetchedResultsController = {

   ../


    workFetchRequest.predicate = NSPredicate(format:"title contains[cd] %@", savedSearchTerm!)

   //...
    return frc
    }()

导致'JobListController.Type' does not have a member named 'savedSearchTerm'

在我的班级顶部,我这样设置:

var savedSearchTerm: NSString?

所以不确定我做错了什么?

【问题讨论】:

    标签: ios swift core-data uisearchbar


    【解决方案1】:

    从您的代码中,我假设您想使用 same 表格视图来显示结果。因此,您只需要使用基于搜索词的新过滤器来更新您的 FRC。

    将搜索词存储在变量中。在 FRC 工厂函数中,包含谓词,如下所示:

    request.predicate = searchText?.characters.count > 0 ?
     NSPredicate(format:"title contains[cd] %@", searchText!) : nil
    

    当文本改变时,重置 FRC 并重新加载。

    fetchedResultsController = nil
    tableView.reloadData()
    

    如果您有其他过滤器,例如范围按钮,请在谓词中添加其他术语。

    【讨论】:

    • 使用单独的表格视图有什么好处吗?我无法想象拥有一个单独的表格视图是如何工作的?
    • 通常它是一个有点透明的叠加表格视图。如果您使用的自定义单元格不显示原始表格视图中的丰富数据,而是仅显示正在搜索的文本,这将非常有用。
    • 我有几个问题,首先我无法从我的惰性 var fetchedResultsController 中访问我的 searchTerm 变量,我也无法将 fetchedResultsController 设置为 nil
    • 您不必将 NSFetchedResultsController 设置为 nil。使用:fetchedResultsController.fetchRequest.predicate 重置谓词。然后调用 self.fetchedResultsController.performFetch(&error) 重新查询。
    • 也许你想看看 UISearchController 看看它是否更符合你的喜好。您可以完全从代码中创建它。
    【解决方案2】:

    斯威夫特 4.2

    这是来自我的一个应用程序的有效解决方案。我已经对其进行了精简,以便于展示它的工作原理。

    我有一个包含大约 6000 行的数据库,我在其中通过 3 个不同的范围进行搜索:备注、作者和关键字。我在viewDidLoad 函数中使用默认值调用initializeFetchedResultsController。稍后当用户开始在“搜索”字段中输入时,再次使用所需的值开始调用它。

    Fetch 部分:

    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 方法中重新初始化获取结果控制器并每次重新加载表。

    // MARK: - Private instance methods
    
    private func notesSearched(_ text: String, _ scope: Int) {
        initializeFetchedResultsController(text, scope)
        tableView.reloadData()
    }
    

    虽然调用执行如此多的表重新加载可能不是最有效的方法,但它的速度很快,而且由于这会在用户键入时实时更新表,因此提供了出色的用户体验。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-29
      • 1970-01-01
      • 1970-01-01
      • 2017-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多