获取 iOS 上所有联系人的列表

我想要一份 iPhone 所有联系人的列表。

我检查了 Address Book引用,我可能遗漏了一些东西,但我没有看到它提供了一个方法来获得联系人列表。

88464 次浏览

也许 ABPerson功能 所有人的数组可以做?

例子 :

ABAddressBookRef addressBook = ABAddressBookCreate( );
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
CFIndex nPeople = ABAddressBookGetPersonCount( addressBook );


for ( int i = 0; i < nPeople; i++ )
{
ABRecordRef ref = CFArrayGetValueAtIndex( allPeople, i );
...
}

确保你有适当的进口

#import <AddressBook/AddressBook.h>

然后您可以得到一个 CFArray 对象,其中所有联系人使用

CFArrayRef ABAddressBookCopyArrayOfAllPeople (ABAddressBookRef addressBook);

在 iOS6中,一定要使用 ABAddressBookCreateWithOptions,它是 ABAddressBookCreate的升级版

CFErrorRef * error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);


for(int i = 0; i < numberOfPeople; i++){
ABRecordRef person = CFArrayGetValueAtIndex( allPeople, i );
// More code here
}

使用此代码显示所有名字 + 姓氏 + 电话号码(iOS6):

CFErrorRef *error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);


for(int i = 0; i < numberOfPeople; i++) {


ABRecordRef person = CFArrayGetValueAtIndex( allPeople, i );


NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
NSLog(@"Name:%@ %@", firstName, lastName);


ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);


for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
NSString *phoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
NSLog(@"phone:%@", phoneNumber);
}


NSLog(@"=============================================");


}
ABAddressBookRef addressBook = ABAddressBookCreate( );
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
CFIndex nPeople = ABAddressBookGetPersonCount( addressBook );


for ( int i = 0; i < nPeople; i++ )
{
ABRecordRef ref = CFArrayGetValueAtIndex( allPeople, i );
...
}

看看 https://github.com/heardrwt/RHAddressBook(254颗星01/2014)。

为 AddressBook 提供一个更简单的 API 的对象包装器。

iOS 9.0更新 。苹果已经否定了 AddressBook,现在他们增加了 Contacts框架:

添加 CNContactStore属性并像下面这样定义它:

self.contactsStrore = [[CNContactStore alloc] init];

然后添加这些方法来读取所有联系人:

-(void)checkContactsAccess{


[self requestContactsAccessWithHandler:^(BOOL grandted) {


if (grandted) {


CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:@[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactNamePrefixKey, CNContactMiddleNameKey, CNContactPhoneNumbersKey]];
[self.contactsStrore enumerateContactsWithFetchRequest:request error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {


NSLog(@"%@", contact.familyName);
NSLog(@"%@", contact.givenName);
NSLog(@"%@", contact.namePrefix);
NSLog(@"%@", contact.middleName);
NSLog(@"%@", contact.phoneNumbers);
NSLog(@"=============================================");
}];
}
}];
}


-(void)requestContactsAccessWithHandler:(void (^)(BOOL grandted))handler{


switch ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]) {
case CNAuthorizationStatusAuthorized:
handler(YES);
break;
case CNAuthorizationStatusDenied:
case CNAuthorizationStatusNotDetermined:{
[self.contactsStrore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {


handler(granted);
}];
break;
}
case CNAuthorizationStatusRestricted:
handler(NO);
break;
}
}

在 iOS 9.0之前 = > 使用 AddressBook框架。 您必须首先检查用户联系人的访问权限和请求访问权限:

// Prompt the user for access to their Address Book data
-(void)requestAddressBookAccess
{
YourViewController * __weak weakSelf = self;


ABAddressBookRequestAccessWithCompletion(self.addressBook, ^(bool granted, CFErrorRef error)
{
if (granted)
{
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf accessGrantedForAddressBook];


});
}
});
}






-(void)checkAddressBookAccess
{
switch (ABAddressBookGetAuthorizationStatus())
{
// Update our UI if the user has granted access to their Contacts
case  kABAuthorizationStatusAuthorized:
[self accessGrantedForAddressBook];
break;
// Prompt the user for access to Contacts if there is no definitive answer
case  kABAuthorizationStatusNotDetermined :
[self requestAddressBookAccess];
break;
// Display a message if the user has denied or restricted access to Contacts
case  kABAuthorizationStatusDenied:
case  kABAuthorizationStatusRestricted:
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Privacy Warning"
message:@"Permission was not granted for Contacts."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
break;
default:
break;
}
}

感谢 Mahesh 和 wzbozon,下面的代码对我很有用:

