美文网首页
Core Data与SearchController

Core Data与SearchController

作者: Jiangyouhua | 来源:发表于2021-08-15 17:13 被阅读0次

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

内容概要

  • 首先,添加搜索功能;
  • 其次,了解搜索设置;
  • 最后,了解Predicate语法。

添加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的委托方法。

  • 首先,我们拓展ViewController类,并实现updateSearchResults方法;
  • 然后,我们建立一个Predicate(断言),使用Predicate语法,告诉这个断言的判断条件;
  • 最后将Predicate给予上一节创建的fetchedResultsController,让它基于该条件重新获取数据。
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的控件,我们可通过设置它要实现。

  • 首先,设置scopeButtonTitles。使它的值为一个包含Title和Info的数组,表示为搜索提供两个选项。至于对ID和全部内容的搜索,我们将在Predicate语法内再添加。
/// ViewController
    override func viewDidLoad() {
        ......
        searchController.searchBar.scopeButtonTitles = ["Title", "Info"]
        ......
   }
  • 其次,更改断言的判断条件。我们将PrediCate字符串中title,改为从Scope Bar获取内容,同时将断言部分独立出来,作为formatPredicate方法。
/// 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的设置

  • SearchController提供了一些Bool值供我们来设置,我们来看看。

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

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

Predicate的语法

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

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


  • 如果你还想看的话,我们需要拓展一下ViewTable显示数据的类型。
  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定义了一个方法。
  • 然后,我们在removeContext方法里调用,运行一次后删除。
/// 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
  • 逻辑运算符
    注意搜索框输入1为Bool类型的true。
/// logic. ==, !=, >=, <=, >, < &&, ||
NSPredicate(format: "index >= 5 || state == 1")
  • BETWEEN
    两者之间。
///  key BETWEEN {lower, upper}
NSPredicate(format: "index BETWEEN {1, 5})
  • IN
    被列举里的。
/// key IN {item, .... item_n}
NSPredicate(format: "index IN {1, 5})
  • LIKE
    精确匹配。
/// key LIKE 'Job'
NSPredicate(format: "title LIKE 'Job'",)
  • MATCHES
    正则匹配。

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

/// title MATCHES 'a+'
NSPredicate(format: "title MATCHES 'A*'")
  • BEGINSWITH
    匹配开头。
// key BEGINSWITH 'prefix'
NSPredicate(format: "title BEGINSWITH 'A'")
  • ENDSWITH
    匹配结尾。
/// key ENDSWITH 'suffix'
NSPredicate(format: "title ENDSWITH 'A'")
  • CONTAINS
    匹配包含的字符,我们一开始用的就是这种方式。

  • 参数c、d,

  1. CONTAINS[c],表示忽略大小写。
  2. CONTAINS[c],查询同读音的字符。

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

相关文章

网友评论

      本文标题:Core Data与SearchController

      本文链接:https://www.haomeiwen.com/subject/ibhmbltx.html