I have an iPhone app that uses a ABPeoplePickerNavigationController to pick out a contact. First time you select a contact, the contact list obviously starts at the top, under the letter 'A'. However, say you selected a contact under 'M'; if you then later want to change which contact is selected, I want to initialise the contact list so that it's already scrolled to the 'M' section.
Here's the code I use to open the contact list:
ABPeoplePickerNavigationController *picker =
[[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
[picker release];
I can't find any information in the reference guide on how to configure the scroll offset where the controller opens.
This functionality is unavailable in ABPeoplePickerNavigationController. However you can implement a custom UITableViewController which does that using the AddressBook framework.
Try to use the [ABAddressBook]
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for( int i = 0 ; i < nPeople ; i++ )
{
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i );
NSString* name = [(NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty) autorelease];
}
Related
In my app, I am fetching the e-mail id from address box using PersonPicker view.
When i select any e-mail id, i try to open the e-mail dialog. But it will just open & close immediatly.
I can't able to solve this issue.
I got code from Here
My code is as follow:
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
// Get the first and the last name. Actually, copy their values using the person object and the appropriate
// properties into two string variables equivalently.
// Watch out the ABRecordCopyValue method below. Also, notice that we cast to NSString *.
NSString *firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
// Compose the full name.
NSString *fullName = #"";
// Before adding the first and the last name in the fullName string make sure that these values are filled in.
if (firstName != nil) {
fullName = [fullName stringByAppendingString:firstName];
}
if (lastName != nil) {
fullName = [fullName stringByAppendingString:#" "];
fullName = [fullName stringByAppendingString:lastName];
}
// Get the multivalue e-mail property.
CFTypeRef multivalue = ABRecordCopyValue(person, property);
// Get the index of the selected e-mail. Remember that the e-mail multi-value property is being returned as an array.
CFIndex index = ABMultiValueGetIndexForIdentifier(multivalue, identifier);
// Copy the e-mail value into a string.
email = (NSString *)ABMultiValueCopyValueAtIndex(multivalue, index);
NSLog(#"%#",email);
// Create a temp array in which we'll add all the desired values.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
[tempArray addObject:fullName];
// Save the email into the tempArray array.
[tempArray addObject:email];
// Now add the tempArray into the contactsArray.
[contactsArray addObject:tempArray];
NSLog(#"%#",contactsArray);
// Release the tempArray.
[tempArray release];
// Reload the table to display the new data.
[table reloadData];
// Dismiss the contacts view controller.
[contacts dismissModalViewControllerAnimated:YES];
[contacts release];
MFMailComposeViewController* Apicker = [[MFMailComposeViewController alloc] init];
if (Apicker != nil)
{
[Apicker setSubject:#""];
NSString * someString = nil;
someString=#"Google";
[Apicker setMessageBody:someString isHTML:YES];
NSArray *toRecipients = [NSArray arrayWithObjects:email, nil];
[Apicker setToRecipients:toRecipients];
Apicker.mailComposeDelegate = self;
[self presentModalViewController:Apicker animated:YES];
[Apicker release];
}
return NO;
}
I think it may be the issue of dismiss & present the modal view.
your problem with dismiss and present is that the two overlap. -- dismiss it and THEN show it kinda like you did BUT you run into a problem because the stuff is animated. dont animate there or delay the presetting till after the dismissal
What I trying to do, is get all contacts and relies if the contact have SocialProfileProperty or not, after get all this contacts that have SocialProfileProperty I will make simple filtration to know if it is Facebook,Twitter,..
Before I post this question I west full day trying to find any solution for this problem ??
But it is allows return 0 ??
ABAddressBookRef addressBook;
CFErrorRef *erer = NULL;
addressBook = ABAddressBookCreateWithOptions(NULL, erer);
CFArrayRef _allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex _nPeople = ABAddressBookGetPersonCount(addressBook);
NSMutableArray *socialArray = [[NSMutableArray alloc] init];
ABMultiValueRef socialProfiles;
for (int i=0;i<_nPeople; i++) {
ABRecordRef _person=CFArrayGetValueAtIndex( _allPeople,i);
NSNumber *recordId = [NSNumber numberWithInteger:ABRecordGetRecordID(_person)];
NSLog(#"%#",recordId);
socialProfiles = ABRecordCopyValue(_person, kABPersonSocialProfileProperty);
if (ABMultiValueGetCount(socialProfiles)>0) {
[socialArray addObject:(__bridge id)(socialProfiles)];
}
}
NSLog(#"%d",socialArray.count);
It is allows return 0 ,until the user marge the Social contacts with native and this does
not happened until he try to delete the Facebook account for example in that case alert appear
and ask if u wanna to marge contacts or not ??? other wise return 0 values :)
So this method will never work until the user marge the FaceBook Contacts with native contacts.
I want to display a ABAddressBook that only shows contacts with an email, so I tried something like this:
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
CFIndex nPeople = ABAddressBookGetPersonCount( addressBook );
for( CFIndex emailIndex = 0; emailIndex < nPeople; emailIndex++ ) {
ABRecordRef person = CFArrayGetValueAtIndex( allPeople, emailIndex );
ABMutableMultiValueRef emailRef=ABRecordCopyValue(person, kABPersonEmailProperty);
int emailCount = ABMultiValueGetCount(emailRef);
if(emailCount == 0) {
ABAddressBookRemoveRecord(addressBook, person, NULL);
}
}
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.addressBook = addressBook;
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
The controller shows, but all the contacts are shown, and if I select the ones without an email, I get a crash. If I called ABAddressBookSave(), then it removes all contacts with an email, but it is a permanent change which even deletes them from the system contacts. What's the right way to do this?
You might need to build an array of contacts that have email, then display it on UITableViewController. This is a way to build such array: https://stackoverflow.com/a/13980023/745862. You can use ABPersonViewController or ABUnknownPersonViewController to display contact details.
Have you considered creating a temporary AddressBook and filling it with those contacts that have email addresses?
You remove all contacts without email address from AddressBook, but didn't save the AddressBook at the end. When the AddressBook is loaded from ABPeoplePickerNavigationController, all previous changes will be lost!
Try this:
if (ABAddressBookHasUnsavedChanges(addressBook))
ABAddressBookSave(addressBook, NULL)
BTW, don't forget to CFRelease() addressBook, allPeople and emailRef.
Did you try to show email values only like this:
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.displayedProperties = #[#(kABPersonEmailProperty)];
Perhaps that already does, what you want? Didn't test it though.
From iOS 8 the ABPeoplePickerNavigationController has a property that resolve your problem:
#property (nonatomic,copy) predicateForEnablingPerson
Code example from Apple doc: https://developer.apple.com/library/prerelease/ios/samplecode/PeoplePicker/Listings/PeoplePicker_AAPL_8or7_EmailPickerViewController_m.html)
ABPeoplePickerNavigationController* abPicker = [ABPeoplePickerNavigationController new];
abPicker.peoplePickerDelegate = self;
//...
if ([abPicker respondsToSelector:#selector(setPredicateForEnablingPerson:)]){
// The people picker will enable selection of persons that have at least one email address.
abPicker.predicateForEnablingPerson = [NSPredicate predicateWithFormat:#"emailAddresses.#count > 0"];
}
Note that from iOS9 the ABPeoplePickerNavigationController is deprecated, you can use the new CNContactPickerViewController in the same way with the property:
#property(nonatomic, copy) NSPredicate *predicateForEnablingContact
From the doc:
You can set a value for this property to determine which contact should become selectable, such as emailAddresses.#count > 0 to enable all the contacts that have an email address to become selectable. If no predicate is set for this property, all contacts become selectable.
I'm having a bit of an issue with using the address book to get the names of the contacts from the device into my own contacts view within my application.
The code I have works fine on the emulator but I when tested on an iPhone 4 it will crash, the application seems to work fine if there are two or less contacts but 3 or more and the application crashes.
Here is the code I am using to get the names of contacts into an array.
ABAddressBookRef addressBook;
bool wantToSaveChanges = YES;
bool didSave;
CFErrorRef error = NULL;
addressBook = ABAddressBookCreate();
listOfContacts = [[NSMutableArray alloc]init];
int i;
int len = (int) ABAddressBookGetPersonCount(addressBook);
for(i = 1; i< (len+1); i++){
ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID) i);
NSString* name = (NSString *)ABRecordCopyCompositeName(person);
ABMultiValueRef number = (NSString *)ABRecordCopyValue(person,kABPersonPhoneProperty);
NSString *mobileNum = (NSString *)ABMultiValueCopyValueAtIndex(number, 0 );
NSLog(#"Name = %#", name);
NSLog(#"Number = %#", mobileNum);
[listOfContacts addObject:name];
[name release];
[mobileNum release];
}
if(ABAddressBookHasUnsavedChanges(addressBook)){
if(wantToSaveChanges){
didSave = ABAddressBookSave(addressBook, &error);
if(!didSave){
//Error
}
}
else{
ABAddressBookRevert(addressBook);
}
}
When it crashes this is the line that gets highlighted in Xcode:
NSString* name = (NSString *)ABRecordCopyCompositeName(person);
And the error states:
Thread 1: Program received signal: "EXC_BAD_ACCESS"
Can anyone see what the problem might be? I dont understand why it would work on the emulator but not on the device? And also why it works for up to two contacts but not 3 or more??
Just a guess:
ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID) i);
This line looks fishy for me. I doubt that the record IDs are numbered from 1 to whatever. Especially if you have deleted an entry.
This would explain why it works on the simulator, I guess you just added some test contacts and never deleted one.
Here is how I solved it:
(Note how I make sure to call only active records, also I created a customized Contact class)
This code takes care of edge case like: email/phone doesn't exist, or exists more than once...
+(NSArray *)getAddressBook{
ABAddressBookRef addressBook;
bool wantToSaveChanges = YES;
bool didSave;
CFErrorRef error = NULL;
addressBook = ABAddressBookCreate();
NSMutableArray *listOfContacts = [[NSMutableArray alloc]init];
CFArrayRef array=ABAddressBookCopyArrayOfAllPeople(addressBook);
int len=CFArrayGetCount(array);
for (int i = 0; i<len; i++){
ABRecordRef person = CFArrayGetValueAtIndex(array, i);
if (ABRecordGetRecordType(person)==kABPersonType){
NSString *firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
ABMultiValueRef emails = (ABMultiValueRef)ABRecordCopyValue(person,kABPersonEmailProperty);
ABMultiValueRef numbers = (ABMultiValueRef)ABRecordCopyValue(person,kABPersonPhoneProperty);
int sumEmails=ABMultiValueGetCount(emails);
int sumNumbers=ABMultiValueGetCount(numbers);
for (int j=0; j<(sumNumbers>sumEmails?sumNumbers:sumEmails); j++) {
ACL_AB_Contact *contact=[[ACL_AB_Contact alloc]initWithFirstName:firstName LastName:lastName];
if (j<sumEmails){
contact.emailAddress=(NSString *)ABMultiValueCopyValueAtIndex(emails,j);
}
if (j<sumNumbers){
contact.phoneNumber=(NSString *)ABMultiValueCopyValueAtIndex(numbers,j);
}
[contact logContact];
[listOfContacts addObject:contact];
[contact release];
}
}
}
if(ABAddressBookHasUnsavedChanges(addressBook)){
if(wantToSaveChanges){
didSave = ABAddressBookSave(addressBook, &error);
if(!didSave){
//Error
}
}
else{
ABAddressBookRevert(addressBook);
}
}
return [listOfContacts autorelease];
}
The record IDs are dynamic. It means that if you add 2 contacts and then remove the first, you will have only a contact with id "2". So I wouln't use a for statement to get through the contacts. Follow the Address Book Programming Guide
I have an app that displays ABAddressBook contacts in a UITableView. Currently I'm reading the contacts into an NSDictionary, however this appears to crash for some users, which I suspect is a memory issue.
Is there another approach to display ABAddressBook contacts in a UITableView without either first storing them in an NSDictionary or using ABPeoplePicker?
A different way using ARC:
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef addressBookData = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex count = CFArrayGetCount(addressBookData);
NSMutableArray *contactsArray = [NSMutableArray new];
for (CFIndex idx = 0; idx < count; idx++) {
ABRecordRef person = CFArrayGetValueAtIndex(addressBookData, idx);
NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
if (firstName) {
NSDictionary *dict = [NSDictionary dictionaryWithObject:firstName ForKey:#"name"];
[contactsArray addObject:dict];
}
}
CFRelease(addressBook);
CFRelease(addressBookData);
You can use following way,
ABAddressBookRef ab = ABAddressBookCreateWithOptions(NULL, NULL);
NSArray *arrTemp = (NSArray *)ABAddressBookCopyArrayOfAllPeople(ab);
The above 2 lines will create an array for all your contacts on the iPhone.
Now whatever property of a contact you want to display you can display by using the below code. For example, I want to display the first name of all contacts and then create one Mutable array called it arrContact.
NSMutableArray *arrContact = [[NSMutableArray alloc] init];
for (int i = 0; i < [arrTemp count]; i++)
{
NSMutableDictionary *dicContact = [[NSMutableDictionary alloc] init];
NSString *str = (NSString *) ABRecordCopyValue([arrTemp objectAtIndex:i], kABPersonFirstNameProperty);
#try
{
[dicContact setObject:str forKey:#"name"];
}
#catch (NSException * e) {
[dicContact release];
continue;
}
[arrContact addObject:dicContact];
[dicContact release];
}
Now just display it using the arrContact array in a table view..
Same as Abizern's answer, but if you want to display full names that are localized, use ABRecordCopyCompositeName. (In English names are "First Last", but in Chinese names are "LastFirst").
ABRecordRef person = CFArrayGetValueAtIndex(addressBookData, idx);
NSString *fullName = (__bridge_transfer NSString *)ABRecordCopyCompositeName(person);//important for localization