如何过滤在 Core Data 中有对象的表视图?

Posted

技术标签:

【中文标题】如何过滤在 Core Data 中有对象的表视图?【英文标题】:How can I filter my tableview that has objects in Core Data? 【发布时间】:2017-07-30 03:05:27 【问题描述】:

我正在创建一个联系人应用程序,到目前为止,我已经成功地将项目保存到我的 tableview 中。我有一个搜索栏,我想按名字过滤掉我的单元格,我知道因为我在核心数据中工作,所以我必须使用 fetchResultsController 和 NSPredicate。我很难弄清楚这些东西,也许有人可以帮助我?

这也是我的核心数据实体,以防万一。

实体:联系人

属性:

名字,字符串 姓氏,字符串 出生日期,字符串 电话号码,字符串 邮政编码,字符串

我知道有些代码可能不完整,但我只需要指导如何使用它。我只想让用户输入一个名字,它会按名字过滤单元格。如果您需要更多信息,请告诉我。

现在这是我的 ContactsTableVC 中的代码:

import UIKit
import CoreData

class ContactsTableVC: UITableViewController, UISearchBarDelegate, NSFetchedResultsControllerDelegate 

    @IBOutlet weak var searchBar: UISearchBar!

    var isFiltered: Bool = false

//Holds the core data model
    var persons: [Person] = []

    override func viewDidLoad() 
        super.viewDidLoad()

        searchBar.delegate = self

        self.tableView.separatorStyle = UITableViewCellSeparatorStyle.none
        self.tableView.backgroundColor = UIColor(red: 240/255.0, green: 240/255.0, blue: 240/255.0, alpha: 1.0)

        fetch()
        self.tableView.reloadData()
    

    func getContext () -> NSManagedObjectContext 
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        return appDelegate.persistentContainer.viewContext
    

    // MARK: - Searchbar
    //add fetchrequest to did ebgin editing
    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) 

    

    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) 
        if(searchBar.text == "") 
            isFiltered = false
         else 
            isFiltered = true
        
    

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) 
        filter(text: searchText)
    

    // MARK: - Fetchresults controller / filtering data

    func filter(text: String) 
        //Create fetch request
        let fetchRequest = NSFetchRequest<Person>()

