iOS Address Book编程指南

一、快速体验:
1、
#import <AddressBookUI/AddressBookUI.h>
ViewController遵循< ABPeoplePickerNavigationControllerDelegate >
相关方法:
- (IBAction)showPicker:(id)sender
{
        ABPeoplePickerNavigationController  *picker =[[ABPeoplePickerNavigation Controller alloc] init];
       picker.peoplePickerDelegate = self;
      [self presentModalViewControll er:picker animated:YES];
       [picker release];
}
相关代理方法:
- (void) peoplePickerNavigationControllerDidCancel :(ABPeoplePickerNavigation Controller *)peoplePicker
{ //这个方法在用户取消选择时调用
       [self dismissModalViewControll erAnimated:YES];
}

- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person  {
     //这个方法在用户选择了一个person时调用
       [self displayPerson:person];
       [self dismissModalViewControll erAnimated:YES];
       return NO;
}

- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{ //这个方法在用户选择了一个person的属性时调用
       return NO;
}

ABRecordRef类型的对象属性:
- (void)displayPerson:( ABRecordRef )person
{//显示一个person
       NSString* name =  (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
       self.firstName.text = name;

       NSString* phone = nil;
        ABMultiValueRef  phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty );
       if ( ABMultiValueGetCount(phoneNumbers)  > 0) {
              phone = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
       } else {
               phone = @"[None]";
       }
       self.phoneNumber.text = phone;
}

二、Building Blocks:Working with Records and Properties
你需要理解4种类型的对象才能完全与Address Book数据库完全交互:address books,records,single-value properties,和mulitivalue properties。这章讨论数据是如何存储在这些对象中,并描述了与这些对象交互的 函数。

1、Address Books:
Address Books对象允许你与Address Book数据库交互。要使用Address Book,声明一个ABAddressBookRef实例,并用ABAddressBookCreate 函数设置其值。

重要提示:ABAddressBookRef 实例不能在多个线程中共用。每个线程必须有其自己的实例。

在你创建了address book reference之后,你的应用就可以从其中读取数据,并且更改并保存它。要保存更改,使用ABAddressBookSave :函数;要放弃更改,使用ABAddressBookRevert 。要检查是否有未保存的更改,使用ABAddressBookHasUnsavedC hanges.
下面是例子:
ABAddressBookRef addressBook ;
bool wantToSaveChanges  = YES;
bool didSave;
CFErrorRef error = NULL;
 
addressBook = ABAddressBookCreate();


if (ABAddressBookHasUnsavedC hanges(addressBook)) {
       if (wantToSaveChanges) {
               didSave = ABAddressBookSave(addressBook, &error);
               if (!didSave) {}
       } else {
               ABAddressBookRevert(addressBook);
       }
}
 
CFRelease(addressBook);

在另一个应用或另一个线程对Address Book Database做出更改时,你的应用程序可以请求接收一个通知。一般地,如果你正在显示已经存在的联系人并且你想更新UI来反应联系人的更改,那么你应该注册一个通知。
使用函数ABAddressBookRegisterExt ernalChangeCallback来注册一个原型为ABExternalChangeCallback 类型的函数。你或许会多次调用ABAddressBookRegisterExt ernalChangeCallback来注册多个callbacks。使用ABAddressBookUnregisterE xternalChangeCallback来取消注册。
当你接收到一个change callback,你需要做两件事:如果你没有未保存的更改,你的代码应该简单地revert(恢复)你的address book来得到最新的数据。如果你有未保存的更改,你可能不想恢复并丢失这些更改。如果你应该保存,Address Book Database会尽力merge你的更改和外部的更改。但是,如果更改不能合并或保存失败,你应该准备好其它的动作。

2、Records:
在Address Book 数据库里,信息被存储到records里,使用ABRecordRef表示。每个record表示一个联系人或一个组。函数ABRecordGetRecordType 返回kABPersonType 表示这是联系人,返回kABGroupType表示这是一个组。熟悉Mac OS的Address Book技术的开发者应该注意没有单独的类来区分records的类型;person对象和group对象都是一个类的实例。

重要提示:Record对象不能在线程间安全传递。取而代之的,你应该传递相应的record identifer。

record可以在Address Book 数据库之外存在。这使得他们在存储联系人信息时很有用。

在一个Record内部,数据被存储为属性的集合。group和person对象的可用属性是不一样的,但是 访问他们的函数一样。函数ABRecordCopyValue 和ABRecordSetValue 用来读取和设置属性。ABRecordRemoveValue 用来移除属性。

3、Person Records:
Person Record有单值属性和多值属性。例如first name和last nam e是单值属性,street address和phone number是多值属性。属性名可以通过kABPersonPhoneProperty这样的常量来获得,例如:
NSString* name =  (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);

