通讯录相关框架详细解析(五)—— Contacts和ContactsUI框架的基本使用示例(一)

版本记录

版本号 时间
V1.0 2019.06.18 星期二

前言

有关通讯录的框架有好几种,包括AddressBookAddressBookUIContactsContactsUI,这里,iOS9 以后AddressBook、AddressBookUI就不推荐使用了,但是如果要支持ios8以及以前的系统还是要用它们两个框架的。ios9以后系统推荐使用ContactsContactsUI,接下来的几篇我们就详细的解析一下这几个框架。感兴趣的可以看我写的上面几篇。
1. 通讯录相关框架详细解析(一)—— AddressBook框架基本概览
2. 通讯录相关框架详细解析(二)—— AddressBookUI框架基本概览
3. 通讯录相关框架详细解析(三)—— Contacts框架基本概览(一)
4. 通讯录相关框架详细解析(四)—— ContactsUI框架基本概览(一)

开始

首先看下写作环境

Swift 5, iOS 12, Xcode 10

在此iOS Contacts框架教程中,了解如何从用户设备中选择,显示,保存和编辑联系人。

移动设备最重要的功能之一是为您想要与之通信的人存储联系信息。 无论是通过电话,消息传递还是共享非常重要的memes,如果没有这些信息,您的设备将无法进行通信。 您可以使用Contacts框架获取此信息。

作为应用程序开发人员,了解如何使用联系信息更好地将应用程序与用户设备集成非常重要。 在此Contacts框架教程中,您将学习如何使用ContactsContactsUI框架。

通过Contacts框架,您可以从应用程序中读取或修改用户的联系人。 这些是在Contacts应用中显示的相同联系人。 它取代了旧的Address Book框架。

在本教程中,您将学习在之前学习中使用的应用程序的修改版本。 您将能够选择,显示,保存和编辑存储在用户设备上的联系人。

打开已有的入门项目。 该应用程序有一些基本界面来显示默认联系人列表。 您将实现一些按钮,因此并非所有内容都在UI中运行。

构建并运行项目,您将看到已在应用程序中进行硬编码的著名联系人列表。

在您使用contacts之前,您需要熟悉CNContact,该类用于存储您的应用程序可能访问的所有联系人信息。 打开Friend.swift并看一下。 Friend是这个项目的一个类,它有一个属性contactValue


Showing Contacts Information

如果您在使用contacts,您可能希望在某些时候显示此信息。 Apple提供了一个内置的视图控制器,可以显示CNContact,只需要很少的工作。

打开FriendsViewController.swift并在文件顶部添加此导入:

import ContactsUI

接下来,将此代码添加到tableView(_:didSelectRowAt :)

// 1
let friend = friendsList[indexPath.row]
let contact = friend.contactValue
// 2
let contactViewController = CNContactViewController(forUnknownContact: contact)
contactViewController.hidesBottomBarWhenPushed = true
contactViewController.allowsEditing = false
contactViewController.allowsActions = false
// 3
navigationController?.navigationBar.tintColor = .appBlue
navigationController?.pushViewController(contactViewController, animated: true)

下面进行详细分解

  • 1) 你从friend那里得到了CNContact值。
  • 2) 您创建了一个CNContactViewController并对其进行了配置。 在这里,您关闭了编辑和操作(editing and actions)。 如果您希望UI显示允许您与此人共享联系人或您的位置的按钮,则可以将allowsActions设置为true
  • 3) CNContactViewController具有浅色主题,与在Contacts.app中查看联系人时的主题非常相似。 由于应用程序的主题,您需要在呈现contact controller之前调整导航栏的色调,以便可以看到导航按钮。 颜色已在viewWillAppear(_ :)中切换回来。

构建并运行并选择列表中的任一联系人。 现在,您将看到一个漂亮的视图控制器中显示的联系人,而您根本没有太多工作。


Picking Contacts

接下来,您可以使用联系人选择器(contact picker)更多地利用ContactsUI框架。 这是从用户设备中选择联系人并将数据导入应用程序的内置方式。

打开FriendsViewController.swift并将以下代码添加到文件的底部:

//MARK: - CNContactPickerDelegate
extension FriendsViewController: CNContactPickerDelegate {
  func contactPicker(_ picker: CNContactPickerViewController,
                     didSelect contacts: [CNContact]) {
    let newFriends = contacts.compactMap { Friend(contact: $0) }
    for friend in newFriends {
      if !friendsList.contains(friend) {
        friendsList.append(friend)
      }
    }
    tableView.reloadData()
  }
}