//        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else  return          replaced with getcontext
//        let managedObjectContext = appDelegate.persistentContainer.viewContext

        let entity = NSEntityDescription.entity(forEntityName: "Contact", in: getContext())
        fetchRequest.entity = entity

        let sortDescriptor = NSSortDescriptor(key: "firstName", ascending: false)
        let sortDescriptors: [Any] = [sortDescriptor]
        fetchRequest.sortDescriptors = sortDescriptors as? [NSSortDescriptor] ?? [NSSortDescriptor]()

        if(text.characters.count > 0) 
            let predicate = NSPredicate(format: "(firstName CONTAINS[c] %@)", text)
            fetchRequest.predicate = predicate
        

        let loadedEntities: [Person]? = try? getContext().fetch(fetchRequest)
        filteredContacts = [Any](arrayLiteral: loadedEntities) as! [Person]
        self.tableView.reloadData()
    

    // MARK: - Data Source
    func fetch() 
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else  return 
        let managedObjectContext = appDelegate.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:"Contact")
        do 
            persons = try managedObjectContext.fetch(fetchRequest) as! [Person] //NSManagedObject
         catch let error as NSError 
            print("Could not fetch. \(error)")
        
    

    func save(firstName: String, lastName: String, dob: String, phoneNumber: String, zipCode: String) 
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else  return 
        let managedObjectContext = appDelegate.persistentContainer.viewContext
        guard let entity = NSEntityDescription.entity(forEntityName:"Contact", in: managedObjectContext) else  return 
        let person = NSManagedObject(entity: entity, insertInto: managedObjectContext)
        person.setValue(firstName, forKey: "firstName")
        person.setValue(lastName, forKey: "lastName")
        person.setValue(dob, forKey: "dateOfBirth")
        person.setValue(phoneNumber, forKey: "phoneNumber")
        person.setValue(zipCode, forKey: "zipCode")
        do 
            try managedObjectContext.save()
            self.persons.append(person as! Person) //previously just contact, no casting!
         catch let error as NSError 
            print("Couldn't save. \(error)")
        
    

    func update(indexPath: IndexPath, firstName: String, lastName: String, dob: String, phoneNumber: String, zipCode: String) 
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else  return 
        let managedObjectContext = appDelegate.persistentContainer.viewContext
        let contact = persons[indexPath.row]
        contact.setValue(firstName, forKey: "firstName")
        contact.setValue(lastName, forKey: "lastName")
        contact.setValue(dob, forKey: "dateOfBirth")
        contact.setValue(phoneNumber, forKey: "phoneNumber")
        contact.setValue(zipCode, forKey: "zipCode")
        do 
            try managedObjectContext.save()
            persons[indexPath.row] = contact
         catch let error as NSError 
            print("Couldn't update. \(error)")
        
    

    func delete(_ contact: NSManagedObject, at indexPath: IndexPath) 
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else  return 
        let managedObjectContext = appDelegate.persistentContainer.viewContext
        managedObjectContext.delete(contact)
        persons.remove(at: indexPath.row)

        //Always remember to save after deleting, updates Core Data
        do 
            try managedObjectContext.save()
         catch 
            print("Something went wrong \(error.localizedDescription)")
        
    

    // MARK: - Table View Setup
    override func numberOfSections(in tableView: UITableView) -> Int 
        return 1
    

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return persons.count
    


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as? PersonsCell

        let person = persons[indexPath.row]

        cell?.firstName?.text = person.value(forKey:"firstName") as? String
        cell?.lastName?.text = person.value(forKey:"lastName") as? String
        cell?.dob?.text = person.value(forKey:"dateOfBirth") as? String
        cell?.phoneNumber?.text = person.value(forKey:"phoneNumber") as? String
        cell?.zipCode?.text = person.value(forKey:"zipCode") as? String

        return cell!
    

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        return 75
    

    // Override to support conditional editing of the table view.
    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool 
        return true
    

    // MARK: - Navigation
    @IBAction func unwindToContactsList(segue:UIStoryboardSegue) 
        if let viewController = segue.source as? AddContactVC 
            guard let _firstName: String = viewController.firstNameLbl.text,
                let _lastName: String = viewController.lastNameLbl.text,
                let _dob: String = viewController.dateOfBirthLbl.text,
                let _phoneNumber: String = viewController.phoneNumberLbl.text,
                let _zipCode: String = viewController.zipCodeLbl.text
                else  return 
            if _firstName != "" && _lastName != "" && _dob != "" && _phoneNumber != "" && _zipCode != "" 
                if let indexPath = viewController.indexPathForContact 
                    update(indexPath: indexPath, firstName: _firstName, lastName: _lastName, dob: _dob, phoneNumber: _phoneNumber, zipCode: _zipCode)
                    print("Any updates?")
                 else 
                    save(firstName: _firstName, lastName: _lastName, dob: _dob, phoneNumber: _phoneNumber, zipCode: _zipCode)
                    print("added to tableview") //this runs twice for some reason...
                
            
            tableView.reloadData()
         else if let viewController = segue.source as? EditContactVC 
            if viewController.isDeleted 
                guard let indexPath: IndexPath = viewController.indexPath else  return 
                let person = persons[indexPath.row]
                delete(person, at: indexPath)
                tableView.reloadData()
            
        
    

【问题讨论】:

【参考方案1】:

这里是使用NSFetchedResultsController 实现目标的示例代码。我省略了一些不相关的代码。

class ContactViewController: UITableViewController 
    let fetchedResultsController: NSFetchedResultsController<Contact>!

    func searchTextFieldDidEditingChanged(_ textField: UITextField) 
        let text = textField.text ?? ""
        refetch(with: text)
    

    // The key is you need change the predicate when searchTextField's 
    // value changed, and invoke proformFetch() again
    func refetch(with text: String) 
        let predicate = NSPredicate(format: "firstName CONTAINS %@", text)
        fetchedResultsController.fetchRequest.predicate = predicate

        do 
            try self.fetchedResultsController.performFetch()
            tableView.reloadData()
         catch let error as NSError 
            loggingPrint("Error: \(error.localizedDescription)")
        
    


// MARK: - Table datasource

extension ContactViewController 
    override func numberOfSections(in tableView: UITableView) -> Int 
        return fetchedResultsController.sections!.count
    

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        let sectionInfo = fetchedResultsController.sections![section]
        return sectionInfo.numberOfObjects
    

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as? PersonsCell

        let contact = fetchedResultsController.object(at: indexPath)
        cell.contact = contact

        return cell!
    

【讨论】:

【参考方案2】:

您可以使用 NSPredicate 的sortDescriptors 属性来过滤获取请求的结果。

查看链接了解更多信息: How to sort a fetch in Core Data

【讨论】:

以上是关于如何过滤在 Core Data 中有对象的表视图?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Core Data 的 View Controller 中的表视图

如何通过 NSPredicate 过滤 Core Data 托管对象?

iPhone iOS Core Data 我应该为我的实体子类化一个抽象实体吗?

如何在重新进入查看时转储 Core Data 搜索结果 FRC?

使用 Swift 使用 Core Data 对象填充 UIPickerView

在 Core Data 中获取过滤的关系