关于iOS Address Book编程指南

来源:互联网 发布:linux ftp指令prompt 编辑:程序博客网 时间:2024/06/05 13:33

关于iOS Address Book编程指南  


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

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
   //这个方法在用户选择了一个person时调用
    [self displayPerson:person];
    [self dismissModalViewControllerAnimated: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。要检查是否有未保存的更改,使用ABAddressBookHasUnsavedChanges.
下面是例子:
ABAddressBookRef addressBook;
bool wantToSaveChanges = YES;
bool didSave;
CFErrorRef error = NULL;
 
addressBook = ABAddressBookCreate();
/* ... Work with the address book. ... */

if (ABAddressBookHasUnsavedChanges(addressBook)) {
    if (wantToSaveChanges) {
        didSave = ABAddressBookSave(addressBook, &error);
        if (!didSave) {/* Handle error here. */}
    } else {
        ABAddressBookRevert(addressBook);
    }
}
 
CFRelease(addressBook);

在另一个应用或另一个线程对Address Book Database做出更改时,你的应用程序可以请求接收一个通知。一般地,如果你正在显示已经存在的联系人并且你想更新UI来反应联系人的更改,那么你应该注册一个通知。
使用函数ABAddressBookRegisterExternalChangeCallback来注册一个原型为ABExternalChangeCallback类型的函数。你或许会多次调用ABAddressBookRegisterExternalChangeCallback来注册多个callbacks。使用ABAddressBookUnregisterExternalChangeCallback来取消注册。
当你接收到一个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 name是单值属性,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,表示组的名称。要获得属于这个组的所有联系人,使用ABGroupCopyArrayOfAllMembers或ABGroupCopyArrayOfAllMembersWithShortOrdering,它返回一个包含ABRecordRef的CFArrayRef对象。

5、属性:
1)单值属性:
ABRecordRef aRecord = ABPersonCreate();
CFErrorRef anError = NULL;
bool didSet;
 
didSet = ABRecordSetValue(aRecord, kABPersonFirstNameProperty, CFSTR("Katie"), &anError);
if (!didSet) {/* Handle error here. */}
 
didSet = ABRecordSetValue(aRecord, kABPersonLastNameProperty, CFSTR("Bell"), &anError);
if (!didSet) {/* Handle error here. */}
 
CFStringRef firstName, lastName;
firstName = ABRecordCopyValue(aRecord, kABPersonFirstNameProperty);
lastName  = ABRecordCopyValue(aRecord, kABPersonLastNameProperty);
 
/* ... Do something with firstName and lastName. ... */
 
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来获取。使用函数ABMultiValueGetIndexForIdentifier和ABMultiValueGetIdentifierAtIndex.
要保持多值属性中的某一个特定值,就存储它的identifier。因为index会变,但identifier不会变,除非设备变了。
ABMultiValueCopyLabelAtIndex和ABMultiValueCopyValueAtIndex来拷贝单独的值
ABMultiValueCopyArrayOfAllValues拷贝所有的值到一个数组中。

3)可变多值属性:
Multivalue对象是不可变的,要更改它你需要创建一个可变的拷贝,通过函数ABMultiValueCreateMutableCopy。你也可以通过ABMultiValueCreatteMutable来创建一个新的可变multivalue对象。
下面的函数用来更改可变MultiValue的属性:
ABMultiValueAddValueAndLabel和ABMultiValueInsertValueAndLabelAtIndex来增加属性
ABMultiValueReplaceValueAtIndex和ABmultiValueReplaceLabelAtIndex来修改属性
ABmultiValueRemoveValueAndLabelAtIndex来移除属性
下面是例子:
ABMutableMultiValueRef multi = ABMultiValueCreateMutable(kABMultiStringPropertyType);
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 = ABMultiValueAddValueAndLabel(multi, @"(555) 555-1234",kABPersonPhoneMobileLabel, &multivalueIdentifier);
if (!didAdd) {/* Handle error here. */}
 
didAdd = ABMultiValueAddValueAndLabel(multi, @"(555) 555-2345",kABPersonPhoneMainLabel, &multivalueIdentifier);
if (!didAdd) {/* Handle error here. */}
 
ABRecordRef aRecord = ABPersonCreate();
didSet = ABRecordSetValue(aRecord, kABPersonPhoneProperty, multi, &anError);
if (!didSet) {/* Handle error here. */}
CFRelease(multi);
 
/* ... */
 
CFStringRef phoneNumber, phoneNumberLabel;
multi = ABRecordCopyValue(aRecord, kABPersonPhoneProperty);
 
for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) {
    phoneNumberLabel = ABMultiValueCopyLabelAtIndex(multi, i);
    phoneNumber      = ABMultiValueCopyValueAtIndex(multi, i);
 
    /* ... Do something with phoneNumberLabel and phoneNumber. ... */
 
    CFRelease(phoneNumberLabel);
    CFRelease(phoneNumber);
}

CFRelease(aRecord);
CFRelease(multi);

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

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,
        &kCFCopyStringDictionaryKeyCallBacks,
        &kCFTypeDictionaryValueCallBacks
);

// Add the street address to the multivalue.
ABMultiValueIdentifier identifier;
bool didAdd;
didAdd = ABMultiValueAddValueAndLabel(address, aDict, kABHomeLabel, &identifier);
if (!didAdd) {/* Handle error here. */}
CFRelease(aDict);
 
/* ... Do something with the multivalue, such as adding it to a person record. ...*/
 
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、提示用户选择一个联系人记录:
使用ABPeoplePickerNavigationController
步骤:
1)创建和初始化ABPeoplePickerNavigationController类的实例。
2)设置代理,遵循ABpeoplePickerNavigationControllerDelegate协议。
3)可选地,设置你想显示的displayedProperties属性(该属性为一个array)。相关的常量被定义为integer;使用NSNumber包装。
4)presentModalViewController:animated:来显示它。
例子:
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];

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

3、显示和编辑联系人记录:
使用ABPersonViewController类来显示联系人。
步骤:
1)创建并初始化它的实例。
2)设置代理,必须遵循ABPersonViewControllerDelegate协议。要允许用户编辑联系人,设置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:shouldPerformDefaultActionForPerson:property:identifier:代理方法来决定是否执行默认行为。要执行默认行为,返回YES,否则返回NO。

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

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

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

当用户完成创建一个新的联系人或添加属性到一个已经存在的联系人时,调用unknownPersonViewController: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,使用ABAddressBookGetPersonWithRecordID。要通过identifier查找一个组,使用函数ABAddressBookGetGroupWithRecordID。要通过name来查找record,使用函数ABAddressBookCopyPeopleWithName。

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

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

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