Проблемы реализации Search Controller с CNContacts (Swift) - PullRequest
2 голосов
/ 28 января 2020

Я занимаюсь разработкой небольшого приложения и хочу реализовать контроллер поиска, чтобы можно было запрашивать контакты, которые я получил. Мне удалось получить системные контакты и отобразить их в виде таблицы. Но когда я пытаюсь реализовать контроллер поиска, чтобы упростить поиск контакта, у меня возникает проблема «Неверный предикат», и я не знаю, как ее решить.

Это мой настоящий подход:

import UIKit
import Contacts

class PhoneContactsVC: UITableViewController {

    var contactsArray = [CNContact]()
    var contacts = [CNContact]()
    var selectedContactCN = CNContact()
    var searchController: UISearchController!
    var searchResults = [CNContact]()

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self as UITableViewDelegate
        tableView.dataSource = self
        let store = CNContactStore()
        retrieveContacts(from: store)
        setColors()
        let searchResultsController = UITableViewController(style: .insetGrouped)
        searchResultsController.tableView.delegate = self
        searchResultsController.tableView.dataSource = self
        searchResultsController.tableView.rowHeight = 63
        searchResultsController.tableView.register(FriendCell.self, forCellReuseIdentifier: "FriendCell")
        searchController = UISearchController(searchResultsController: searchResultsController)
        searchController.searchResultsUpdater = self
        searchController.searchBar.sizeToFit()
        searchController.searchBar.tintColor = .systemGreen
        searchController.searchBar.delegate = self
        searchController.searchBar.barTintColor = .systemGreen
        definesPresentationContext = true
        navigationItem.searchController = searchController

    }

    override func willMove(toParent parent: UIViewController?) { // tricky part in iOS 10
        navigationController?.navigationBar.barTintColor = UIColor.systemGreen //previous color
        super.willMove(toParent: parent)
    }

    private func setColors(){
        navigationController?.navigationBar.barTintColor = UIColor.white
        let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.black]
        navigationController?.navigationBar.titleTextAttributes = textAttributes
        navigationController?.navigationBar.tintColor = UIColor.systemBlue;
    }

    func filterResultsWithSearchString(searchString: String) {
        let predicate: NSPredicate = CNContact.predicateForContacts(matchingName: searchString)
        let keysToFetch = [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactImageDataAvailableKey as CNKeyDescriptor, CNContactBirthdayKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactPhoneNumbersKey as CNKeyDescriptor]
        searchResults = try! CNContactStore().unifiedContacts(matching: predicate, keysToFetch: keysToFetch)

    }


func retrieveContacts(from store: CNContactStore) {
  let containerId = store.defaultContainerIdentifier()
  let predicate = CNContact.predicateForContactsInContainer(withIdentifier: containerId)
  // 4
  let keysToFetch = [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactImageDataAvailableKey as CNKeyDescriptor, CNContactBirthdayKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactPhoneNumbersKey as CNKeyDescriptor]
  contactsArray = try! store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch)
    contacts = contactsArray.sorted(by: {$0.givenName < $1.givenName})
    contacts.removeAll(where: {$0.givenName == ""})
  DispatchQueue.main.async { [weak self] in
    self?.tableView.reloadData()
    }
}
}

extension PhoneContactsVC: UISearchBarDelegate {
}

extension PhoneContactsVC: UISearchResultsUpdating {
  func updateSearchResults(for searchController: UISearchController) {
    let searchString = searchController.searchBar.text!
    filterResultsWithSearchString(searchString: searchString)

    let searchResultsController = searchController.searchResultsController as! UITableViewController
    searchResultsController.tableView.reloadData()
  }
}

extension PhoneContactsVC {
  // 1
  override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return contacts.count
  }

  // 2
  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // 3
    let cell = self.tableView.dequeueReusableCell(withIdentifier: "ContactTableViewCell") as! ContactTableViewCell
    let contact = searchController.isActive ? searchResults[indexPath.row] : contacts[indexPath.row]

    if contact.birthday == nil {
        cell.registeredBday.isHidden = true
    }
    else {
        cell.registeredBday.isHidden = false
    }
    cell.nameLabel.text = "\(contact.givenName) \(contact.familyName)"

    // 4
    if contact.imageDataAvailable == true, let imageData = contact.imageData {
      cell.contactImage.image = UIImage(data: imageData)
    }
    else {
        cell.contactImage.image = UIImage(named: "MaLong")
    }
    return cell
  }

  override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath{
    selectedContactCN = contacts[indexPath.row]
    return indexPath
  }
}

Я надеюсь, что вы можете предложить мне решение. Спасибо вам всем!

Ответы [ 2 ]

2 голосов
/ 28 января 2020

.. потому что ваш предикат неверен =). Обновите код следующим образом:

    func filterResultsWithSearchString(searchString: String) {
        let keysToFetch = [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactImageDataAvailableKey as CNKeyDescriptor, CNContactBirthdayKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactPhoneNumbersKey as CNKeyDescriptor]
        var predicate: NSPredicate!
        if searchString.isEmpty {
            let containerId = CNContactStore().defaultContainerIdentifier()
            predicate = CNContact.predicateForContactsInContainer(withIdentifier: containerId)

        }
        else {
            predicate = CNContact.predicateForContacts(matchingName: searchString)
        }
        do {
            searchResults = try CNContactStore().unifiedContacts(matching: predicate, keysToFetch: keysToFetch)
        }
        catch let error {
            print("ERROR: \(error)")
        }
    }

...

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return searchController.isActive ? searchResults.count : contacts.count
    }

0 голосов
/ 28 января 2020

Вместо этого используйте CNContactPicker, предоставленный APPLE, в него включен поиск.

import ContactsUI

let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self
present(contactPicker, animated: true, completion: nil)

В методах делегата делайте с выбранными контактами все, что хотите.

extension YourViewController : CNContactPickerDelegate {
    func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {

    }
    func contactPickerDidCancel(_ picker: CNContactPickerViewController) {

    }
}
...