Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2697108
  • 博文数量: 877
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 5921
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-05 12:25
个人简介

技术的乐趣在于分享,欢迎多多交流,多多沟通。

文章分类

全部博文(877)

文章存档

2021年(2)

2016年(20)

2015年(471)

2014年(358)

2013年(26)

分类: iOS平台

2015-10-19 17:04:38

ios9学习系列: Contacts Framework


iOS 9 中,苹果介绍了新的 Contacts framework。允许用户使用 Objective-C 的 API 和设备的通讯录进行交互,同样适用于 Swift 语言。比起之前通过 AddressBook framework 来读取联系人信息来说,这是一个巨大的进步。因为 AddressBook framework 没有 Objective-C 的 API,非常难用,用 Swift 写的时候更是痛苦。希望新的 Contacts framework 能够解决这些痛点。

开发者有多不喜欢 AddressBook framework 呢?我想在 WWDC 的相关 session 里,当宣布 AddressBook framework 会在 iOS 9 中弃用后,现场爆发了最长时间、最大声的欢呼,就是最好的证明。

从 Framework 中返回的联系人是统一的,这意味着,如果你有从不同的数据源来的相同联系人数据,他们会自动合并,无需手动进行合并的操作。

使用新的 Contacts Framework

现在我们来创建一个简单的应用。这个应用展示一个你的通讯录的联系人列表,同时允许你查看(联系人的)详细信息。

contact result


如果你所见,这是一个 master detail view controller 应用,在 iPhone 同样可以很好的展示。在左边是一个你的设备上的联系人列表,右边可以看到联系人的头像、姓名、电话号码等详细信息。

获取用户的联系人

用Xcode 新建一个项目,只需要选择 master detail view controller 模版就可以开始了。他会给你设置好。

创建好项目后,打开 MasterViewController 类,首先我们要在头部引入 Contacts 和 ContactsUI 框架。

import Contacts

import ContactsUI

现在我们写一个方法,填充 datasrouce的特性。这个方法要读取和展示当前设备通讯录里的联系人。

func findContacts() -> [CNContact] {

let store = CNContactStore()

CNContactStore 是一个用来读取和保存联系人的新的类。这篇文章中我们仅仅展示如何读取联系人,但是你同样可以(用此方法)进行展示和保存联系人群组操作。

let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),

CNContactImageDataKey,

CNContactPhoneNumbersKey]

let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch)

当我们有了这个联系人数据库的引用后,我们需要创建一个指定条件的请求,通过这个 query 的请求去获取某些结果。创建一个 CNContactFetchRequest ,我们可以通过设置 contact keys 的数组,来获取我们需要的结果。有趣的是,我们可以通过CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName) 来格式化。这是CNContactFormattter 的一个非常方便的方法,稍后我们还会用到。

CNContactFormatter 需要很多不同的 keys,如果不使用 descriptorForRequiredKeysForStyle 方法,我们需要手动设置以下的 keys。

[CNContactGivenNameKey,

CNContactNamePrefixKey,

CNContactNameSuffixKey,

CNContactMiddleNameKey,

CNContactFamilyNameKey,

CNContactTypeKey...]

