Core Data与SearchController

2021-08-15  本文已影响0人  Jiangyouhua

Hi, 大家好,我是姜友华。
CoreData不仅与TableView的Data Source相匹配,它同时与SearchController的Predicate相匹配,这一特征能让我们非常便捷的将搜索功能加入其中。好,我们继续上一节,为App添加搜索功能。

内容概要


添加SearchController

我们打开ViewController.swift文件,通过下面代码为本例添加搜索框。

/// viewController
    override func viewDidLoad() {
        super.viewDidLoad()
        ......
        let searchController = UISearchController(searchResultsController: nil)
        searchController.obscuresBackgroundDuringPresentation = false
        searchController.searchResultsUpdater = self
        self.navigationItem.searchController = searchController
    }

代码的内容是:

  1. 我们使用了一个SearchController;
  2. 设置它在输入搜索内容时不加深列表内容;
  3. 指定它的结果更新器的委托为当前类;
  4. 将searchController作为导航项的searchController。

searchController呈现的效果,后面会图示。

实现搜索功能

我们需要实现searchResultsUpdater的委托方法。

extension ViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        let predicate: NSPredicate
        if let userInput = searchController.searchBar.text, !userInput.isEmpty {
            predicate = NSPredicate(format: "title CONTAINS[c] %@", userInput)
        } else {
            predicate = NSPredicate(value:  true)
        }
        fetchedResultsController.fetchRequest.predicate = predicate
        do {
            try fetchedResultsController.performFetch()
        } catch {
            fatalError("\(error)")
        }
        tableView.reloadData()
    }
}
运行得到的效果如下: Screen Shot 2021-08-16 at 10.25.23.png

不止于搜索标题内容。

一开始我们实现对Title的搜索。现在,我们需要前进一步,来实现基Title、Info、ID甚至全部的内容进行搜索。UISearchController为这种需要求提供了一个叫Scope Button的控件,我们可通过设置它要实现。

/// ViewController
    override func viewDidLoad() {
        ......
        searchController.searchBar.scopeButtonTitles = ["Title", "Info"]
        ......
   }
/// ViewController
extension ViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        let predicate = formatPredicate(searchController: searchController)
        fetchedResultsController.fetchRequest.predicate = predicate
        do {
            try fetchedResultsController.performFetch()
        } catch {
            fatalError("\(error)")
        }
        tableView.reloadData()
    }
    
    private func formatPredicate(searchController: UISearchController) -> NSPredicate {
        guard let userInput = searchController.searchBar.text, !userInput.isEmpty else {
            return NSPredicate(value:  true)
        }
        let index = searchController.searchBar.selectedScopeButtonIndex
        guard let scopeTitle = searchController.searchBar.scopeButtonTitles?[index] else {
            fatalError("Error getting title of Scope Button.")
        }
    
        return  NSPredicate(format: "\(scopeTitle.lowercased()) CONTAINS[c] %@", userInput)
    }
}
我们搜索一个title不包含的字符串试试,结果达到了我们的需求: Screen Shot 2021-08-16 at 12.21.39.png

我们再来深入了解一下。

SearchController的设置

由于我们在初始UISearchController(searchResultsController: nil),结果控制器设置为空,所以最后两项你在本例中无法呈现。

 // 搜索时,列表区是否变暗,默认:是。
 searchController.obscuresBackgroundDuringPresentation = true
 // 搜索时,导航栏其它元素是否隐藏,默认:是。
 searchController.hidesNavigationBarDuringPresentation = true
 // 搜索框有内容时,是否显示框后的取消的文字按钮,默认:是。
 searchController.automaticallyShowsCancelButton = true
 // 搜索时,是否显示搜索栏范围栏,默认:是。
 searchController.automaticallyShowsScopeBar = true
 // 是否管理结果控制器的可见性,默认:否。
 searchController.automaticallyShowsSearchResultsController = false
 // 搜索时,是否显示搜索结果控制器,默认:否。
 searchController.showsSearchResultsController = false

Predicate的语法

折腾了半才发现这个,你可以收藏一下是Objective-C的:

官网地址,到这里后面可以不看了。


  1. 我们为StudyEntity添加两个字段:index: Integer32, state: Boolean。
  2. Build一下,应用上这两个字段。
/// AppDelegate
func initializationData() {
        ......
        for i in 0...20 {
            let studyEntity = StudyEntity(context: context)
            ......
            studyEntity.index = Int32(i)
            studyEntity.state = i % 2 == 0
        }
       ......
    }
  1. 将这新加的属性值,添加到TableViewCell的info里。
/// ViewController
extension ViewController: UITableViewDataSource {
    ......
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        ......
        cell.info.text = "\(studyEntity.index). \(studyEntity.state), " + (studyEntity.info ?? "")
        ......
    }
}
  1. 修改搜索范围,将全部All, index, state加入。
searchController.searchBar.scopeButtonTitles = ["All", "Title", "Info", "Index", "State"]
  1. 删除现有数,重新初始化数据,原数据里index, state没有赋值。
/// Appdelegate
func removeContext() {
       let context = persistentContainer.viewContext
       if let result = try? context.fetch(StudyEntity.fetchRequest()) {
           for object in result {
               context.delete(object as! NSManagedObject)
           }
       }
   }
   
   func removeContext() {
       removeContext()
       ......
   }
最后,运行后显示如下图: Screen Shot 2021-08-16 at 14.43.26.png
/// logic. ==, !=, >=, <=, >, < &&, ||
NSPredicate(format: "index >= 5 || state == 1")
///  key BETWEEN {lower, upper}
NSPredicate(format: "index BETWEEN {1, 5})
/// key IN {item, .... item_n}
NSPredicate(format: "index IN {1, 5})
/// key LIKE 'Job'
NSPredicate(format: "title LIKE 'Job'",)

请留意,使用MATCHES,在本例未测试成功。

/// title MATCHES 'a+'
NSPredicate(format: "title MATCHES 'A*'")
// key BEGINSWITH 'prefix'
NSPredicate(format: "title BEGINSWITH 'A'")
/// key ENDSWITH 'suffix'
NSPredicate(format: "title ENDSWITH 'A'")
  1. CONTAINS[c],表示忽略大小写。
  2. CONTAINS[c],查询同读音的字符。

在这里,就讲这几个常用的吧。
我是姜友华,下次再见。

上一篇 下一篇

猜你喜欢

热点阅读