NSString* phone = nil;
ABMultiValueRef  phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty );
  if ( ABMultiValueGetCount(phoneNumbers)  > 0) {
        phone = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
       ...

4、Group Records:
Group Records只有一个属性,kABGroupNameProperty,表示组的名称。要获得属于这个组的所有联系人,使用ABGroupCopyArrayOfAllMem bers或ABGroupCopyArrayOfAllMem bersWithShortOrdering,它返回一个包含ABRecordRef的CFArrayRef对象

5、属性:
1)单值属性:
ABRecordRef aRecord = ABPersonCreate();
CFErrorRef anError = NULL;
bool didSet;
 
didSet =  ABRecordSetValue(aRecord, kABPersonFirstNameProperty, CFSTR("Katie"), &anError);
if (!didSet) {}
 
didSet = ABRecordSetValue (aRecord, kABPersonLastNamePropert y , CFSTR("Bell"), &anError);
if (!didSet) {}
 
CFStringRef firstName, lastName;
firstName = ABRecordCopyValue(aRecord, kABPersonFirstNameProper ty);
lastName   = ABRecordCopyValue(aRecord, kABPersonLastNamePropert y);
 

 
CFRelease(aRecord);
CFRelease(firstName);
CFRelease(lastName);

2)多值属性:
多值属性由一组值组成,每个值拥有一个text label和一个 相关的identifier。相同的label可以有多个value,但是identifier是唯一的。
例如,下图显示了一个电话号码属性。这里,一个联系人有多个电话号码 ,每个电话号码都有一个text label ,例如home或work,并且有一个identifier。注意,这个例子有2个家庭电话,他们有相同的label, 但是有不 同的identifier。
关于iOS Address Book编程指南 - supershll - 记忆里
 
一个多值属性的每个单独的值可以通过index或identifier 来获取。使用函数ABMultiValueGetIndexForI dentifier 和ABMultiValueGetIdentifie rAtIndex.
要保持多值属性中的某一个特定值,就存储它的identifier。因为index会变,但identifier不会变,除非设备变了。
ABMultiValueCopyLabelAtI ndex和ABMultiValueCopyValueAtI ndex来拷贝单独的值
ABMultiValueCopyArrayOfA llValues拷贝所有的值到一个数组中。

3)可变多值属性:
Multivalue对象是不可变的,要更改它你需要创建一个可变的拷贝 ,通过函数ABMultiValueCreateMutabl eCopy 。你也可以通过ABMultiValueCreatteMutab le 来创建一个新的可变multivalue对象。
下面的函数用来更改可变MultiValue的属性:
ABMultiValueAddValueAndL abel 和ABMultiValueInsertValueA ndLabelAtIndex 来增加属性
ABMultiValueReplaceValue AtIndex和ABmultiValueReplaceLabel AtIndex来修改属性
ABmultiValueRemoveValueA ndLabelAtIndex来移除属性
下面是例子:
ABMutableMultiValueRef multi = ABMultiValueCreateMutabl e(kABMultiStringPropertyTy pe);
CFErrorRef anError = NULL;
ABMultiValueIdentifier multivalueIdentifier;
bool didAdd, didSet;
 
// Here, multivalueIdentifier is just for illustration purposes; it isn't
// used later in the listing.   Real-world code can use this identifier to
// reference the newly-added value.
didAdd = ABMultiValueAddValueAndL abel(multi, @"(555) 555-1234",kABPersonPhoneMobileLabe l, &multivalueIdentifier );
if (!didAdd) {}
 
didAdd = ABMultiValueAddValueAndL abel(multi, @"(555) 555-2345",kABPersonPhoneMainLabel, &multivalueIdentifier);
if (!didAdd) {}
 
ABRecordRef aRecord = ABPersonCreate();
didSet = ABRecordSetValue(aRecord, kABPersonPhoneProperty, multi, &anError);
if (!didSet) {}
CFRelease(multi);
 

 
CFStringRef phoneNumber, phoneNumberLabel;
multi = ABRecordCopyValue (aRecord, kABPersonPhoneProperty );
 
for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) {
       phoneNumberLabel  = ABMultiValueCopyLabelAtI ndex (multi, i);
       phoneNumber            = ABMultiValueCopyValueAtI ndex (multi, i);
 
     
 
       CFRelease(phoneNumberLabel);
       CFRelease(phoneNumber);
}

CFRelease(aRecord);
CFRelease(multi);

4)街道地址:
街道地址使用一个multivalue的字典来表示。上面所有讨论的multivalue仍然适用于street address。这个属性的每个值都有一个label,例如home或work,并且每个值都是一个street address--使用字典来存储。在这个值内的字典包含keys表示接到地址的不同部分。
下面是例子:
ABMutableMultiValueRef address =ABMultiValueCreateMutabl e(kABDictionaryPropertyTyp e);
 