在这里,您确保从CNContactPickerViewController中选择的联系人已正确添加到列表中,但前提是它们之前未添加。

最后,将以下实现添加到addFriends(sender :)

// 1
let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self
// 2
contactPicker.predicateForEnablingContact = NSPredicate(
  format: "emailAddresses.@count > 0")
present(contactPicker, animated: true, completion: nil)

以下是您添加内容的快速细分:

  • 1) 您创建了一个联系人选择器并设置了代理。
  • 2) 您在联系人选择器上设置谓词(predicate)。 您可以使用谓词来过滤符合您选择的特定条件的联系人。 在这种情况下,您可以确保要选择的联系人至少有一个电子邮件地址。

构建并运行,然后从右上角选择添加按钮。

如果您在模拟器中运行,您将看到David Taylor的默认联系人列表变灰。 这是因为他的联系人没有电子邮件地址。

从列表中选择其中一个联系人,然后选择Done。 您应该看到联系人在列表中,其中包含默认联系人。


Setting Up Contacts Permissions

到目前为止,您无需获得许可即可访问用户的联系人数据。 你可以这样做,因为Apple称之为out-of-process pickers。 选择器在您的应用程序进程外运行,您获得的唯一信息是用户选择的数据。

对于后续步骤,您将直接存储的访问联系人。 您需要适当的权限才能执行此操作。

首先,打开Info.plist并选择属性列表中任何项旁边的add按钮。 在新添加的项目中,查找或键入:

Privacy - Contacts Usage Description

value字段中,添加以下描述:

Access is needed to save your RWConnect friends information to your Contacts list.

系统将在显示要求许可的alert时向用户显示此属性的值。

注意:请务必正确解释您需要该权限的原因,因为模糊目的字符串可能导致应用审核中的拒绝。

接下来,打开AppDelegate.swift并将Contacts的导入添加到文件顶部:

import Contacts

最后,在它返回之前将以下代码添加到application(_:didFinishLaunchingWithOptions:)

CNContactStore().requestAccess(for: .contacts) { (access, error) in
  print("Access: \(access)")
}

此代码将触发系统要求用户访问Contacts的权限。

构建并运行项目,您将看到Info.plist中添加了文本值的权限alert。 一定要选择OK


Editing Contacts

在最后一节中,您将编辑联系人并将其保存回用户的设备。 到目前为止,您一直在使用ContactsUI框架提供的内置UI。 现在,您将使用自定义UI编辑或向联系人添加电话号码,并将其保存回设备的联系人记录。

为此,您将从设备中获取联系人并检查是否已存在电话号码,然后将其显示在EditFriendTableViewController中。

FriendsViewController.swift中,添加以下代码到 prepare(for:sender:)

if segue.identifier == "EditFriendSegue",
  // 1
  let cell = sender as? FriendCell,
  let indexPath = tableView.indexPath(for: cell),
  let editViewController = segue.destination as? EditFriendTableViewController {
  let friend = friendsList[indexPath.row]
  // 2
  let store = CNContactStore()
  // 3
  let predicate = CNContact.predicateForContacts(matchingEmailAddress: friend.workEmail)
  // 4
  let keys = [CNContactPhoneNumbersKey as CNKeyDescriptor]
  // 5
  if let contacts = try? store.unifiedContacts(matching: predicate, keysToFetch: keys),
    let contact = contacts.first,
    let contactPhone = contact.phoneNumbers.first {
    // 6
    friend.storedContact = contact.mutableCopy() as? CNMutableContact
    friend.phoneNumberField = contactPhone
    friend.identifier = contact.identifier
  }
  editViewController.friend = friend
}

这是你添加的内容:

  • 1) 这将从table view中选择FriendCell
  • 2) 您创建CNContactStore。这是允许您通过fetch and save requests来读取和写入联系人的类。
  • 3) 您可以使用CNContact上提供的类方法创建一个谓词,该谓词将过滤朋友的工作电子邮件。
  • 4) 这将创建您要从商店中获取的键数组。由于您要编辑电话号码,因此这是您要添加的唯一键。
  • 5) 接下来,您在商店上执行提取。您可以在此处看到传递给fetch的predicatekeys
  • 6) 最后,如果联系人与谓词匹配,则使用添加的电话号码创建可变联系人。如果要编辑联系人信息,则需要使用CNMutableContact实例。您还会注意到标识符已添加到联系人中 - 您稍后将使用此标识符。

建立并运行。在任何联系人上选择信息附件,i按钮,然后查看EditFriendTableViewController现在已填充联系人信息。

