Using and customizing the Address Book UI framework

Apple provides a nice framework for incorporating Address Book access  into your iOS application, using  the familiar UI from the Contacts app.  They provide some nice documentation on this UI in the section User Interaction: Prompting for and Displaying Data.

Here is an example of the UI that Apple indicates is available:

AddressBookUIFramework

The default UI is a little different

The above view of the controllers looks great, but getting them to work together as the images imply is not quite so straightforward.  Consider the following code snippet used to present the People Picker:

ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];

[picker setPeoplePickerDelegate:self];

[self presentViewController:picker animated:YES completion:NULL];

Using this code  and the related protocol methods described in the documentation, there is actually a Cancel button instead of the + button that is shown above for the People Picker.   What I really wanted was a Cancel button and a + button for adding new contacts.

For the Person View Controller, there is actually a Cancel button in place of the Edit button that is shown above.

Customizing the Navigation Buttons

I found a great article by Scott Sherwood titled iOS5: Removing the cancel button on ABPeoplePickerNavigationController.

Scott discusses the problems mentioned above and shows how to fix the navigation buttons using the UINavigationControllerDelegate protocol method navigationController:willShowViewController:animated:

Scott also provided a demo project to complement the article.

As noted in one of the article comments though, there is a bug in the example wherein the Cancel is not handled correctly when editing a contact.

A fix to Scott Sherwood’s AddressBookDemo project

When in edit mode, Scott assigns a custom Done button to the right Bar Button item.   The fix is to also add a custom Cancel button to the left Bar Button item in the edit method:

self.picker.topViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(personViewCancel:)];

The problem though is what to do in the cancel method personViewCancel: ?

After searching through the documentation, I could not find an easy way to cancel an edit operation and avoid committing any changes.   If you leave editing mode as we do in the method associated with the Done button, changes are committed.

The only means I could come up with of actually canceling the edit without a commit was to dismiss the People Picker altogether:

[self dismissViewControllerAnimated:YES completion:NULL];

Not an ideal solution, since we really want to go back to the Person View for that contact after a cancel.

Solving the problem from a different angle

I decided to take Scott Sherwood’s solution to fix the People Picker interface, but use a different approach for the Person View Controller.

My different approach starts in the protocol method peoplePickerNavigationController:shouldContinueAfterSelectingPerson: for the ABPeoplePickerNavigationControllerDelegate.

I refer to Method 1 as Scott Sherwood’s approach and Method 2 as my own.

// Displays the information of a selected person
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
    NSLog(@"peoplePickerNavigationController:shouldContinueAfterSelectingPerson:");

    // First method allows ABPeoplePickerNavigationController to present ABPersonViewController
    //
#ifdef PERSON_VIEW_CONTROLLER_METHOD1
    return YES;  // YES to continue and show details for selected person

    // Second method presents ABPersonViewController manually
    //
#else
    ABPersonViewController *view = [[ABPersonViewController alloc] init];

    view.personViewDelegate = self;
    view.displayedPerson = person; // Assume person is already defined.
    view.allowsEditing = YES;
    view.allowsActions = YES;

    [peoplePicker pushViewController:view animated:YES];

	return NO;
#endif

}

The difference as shown above is that I present the Person View Controller manually instead of allowing the People Picker Controller to do so.  This lets me set the property allowsEditing to YES to get the navigation buttons and behavior that I’m looking for.

Refreshing the People Picker table view

One problem I noted with my approach was that the People Picker table view of names was not automatically updated after a record edit (it was with Scott Sherwood’s approach).   I was able to fix this by using an Address Book callback method (to alert me to changes in the Address Book), along with a dummy save to the Address Book when needed.

GitHub demo project

github_icon

I created a demo project at GitHub called AddressBookPeoplePicker.

The project shows both methods (Scott Sherwood’s and mine) and is controlled by the define PERSON_VIEW_CONTROLLER_METHOD1 in AddressBookPeoplePickerViewController.m.   The default is Method 2 (uncomment define for Method 1).

I’ve included detailed comments discussing how I addressed several issues (including the refresh issue mentioned in the previous section).

Advertisements

6 responses to “Using and customizing the Address Book UI framework

  1. First of all thanks for this nice demo. I am using your code to my “Contact Organiser app”. I have created some tabs at the bottom of the tab bar controller where there is a Contacts tab. I used your code there. But the problem is when i am hitting your “Contacts button” then the contacts view is popping up from bottom to top (modal transaction/segue) and covering up the whole tab bar control. I want to put here a push segue. so that the contacts can come up on the same view controller,and the tab bar will be visible. I think you got me what i am trying to say. So how can i do that from your code ? Please advise me that where the changes will need on your code to do as i want.

    Thanks in advance for your help.

    Like

    • You could create an empty view controller and using this method should make your view controller AllContacts view controller:

      – (void)awakeFromNib
      {
      ABPeoplePickerNavigationController * peoplePicker = [[ABPeoplePickerNavigationController alloc] init];
      peoplePicker.peoplePickerDelegate = self;
      [self.view addSubview:peoplePicker.view];
      [self addChildViewController:peoplePicker];
      [peoplePicker didMoveToParentViewController:nil];
      }

      Then you can use it inside your tab bar view controller (or any other way your heart desires)

      Hope it helps.

      (Don’t forget to import the AddressBook and AddressBookUI)

      Like

      • Islam, thank you for your code! Now AddressBook appears where it should. But the problem is now that my application does not do anything when the user chooses a person from AddressBook (or even presses “Cancel”). That is, now it never executes
        – (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person{
        ….
        }

        Like

  2. Glad that the demo was useful. I honestly don’t know if you can accomplish what you are trying to do. When you present an instance of ABPeoplePickerNavigationController, you are showing a view controller that has a specific interface. I am not sure how you can prevent it from covering up the tab bar.

    Like

  3. Pingback: The development of Contacts2Web | Finalize.com: My journey with iOS and other code adventures

  4. hi, i made a similar app. In order to set the PeoplePicker controller inside a tab bar, i have subclassed the controller and overridden the allowsCancel method (returning NO). Anyway with iOS7 subclassing is forbidden, even if it works until now.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s