views:

4510

answers:

4

Update 9/2/10: This code no longer works as of the iOS 4 update--it fails with an internal assertion. There is now a great Address Book API example available in the iPhone SDK Reference Library called "QuickContacts."

The example code is available here; it will help you solve this problem: http://developer.apple.com/iphone/library/samplecode/QuickContacts/Introduction/Intro.html


I'm attempting to add a feature to my app that allows the user to select a contact from an ABPeoplePickerNavigationController, which then displays an ABPersonViewController corresponding to the contact they picked. At that point, I want the user to be able to click on a contact's phone number and have my app respond with custom behavior.

I've got the ABPeoplePickerNavigationController working fine, but I'm running into a problem displaying the ABPersonViewController. I can get the ABPersonViewController to animate onto the screen just fine, but it only displays the contact's photo, name, and company name. None of the contact's other fields are displayed.

I'm using the 'displayedProperties' element in the ABPersonViewController to tell the program to display phone numbers. This creates some strange behavior; when I select a contact that has no phone numbers assigned, the contact shows up with "No Phone Numbers" written in the background (as you'd expect), but when selecting a contact that does have a phone number, all I get is a blank contact page (without the "No Phone Numbers" text).

Here's the method in my ABPeoplePickerNavigationController delegate class that I'm using to create my PersonViewController class, which implements the ABPersonViewController interface:

- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {

    BOOL returnState = NO;

    PersonViewController *personView = [[PersonViewController alloc] init];
    [personView displayContactInfo:person];

    [peoplePicker pushViewController:personView animated:YES];

    [personView release];

    return returnState;
}

Here's my PersonViewController.h header file:

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <AddressBookUI/AddressBookUI.h>

@interface PersonViewController : UIViewController <ABPersonViewControllerDelegate> 
{

}

- (void) displayContactInfo: (ABRecordRef)person;

@end

Finally, here's my PersonViewController.m that's creating the ABPersonViewController to view the selected contact:

#import "PersonViewController.h"

@implementation PersonViewController

- (void) displayContactInfo: (ABRecordRef)person
{
    ABPersonViewController *personController = [[ABPersonViewController alloc] init];

    personController.personViewDelegate = self;
    personController.allowsEditing = NO;
    personController.displayedPerson = person;
    personController.addressBook = ABAddressBookCreate();

    personController.displayedProperties = [NSArray arrayWithObjects:
            [NSNumber numberWithInt:kABPersonPhoneProperty], 
            nil];

    [self setView:personController.view];

    [[self navigationController] pushViewController:personController animated:YES];
    [personController release];
}

- (BOOL) personViewController:(ABPersonViewController*)personView shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue
{
    return YES;
}


@end

Does anyone have any idea as to why I'm seeing this blank Contact screen instead of one with clickable phone number fields?

+1  A: 

Alright, I think I'm getting closer to finding the problem. I'm fairly sure it's with this part of the displayContactInfo call above:

[self setView:personController.view];

When I omit this line, all I see is a blank screen when I click a contact. The ABPeoplePickerNavigationController is pushing the PersonViewController onto the NavigationController stack. The PersonViewController then instantiates an ABPersonViewController object, but for whatever reason the ABPersonViewController never gets properly added to the NavigationController stack.

Does anyone know how to add the ABPersonViewController to the stack, rather than just the PersonViewController?

Maha
How about to use ABPersonViewController directly. Not sure why do you use PersonViewController as a container to hold ABPersonViewController. I guess PeronViewController is just your customized ViewController, right?
David.Chu.ca
Did you tried to subclass ABPersonViewController like PersonViewController: ABPersonViewController {...}?
David.Chu.ca
+2  A: 

Just a heads-up to anyone who runs into this problem themselves: I was able to product the correct behavior by instantiating the ABPersonViewController in its delegate's viewDidLoad() method as below:

As before, here's my ABPeoplePickerNavigationController delegate's method:

- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
    BOOL returnState = NO;

    PersonViewController *personView = [[PersonViewController alloc] init];

    [peoplePicker pushViewController:personView animated:YES];
    [personView displayContactInfo:person];

    [personView release];

    return returnState;
}

Here's my PersonViewController.h (ABPersonViewController delegate) header file:

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <AddressBookUI/AddressBookUI.h>

@interface PersonViewController : UIViewController <ABPersonViewControllerDelegate> 
{
    ABPersonViewController *personController;
}

- (void) displayContactInfo: (ABRecordRef)person;

@end

Finally, here's the delegate's implementation (PersonViewController.m):

#import "PersonViewController.h"

@implementation PersonViewController

- (void) viewDidLoad
{
    personController = [[ABPersonViewController alloc] init];

    [personController setPersonViewDelegate:self];
    [personController setAllowsEditing:NO];
    personController.addressBook = ABAddressBookCreate();   

    personController.displayedProperties = [NSArray arrayWithObjects:
        [NSNumber numberWithInt:kABPersonPhoneProperty], 
        nil];

    [self setView:personController.view];
}

- (void) viewDidUnload
{
    [personController release];
}

- (void) displayContactInfo: (ABRecordRef)person
{
    [personController setDisplayedPerson:person];
}

- (BOOL) personViewController:(ABPersonViewController*)personView shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue
{
    // This is where you pass the selected contact property elsewhere in your program
    [[self navigationController] dismissModalViewControllerAnimated:YES];
    return NO;
}

@end

Hopefully this ends up being helpful for someone. The AddressBook UI framework was a bit tricky for me to wrap my head around (although I'm new to iPhone development so I'm still learning a lot of the nuances of iPhone program organization).

Maha
A: 

Thank U for publishing your solution!!! This really helped me !

I have 2 more question if I may: I cant see the: "Text message" and the "Add to favorites" options in the view (like in the iphone) and also there are no bar buttons to go back to my App , or to "Edit" the contact

did you added those buttons yourself or it can be done ?

Thanks

Itay

Itay
No problem, glad it helped you! I didn't add those buttons to my app. It's definitely possible to add them yourself, although any "favorites" have to be exclusive to your app--from what I understand, you can't access the iPhone's favorite contacts from your own app.
Maha
A: 

I got it working sorta....

I placed

-(BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {

in the FlipsideViewController. Which is the place where I want to make the selection of the phoneNumber.

I added the PersonViewController.h and .m files.

I added the following case to the method:

-(BOOL) personViewController:(ABPersonViewController*)personView shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue

Case :

CFStringRef value = ABMultiValueCopyValueAtIndex(multi, index);

NSLog(@"PersonViewController value = %@", (NSString *)value);

Does someone can tell me how I pass the value found in the case back into the FlipsideViewController, where I can set it to a label in the view itself.

I tried the following:

Added a property NSString *selectedPhone to the PersonViewController.h. Added the following line in the case, just below NSLog():

selectedPhone = (NSString *)value;

In my thinking this should place a value in the selectedPhone string

In the flipsideViewController I added the following line to method:

-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person

[peoplePicker pushViewController:personView animated:YES]; [personView displayContactInfo:person]; self.defaultPhone.text = [personView selectedPhone]; //this is the label I want to set.

[personView release];

The label is set to empty.

What am I doing wrong?

Also did the reverse:

in the case added the following lines:

FlipsideViewController *fVC = [[FlipsideViewController alloc] init];

fVC.defaultPhone.text = (NSString *)value;

[fVC release];

Also not the result i'm looking for....

Arnold
Solved it using the following link:http://stackoverflow.com/questions/2330615/iphone-abpeoplepickernavigationcontroller-how-to-select-two-single-entries-of-t
Arnold