// Set up keys and values for the dictionary.
CFStringRef keys[5];
CFStringRef values[5];
keys[0] = kABPersonAddressStreetKe y;
keys[1] = kABPersonAddressCityKey;
keys[2] = kABPersonAddressStateKey ;
keys[3] = kABPersonAddressZIPKey;
keys[4] = kABPersonAddressCountryK ey;

values[0] = CFSTR("1234 Laurel Street");
values[1] = CFSTR("Atlanta");
values[2] = CFSTR("GA");
values[3] = CFSTR("30303");
values[4] = CFSTR("USA");
 
CFDictionaryRef aDict = CFDictionaryCreate(
               kCFAllocatorDefault,
               (void *)keys,
               (void *)values,
               5,
               &kCFCopyStringDictionaryK eyCallBacks,
               &kCFTypeDictionaryValueCa llBacks
);

// Add the street address to the multivalue.
ABMultiValueIdentifier identifier;
bool didAdd;
didAdd = ABMultiValueAddValueAndL abel(address, aDict, kABHomeLabel, &identifier);
if (!didAdd) {}
CFRelease(aDict);
 

 
CFRelease(address);

三、用户交互:提示和显示数据
Address Book UI框架提供了3个view Controllers和一个navigation Controller来处理一般地的Address Book数据库和联系人信息。通过使用这个controller而不是你自己创建它们,你可以显著地减少你需要写的代码,并提供一致的用户体验。

1、有什么可用的?
Address Book UI框架提供了4个controllers:
1)ABPeoplePickerNavigationController提示用户选择一个联系人记录。
2)ABPersonViewController显示一个联系人记录并可选地允许用户修改。
3)ABNewPersonViewController提示用户创建一个新的联系人
4)ABUnknownPersonViewController提示用户完善一个联系人资料,可选地允许用户将其添加到adress book。

如图所示:
关于iOS Address Book编程指南 - supershll - 记忆里
 
要使用这个Controller,你需要为他们设置一个代理。你一般不需要子类化它们;要修改他们的行为一般是通过代理来完成。

2、提示用户选择一个联系人记录:
使用ABPeoplePickerNavigation Controller
步骤:
1)创建和初始化ABPeoplePickerNavigation Controller类的实例。
2)设置代理,遵循ABpeoplePickerNavigation ControllerDelegate协议。
3)可选地,设置你想显示的displayedProperties属性(该属性为一个array)。相关的常量被定义为integer;使用NSNumber包装。
4)presentModalViewControll er:animated:来显示它。
例子:
ABPeoplePickerNavigation Controller *picker = [[ABPeoplePickerNavigation Controller alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewControll er:picker animated:YES];

代理函数:
1)如果用户点击了取消,调用peoplePickerNavigationCo ntrollerDidCancel:代理方法
2)如果用户选择了一个person,调用peoplePickerNavigationCo ntroller:shouldContinueAfterSelec tingPerson:代理函数来决定people picker是否继续。要提示用户选择选中联系人的某个属性,那就返回YES,否则返回NO。
3)如果用户选择了一个person的某个属性,调用peoplePickerNavigationCo ntroller:shouldContinueAfterSelec tingPerson:property:identifier:代理方法来决定people picker是否继续。要为选中的属性执行默认的动作(例如拨打电话,开始一个新邮件等等),返回YES。否则返回NO

3、显示和编辑联系人记录:
使用ABPersonViewController类来显示联系人。
步骤:
1)创建并初始化它的实例。
2)设置代理,必须遵循ABPersonViewControllerDe legate协议。要允许用户编辑联系人,设置allowsEditing属性为YES。
3)设置displayedPerson属性为你想要显示的person record。
4)可选地,设置displayedProperties属性为你想要显示的属性(该属性为一个array)。
5)使用pushViewController:animted:来显示它。 Person View Controller必须使用navigation Controller来显示。
例子:
ABPersonViewController *view = [[ABPersonViewController alloc] init];
view.personViewDelegate = self;
view.displayedPerson = person; // Assume person is already defined.
[self.navigationController pushViewController:view animated:YES];

如果用户轻击了属性,调用personViewController:shouldPerformDefaultActi onForPerson:property:identifier:代理方法来决定是否执行默认行为。要执行默认行为,返回YES,否则返回NO。

4、提示用户创建一个新的联系人记录:
使用ABNewPersonViewControlle r
步骤:
1)创建并实例化该类。
2)设置代理,必须遵循ABNewPersonViewControlle rDelegate协议。要填充某些字段,设置displayedPerson属性。要将新联系人放到一个特定的组中,设置 parentGroup。
3)创建并实例化一个新的Navigation contrller,并设置其root view controller为new-person view controller。
4)使用presentModalViewControll er:animated:来显示navigation controller。
例子:
ABNewPersonViewControlle r *view = [[ABNewPersonViewControlle r alloc] init];
view.newPersonViewDelegate = self;
 