CFErrorRef * error = NULL;
addressBook = ABAddressBookCreateWithOptions(NULL, error);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error)
{
if (granted)
{
dispatch_async(dispatch_get_main_queue(), ^{
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);


for(int i = 0; i < numberOfPeople; i++){
ABRecordRef person = CFArrayGetValueAtIndex( allPeople, i );


NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
NSLog(@"Name:%@ %@", firstName, lastName);


ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);


NSMutableArray *numbers = [NSMutableArray array];
for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
NSString *phoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
[numbers addObject:phoneNumber];
}


NSMutableDictionary *contact = [NSMutableDictionary dictionary];
[contact setObject:name forKey:@"name"];
[contact setObject:numbers forKey:@"numbers"];


[all_contacts addObject:contact];
}
});
}
});

在我最初的答案中,在这个答案的末尾,我展示了如何在9.0之前的 iOS 版本中检索联系人,这种方式解决了这里其他答案带来的一些问题。

但是,如果只支持 iOS9和更高版本,那么应该使用 Contacts框架,避免使用旧的 AddressBook框架时出现的一些恼人的桥接问题。

因此,在 iOS9中,你可以使用 Contacts框架:

@import Contacts;

你还需要更新你的 Info.plist,添加一个 NSContactsUsageDescription来解释为什么你的应用程序需要访问联系人。

然后做下面这样的动作:

CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusDenied || status == CNAuthorizationStatusRestricted) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Access to contacts." message:@"This app requires access to contacts because ..." preferredStyle:UIAlertControllerStyleActionSheet];
[alert addAction:[UIAlertAction actionWithTitle:@"Go to Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alert animated:TRUE completion:nil];
return;
}


CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {


// make sure the user granted us access


if (!granted) {
dispatch_async(dispatch_get_main_queue(), ^{
// user didn't grant access;
// so, again, tell user here why app needs permissions in order  to do it's job;
// this is dispatched to the main queue because this request could be running on background thread
});
return;
}


// build array of contacts


NSMutableArray *contacts = [NSMutableArray array];


NSError *fetchError;
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:@[CNContactIdentifierKey, [CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName]]];


BOOL success = [store enumerateContactsWithFetchRequest:request error:&fetchError usingBlock:^(CNContact *contact, BOOL *stop) {
[contacts addObject:contact];
}];
if (!success) {
NSLog(@"error = %@", fetchError);
}


// you can now do something with the list of contacts, for example, to show the names


CNContactFormatter *formatter = [[CNContactFormatter alloc] init];


for (CNContact *contact in contacts) {
NSString *string = [formatter stringFromContact:contact];
NSLog(@"contact = %@", string);
}
}];

如果在 iOS 9.0之前支持 iOS 版本,以下是我的回答。

--