如你所见,要写一大堆代码。当 CNContactFormatter key 的需求发生改变,在从CNContactFormatter 生成一个字符串时,你会接到一个异常。

  1. var contacts = [CNContact]() 
  2.  
  3. do { 
  4.  
  5.     try store.enumerateContactsWithFetchRequest(fetchRequest, usingBlock: { (let contact, let stop) -> Void in 
  6.  
  7.     contacts.append(contact) 
  8.  
  9. }) 
  10.  
  11.  
  12. catch let error as NSError { 
  13.  
  14.     print(error.localizedDescription) 
  15.  
  16.  
  17. return contacts 

这段代码非常简单。我们所做的是从 CNContactStore 中遍历所有符合我们需求的联系人。这个request 没有加任何的条件,所以会返回全部的联系人,包含我们需要的 keys。我们把每一条记录都逐个保存到一个数组中,返回。

现在我们要调用这个方法,用表格来展示结果。再次打开 MasterViewController, 添加一个属性,用来展示结果。

var contacts = [CNContact]()

更新 viewDidLoad 方法,用同步的方法调用并存储结果。

  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
  2.  
  3.     self.contacts = self.findContacts() 
  4.  
  5.     dispatch_async(dispatch_get_main_queue()) { 
  6.  
  7.         self.tableView!.reloadData() 
  8.  
  9.     } 
  10.  
  11.   
  12.  

一旦保存好结果,刷新表格。

你需要修改一下 UITableViewDatasource 的方法来展示刚刚得到的结果。

  1. override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
  2.  
  3.     return self.contacts.count 
  4.  
  5.  
  6. override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
  7.  
  8.     let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 
  9.  
  10.     let contact = contacts[indexPath.row] as CNContact 
  11.  
  12.     cell.textLabel!.text = "\(contact.givenName) \(contact.familyName)" 
  13.  
  14.     return cell 
  15.  

现在剩下的就是在 DetailViewController 中展示联系人的详细信息了。这里我不在细述,你需要在 DetailViewController 中添加一个图像视图、两个标签视图,来展示头像、姓名和电话号码。并且在 interface builder 中创建 IBOutlet.

@IBOutlet weak var contactImageView: UIImageView!

@IBOutlet weak var contactNameLabel: UILabel!

@IBOutlet weak var contactPhoneNumberLabel: UILabel!

当这些做完,我们需要设置当前的值。在 configureView ,你需要添加下面这行代码。

label.text = CNContactFormatter.stringFromContact(contact, style: .FullName)

正如我们之前提到的,CNContactFormatter 能够很好的格式化联系人的名字。我们所要做的仅仅是按需求格式化他们,formatter可以很好的控制格式。

在设置头像时,我们需要先检测一下 imageData 是否存在。如果设备上的某个联系人没有设置头像, imageData 可能没有,(不检测的话)应用会崩溃。

  1. if contact.imageData != nil { 
  2.  
  3.     imageView.image = UIImage(data: contact.imageData!) 
  4.  
  5. else { 
  6.  
  7.     imageView.image = nil 
  8.  

如果存在,我们给 image view 设置好。

最后,我们给电话号码标签指定值。

  1. if let phoneNumberLabel = self.contactPhoneNumberLabel { 
  2.  
  3.     var numberArray = [String]() 
  4.  
  5.     for number in contact.phoneNumbers { 
  6.  
  7.         let phoneNumber = number.value as! CNPhoneNumber 
  8.  
  9.         numberArray.append(phoneNumber.stringValue) 
  10.  
  11.     } 
  12.  
  13.     phoneNumberLabel.text = ", ".join(numberArray) 
  14.  

这是最终的展示结果。现在,我们拥有一个app,可以在左侧,显示设备上通讯录中联系人的列表,并可以逐个找到他的详细信息。

 

contact details


使用 ContactsUI 选择联系人

也许我们希望这个应用,可以让用户自己选择联系人,并且展示详细信息给我们。正如此前你看到的,这可能要写很多代码。如果这些功能已经做好了的,会让开发变的更加简单。

这正是 ContactsUI framework 的功能。他提供了一套 view controllers,我们可以用在我们的应用中,展示联系人的信息。

在这一节,我们想让用户可以选择某个电话号码,并且保存起来。因为只是一个 demo,所以我们选择在 MasterViewController 的右上角添加一个 UIBarButtonItem,然后在 MasterViewController 类中,给 UIBarButtonItem 一个方法。

@IBAction func showContactsPicker(sender: UIBarButtonItem) {

  let contactPicker = CNContactPickerViewController()

  contactPicker.delegate = self;

  contactPicker.displayedPropertyKeys = [CNContactPhoneNumbersKey]

  self.presentViewController(contactPicker, animated: true, completion: nil)

}

我们创建了一个简单的 CNContactPickerViewController ,设置他的代理为 self.这样我们就能够响应他的请求,我们感兴趣的事电话号码,尽在选中电话号码后,展示联系人信息。CNContactPickerViewController 帮我们控制UI。

  1. func contactPicker(picker: CNContactPickerViewController, didSelectContactProperty contactProperty: CNContactProperty) { 
  2.  
  3.     let contact = contactProperty.contact 
  4.  
  5.     let phoneNumber = contactProperty.value as! CNPhoneNumber 
  6.  
  7.     print(contact.givenName) 
  8.  
  9.     print(phoneNumber.stringValue) 
  10.  

在 contactPicker 代理方法 didSelectContactProperty 中,我们复制一个CNContactProperty 对象。这是 CNContact 的一个 wrapper。让我们来看一下他是怎么工作的。

 

contact picker


当我们点击 MasterViewController 右上角的 UIBarButtonItem 后,会展示一个页面。这个页面是所有联系人的列表,我们没有添加任何的过滤条件。

 

contact selected


当你点击某个联系人,会展示出这个联系人的电话列表。正是我们之前CNContactPhoneNumbersKey 里设置的一样,这个页面仅展示了我们需要的关键字段。

最后,当你点击了页面中某些属性,例如电话号码后,会在 picker 关闭前触发 contactPicker:didSelectContactProperty方法。

在这个例子中,名字叫“Kate Bell”的联系人是 CNContact 的一个例子。“phoneNumbers”是 key,“5555648583”是 CNPhoneNumber 的值。最后 identifier 字符串作为他的 identifier property.

总结一下,这个例子里我们使用 ContactsUI framework 来展示选取某个联系人,是多么简单和易用。如果你想开发更加丰富的页面,更自主的控制页面的展示信息,Contacts framework 会给你提供很好的获取数据信息的方式。

延伸阅读

更多关于 Contacts Framework 的信息,我推荐你观看WWDC 2015 的 session 223Introducing the Contacts Framework for iOS and OS X. 最后不要忘了,你可以在Github 上找到我们已经创建的本篇文章的Demo项目。

阅读(781) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~