UINavigationController *newNavigationController = [[UINavigationController alloc] initWithRootViewControll er:view];
[self presentModalViewControll er:newNavigationController animated:YES];

当用户轻击保存或取消按钮时,调用newPersonViewController:didCompleteWithNewPerson :代理方法。如果用户单击的保存,那么新联系人记录是第一个被添加到address book的记录;如果用户单击的取消,那么person为NULL。

5、提示用户从已存在的数据创建一个新的联系人记录
使用ABUnknownPersonViewContr oller
步骤:
1)创建并实例化。
2)创建一个新的person记录并populate要显示的属性
3)设置displayedPerson属性为这个新的person记录
4)设置代理,必须遵循ABUnknownPersonViewContr ollerDelegate协议
5)要允许用户添加unknown-person view controller显示的信息到一个已经存在的联系人或创建一个新的联系人,那么就设置allowsAddingToAddressBoo k为YES。
6)使用pushViewContrlller:animted:来显示Unknown-person view controller。
例子:
ABUnknownPersonViewContr oller *view = [[ABUnknownPersonViewContr oller alloc] init];
 
view.unknownPersonViewDelegat e = self;
view.displayedPerson = person; // Assume person is already defined.
view.allowsAddingToAddressBoo k = YES;
 
[self.navigationController pushViewController:view animated:YES];

当用户完成创建一个新的联系人或添加属性到一个已经存在的联系人时,调用unknownPersonViewControl ler:didResolveToPerson:代理函数,并返回person record。如果用户取消了,person为NULL。

四、直接交互:代码访问数据库
使用代码修改联系人信息,这一般需要用户确认。尤其是组,因为手机上没有界面让用户管理组,用户没办法回退你做的更改。
1、使用Record Identifiers:
地址簿数据库里的每个记录都有一个identifier。这个identifier总是和同一个record相关,除非记录被删除了或者MobileMe同步数据被重置了。Record Identifier可以在多个线程中安全地传递。但是在不同设备之间不保证。
要保持一个长时间的引用到某个特定的记录,推荐方法是在使用identifier之外,还另外存储其first name和last name。当你通过ID来查找一个联系人时,比较联系人的name和你存储的name。如果他们不匹配,使用存储的name来查找联系人,然后为这个联系人存储新的id。
要获得一个记录的identifier,使用函数ABRecordGetRecordID函数。要通过identifier来查找一个person,使用ABAddressBookGetPersonWi thRecordID。要通过identifier查找一个组,使用函数ABAddressBookGetGroupWit hRecordID。要通过name来查找record, 使用函数ABAddressBookCopyPeopleW ithName。

2、使用Person Records:
你可以添加和移除联系人到/从地址簿,使用函数ABAddressBookAddRecord和ABAddressBookRemoveRecor d。
有两个方法来从地址簿中查找联系人:BAddressBookGetPersonWit hRecordID和ABAddressBookCopyPeopleW ithName。要实现其它类型的搜索,使用函数ABAddressBookCopyArrayOf AllPeople,然后使用NSArray的filteredArrayUsingPredic ate:方法来过滤结果。
要排序联系人,使用函数CFArraySortValues与函数ABPersonComparePeopleByN ames作为比较器和一个类型为ABPersonSortOrdering的上下文(一般使用ABPersonGetSortOrdering来获得用户期望的排序顺序)。
下面是排序的例子:
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef people = ABAddressBookCopyArrayOf AllPeople(addressBook);
CFMutableArrayRef peopleMutable = CFArrayCreateMutableCopy (
                                                                                   kCFAllocatorDefault,
                                                                                   CFArrayGetCount(people),
                                                                                   people
                                                                   );

CFArraySortValues(
               peopleMutable,
               CFRangeMake(0, CFArrayGetCount(peopleMutable)),
               (CFComparatorFunction) ABPersonComparePeopleByN ame,
               (void*) ABPersonGetSortOrdering()
);
 
CFRelease(addressBook);
CFRelease(people);
CFRelease(peopleMutable);

3、使用Group Records:
使用ABAddressBookGetGroupWit hRecordID来查找一个特定的组。你还可以检索所有的组,使用函数:ABAddressBookCopyArrayOf AllGroups, 要检索有多少个组,使用函数ABAddressBookGetGroupCou nt。
你可以使用代码来修改一个组的成员。要添加成员,使用函数ABGroupAddMemeber;要移除成员,使用ABGroupRemoveMember。在添加联系人到一个组之前,这个联系人首先需要存在于地址簿中。如果你需要添加一个新的联系人到一个组并且同时添加到数据库,你必须首先将其添加到数据库,保存数据库,然后再将其添加到组。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值