不仅仅是对你的问题的一些反应,还有这里提供的许多答案(这些答案要么没有请求许可,要么没有正确处理 ABAddressBookCreateWithOptions错误,要么泄露) :

  1. 显然,导入 AddressBook框架:

    #import <AddressBook/AddressBook.h>
    

    或者

    @import AddressBook;
    
  2. You must request permission for the app to access the contacts. For example:

    ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
    
    
    if (status == kABAuthorizationStatusDenied || status == kABAuthorizationStatusRestricted) {
    // if you got here, user had previously denied/revoked permission for your
    // app to access the contacts and all you can do is handle this gracefully,
    // perhaps telling the user that they have to go to settings to grant access
    // to contacts
    
    
    [[[UIAlertView alloc] initWithTitle:nil message:@"This app requires access to your contacts to function properly. Please visit to the \"Privacy\" section in the iPhone Settings app." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
    return;
    }
    
    
    CFErrorRef error = NULL;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
    
    
    if (!addressBook) {
    NSLog(@"ABAddressBookCreateWithOptions error: %@", CFBridgingRelease(error));
    return;
    }
    
    
    ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
    if (error) {
    NSLog(@"ABAddressBookRequestAccessWithCompletion error: %@", CFBridgingRelease(error));
    }
    
    
    if (granted) {
    // if they gave you permission, then just carry on
    
    
    [self listPeopleInAddressBook:addressBook];
    } else {
    // however, if they didn't give you permission, handle it gracefully, for example...
    
    
    dispatch_async(dispatch_get_main_queue(), ^{
    // BTW, this is not on the main thread, so dispatch UI updates back to the main queue
    
    
    [[[UIAlertView alloc] initWithTitle:nil message:@"This app requires access to your contacts to function properly. Please visit to the \"Privacy\" section in the iPhone Settings app." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
    });
    }
    
    
    CFRelease(addressBook);
    });
    
  3. Note that above, I have not used the pattern suggested by others:

    CFErrorRef *error = NULL;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
    

    这是不正确的。正如你在上面看到的,你想要:

    CFErrorRef error = NULL;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
    

    前一种模式不能正确捕获错误,而后一种模式可以。如果 error不是 NULL,不要忘记 CFRelease它(或者像我一样把所有权转移到 ARC) ,否则你将泄露该对象。

  4. 要在联系人中进行迭代,您需要:

    - (void)listPeopleInAddressBook:(ABAddressBookRef)addressBook
    {
    NSArray *allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
    NSInteger numberOfPeople = [allPeople count];
    
    
    for (NSInteger i = 0; i < numberOfPeople; i++) {
    ABRecordRef person = (__bridge ABRecordRef)allPeople[i];
    
    
    NSString *firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
    NSString *lastName  = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
    NSLog(@"Name:%@ %@", firstName, lastName);
    
    
    ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
    
    
    CFIndex numberOfPhoneNumbers = ABMultiValueGetCount(phoneNumbers);
    for (CFIndex j = 0; j < numberOfPhoneNumbers; j++) {
    NSString *phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneNumbers, j));
    NSLog(@"  phone:%@", phoneNumber);
    }
    
    
    CFRelease(phoneNumbers);
    
    
    NSLog(@"=============================================");
    }
    }
    
  5. I want to draw your attention to a fairly key detail, namely the "Create Rule":

    Core Foundation functions have names that indicate when you own a returned object:

    • Object-creation functions that have “Create” embedded in the name;

    • Object-duplication functions that have “Copy” embedded in the name.

    If you own an object, it is your responsibility to relinquish ownership (using CFRelease) when you have finished with it.

    This means that you bear responsibility for releasing any object returned by any Core Foundation function with Create or Copy in the name. You can either call CFRelease explicitly (as I did above with addressBook and phoneNumbers) or, for objects that support toll-free bridging, you can transfer ownership to ARC with __bridge_transfer or CFBridgingRelease (as I did above with allPeople, lastName, firstName, and phoneNumber).

    The static analyzer (press shift+command+B in Xcode or choose "Analyze" from the "Product" menu) can identify many situations in which you neglected to observe this "Create Rule" and failed to release the appropriate objects. So, whenever writing Core Foundation code like this, always run it through the static analyzer to make sure you don't have any obvious leaks.

这适用于 ios 7和 ios 8,我希望它能帮助你... ... ... ... 。

NSMutableArray *result = [[NSMutableArray alloc] init];
CFErrorRef *error = nil;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
__block BOOL accessGranted = NO;


if (ABAddressBookRequestAccessWithCompletion != NULL){
dispatch_semaphore_t sema = dispatch_semaphore_create(0);


ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
accessGranted = granted;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
else{
accessGranted = YES;
}
if (accessGranted){
// If the app is authorized to access the first time then add the contact
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);


for (int i=0; i<numberOfPeople; i++){
CFStringRef phone;
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
CFStringRef firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
CFStringRef lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
NSString *userName = @"NoName";


userName = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
userName = [userName stringByReplacingOccurrencesOfString:@"(null)" withString:@""];


ABMutableMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
CFIndex phoneNumberCount = ABMultiValueGetCount( phoneNumbers );


phone = nil;


for ( CFIndex ind= 0; ind<phoneNumberCount; ind++ ){
CFStringRef phoneNumberLabel = ABMultiValueCopyLabelAtIndex( phoneNumbers, ind);
CFStringRef phoneNumberValue = ABMultiValueCopyValueAtIndex( phoneNumbers, ind);


// converts "_$!<Work>!$_" to "work" and "_$!<Mobile>!$_" to "mobile"
// Find the ones you want here
if (phoneNumberLabel != nil){
NSStringCompareOptions  compareOptions = NSCaseInsensitiveSearch;
if(CFStringCompare(phoneNumberLabel, CFSTR("mobile"),compareOptions)){
phone = phoneNumberValue;
}
phone = phoneNumberValue;


NSStringCompareOptions  compareOptionss = NSCaseInsensitiveSearch;
if(!CFStringCompare(phone, CFSTR("1-800-MY-APPLE"),compareOptionss)){
continue;
}
NSMutableArray *theKeys = [NSMutableArray arrayWithObjects:@"name", @"small_name",@"phone", @"checked", nil];
NSMutableArray *theObjects = [NSMutableArray arrayWithObjects:userName, [userName lowercaseString],phone, @"NO", nil];
NSMutableDictionary *theDict = [NSMutableDictionary dictionaryWithObjects:theObjects forKeys:theKeys];
if (![[functions formatNumber:(__bridge NSString *)(phone)] isEqualToString:[[NSUserDefaults standardUserDefaults]valueForKey:@"phoneNumber"]]){
[result addObject:theDict];
}
}
}
}
}
//sort array
NSSortDescriptor * descriptor = [[NSSortDescriptor alloc] initWithKey:@"small_name"
ascending:YES]; // 1
NSArray * sortedArray = [result sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];