您可能会注意到电话号码区域仍为空。 那是因为没有一个默认联系人有电话号码。

将设备的其中一个联系人添加到好友列表,然后选择信息info附件。 您可以看到也填充了电话号码。

在最后一步中,您将保存联系人信息到设备。 打开EditFriendTableViewController.swift并将以下内容添加到save(_ :)的末尾:

let store = CNContactStore()
guard 
  let friend = friend,
  let phoneNumberText = phoneTextField.text 
  else { return }
let phoneNumberValue = CNPhoneNumber(stringValue: phoneNumberText)
let saveRequest = CNSaveRequest()

在这里,您创建了一个CNContactStore并确保您拥有可以保存到电话号码的文本。 使用联系人时,无法将String类型保存为电话号码,因此您需要CNPhoneNumber类型。

最后,由于您要保存信息,因此需要CNSaveRequest。 您将决定联系人是否需要使用此请求对象添加或更新。

接下来,在刚刚添加的内容之后添加以下内容:

if let storedContact = friend.storedContact,
  let phoneNumberToEdit = storedContact.phoneNumbers.first(
    where: { $0 == friend.phoneNumberField }
  ),
  let index = storedContact.phoneNumbers.firstIndex(of: phoneNumberToEdit) {
  // 1
  let newPhoneNumberField = phoneNumberToEdit.settingValue(phoneNumberValue)
  storedContact.phoneNumbers.remove(at: index)
  storedContact.phoneNumbers.insert(newPhoneNumberField, at: index)
  friend.phoneNumberField = newPhoneNumberField
  // 2
  saveRequest.update(storedContact)
  friend.storedContact = nil
} else if let unsavedContact = friend.contactValue.mutableCopy() as? CNMutableContact {
  // 3
  let phoneNumberField = CNLabeledValue(label: CNLabelPhoneNumberMain,
                                        value: phoneNumberValue)
  unsavedContact.phoneNumbers = [phoneNumberField]
  friend.phoneNumberField = phoneNumberField
  // 4
  saveRequest.add(unsavedContact, toContainerWithIdentifier: nil)
}

它可能看起来很多,但下面进行细分:

  • 1) 在if条件下,您正在检查您是否正在使用已保存到设备的联系人。使用联系人时遇到的一个问题是您无法直接更新电话号码字段 - 您必须更换它。 phoneNumberToEdit是最初显示的旧电话号码,并使用用户编辑的新电话号码进行更新,然后换出联系人。
  • 2) 由于此联系人已经存在并且只需要更新,因此您将在CNSaveRequest上将其传递给update(_ :)。这是可能的,因为在前面的步骤中设置了identifier。如果联系人尚不存在,尝试保存会抛出错误。
  • 3) else if条件适用于设备上尚不存在的默认联系人。由于他们还没有电话号码,您需要从头开始创建新的电话号码。您可以使用CNLabeledValue执行此操作。您可以从不同电话号码标签的列表中进行选择,但在这种情况下,您将保存电话号码作为主要电话号码。
  • 4) 与更新联系人的情况一样,您需要告知保存请求您要将此联系人添加到设备。

你几乎完成!最后一步是执行保存。在save(_ :)结束时添加最后一段代码:

do {
  try store.execute(saveRequest)
  let controller = UIAlertController(title: "Success",
                                     message: nil,
                                     preferredStyle: .alert)
  controller.addAction(UIAlertAction(title: "OK", style: .default))
  present(controller, animated: true)
  setup()
} catch {
  print(error)
}

在这里,您尝试在store上执行保存请求。 如果有效,您将看到成功消息。

注意:CNContactStore上的方法是同步的并访问文件系统,因此在生产代码中,您应该在后台线程上实际运行它们。

构建并运行和编辑默认联系人以获取电话号码。 保存后,在模拟器或设备上打开Contacts.app,找到您保存的联系人。 您将看到应用程序中始终显示的默认信息以及您添加的电话号码。

接下来,在应用程序中,从设备添加联系人。 编辑电话号码并保存。 返回Contacts.app,找到联系人以查看您保存的更新的电话号码。

通过使用Contacts框架,您可以简化需要联系信息的任务或增强联系人体验,而无需强制用户维护其联系人的单独副本。 如果您想了解更多信息,可以阅读Apple文档中关于Contacts frameworkContactsUI上的相关内容。

还有关于Apple的Apps Privacy的精彩视频。

后记

本篇主要讲述了ContactsContactsUI框架,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容