快速版:

override func viewDidLoad() {
super.viewDidLoad()


var error: Unmanaged<CFErrorRef>?


var addressBook: ABAddressBook = ABAddressBookCreateWithOptions(nil, &error).takeRetainedValue()




if ABAddressBookGetAuthorizationStatus() == ABAuthorizationStatus.NotDetermined {
ABAddressBookRequestAccessWithCompletion(addressBook,  {


(granted:Bool, error:CFErrorRef!) -> Void in


self.populateFrom(addressBook: addressBook)


})
}
else if ABAddressBookGetAuthorizationStatus() == ABAuthorizationStatus.Authorized {


self.populateFrom(addressBook: addressBook)


}


}


func populateFrom(#addressBook:ABAddressBook){
let allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
let nPeople = ABAddressBookGetPersonCount(addressBook)
for index in 0..<nPeople{
let person: ABRecordRef = Unmanaged<ABRecordRef>.fromOpaque(COpaquePointer(CFArrayGetValueAtIndex(allPeople, index))).takeUnretainedValue()
let firstName: String = ABRecordCopyValue(person, kABPersonFirstNameProperty).takeUnretainedValue() as? String
println("\(firstName.debugDescription)")




}


}

这是用表视图获取所有联系人的完整演示。

import UIKit
import ContactsUI
import AddressBook
import Contacts


class ShowContactsVC: UIViewController,CNContactPickerDelegate,UITableViewDelegate,UITableViewDataSource
{






@IBOutlet weak var tableView: UITableView!
let peoplePicker = CNContactPickerViewController()
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var contacts = [CNContact]()
var option : Int = 0
var userAccessGranted : Bool = false
var dataArray : NSMutableArray?








override func viewDidLoad()
{
super.viewDidLoad()


peoplePicker.delegate = self


self.checkIfUserAccessGranted()


self.tableView.delegate = self
self.tableView.dataSource = self




navigationController!.navigationBar.barTintColor = UIColor.grayColor()




if(self.userAccessGranted)
{
self.tableView.hidden = false
fetchContacts()


}


}


func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}


func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if dataArray == nil {
return 0;
}
else{




return dataArray!.count
}
}


func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("TableCell", forIndexPath: indexPath) as! ContactsTableViewCell






let data = dataArray![indexPath.row] as! Data;
cell.lblName.text = data.name
cell.imgContact.image = data.image
return cell
}


func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath)
{
cell.backgroundColor = UIColor.cyanColor()


}








func checkIfUserAccessGranted()
{
appDelegate.requestForAccess { (accessGranted) -> Void in
if accessGranted {
self.userAccessGranted = true;
}else{
self.userAccessGranted = false;
}
}
}










func fetchContacts()
{


dataArray = NSMutableArray()


let toFetch = [CNContactGivenNameKey, CNContactImageDataKey, CNContactFamilyNameKey, CNContactImageDataAvailableKey]
let request = CNContactFetchRequest(keysToFetch: toFetch)


do{
try appDelegate.contactStore.enumerateContactsWithFetchRequest(request) {
contact, stop in
print(contact.givenName)
print(contact.familyName)
print(contact.identifier)


var userImage : UIImage;
// See if we can get image data
if let imageData = contact.imageData {
//If so create the image
userImage = UIImage(data: imageData)!
}else{
userImage = UIImage(named: "no_contact_image")!
}


let data = Data(name: contact.givenName, image: userImage)
self.dataArray?.addObject(data)


}
} catch let err{
print(err)


}


self.tableView.reloadData()


}


func contactPickerDidCancel(picker: CNContactPickerViewController)
{
picker.dismissViewControllerAnimated(true, completion: nil)
self.navigationController?.popToRootViewControllerAnimated(true)
}






override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}






}








import UIKit


class Data {




let name : String
let image : UIImage


init(name : String, image : UIImage) {
self.image = image
self.name = name
}


}

如果要按字母顺序排序,可以使用下面的代码。

 CFErrorRef *error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);


CFMutableArrayRef peopleMutable = CFArrayCreateMutableCopy(kCFAllocatorDefault,
CFArrayGetCount(allPeople),
allPeople);


CFArraySortValues(peopleMutable,
CFRangeMake(0, CFArrayGetCount(peopleMutable)),
(CFComparatorFunction) ABPersonComparePeopleByName,
kABPersonSortByFirstName);