iOS8 bugs: ABPersonViewController does not display properties & back button

1.4k Views Asked by At

The following code works correctly in iOS7, but not in iOS8 (the variable recordID is set correctly):

    CFErrorRef error = nil;
    const ABAddressBookRef addressBook = (ABAddressBookCreateWithOptions (NULL, &error));
    ABRecordRef contactRef = ABAddressBookGetPersonWithRecordID (addressBook, recordID);
    ABPersonViewController *personViewController = [[ABPersonViewController alloc] init];
    personViewController.addressBook     = addressBook;
    personViewController.displayedPerson = contactRef;
    CFRelease(addressBook);
    NSArray *displayedProperties = @[@(kABPersonFirstNameProperty),
                                     @(kABPersonLastNameProperty),
                                     @(kABPersonMiddleNameProperty),
                                     @(kABPersonPrefixProperty),
                                     @(kABPersonSuffixProperty),
                                     @(kABPersonOrganizationProperty),
                                     @(kABPersonJobTitleProperty),
                                     @(kABPersonDepartmentProperty),
                                     @(kABPersonEmailProperty),
                                     @(kABPersonBirthdayProperty),
                                     @(kABPersonKindProperty),
                                     @(kABPersonAddressProperty),
                                     @(kABPersonPhoneProperty),
                                     @(kABPersonInstantMessageProperty),
                                     @(kABPersonURLProperty),
                                     @(kABPersonSocialProfileProperty),
                                     @(kABPersonNoteProperty)];
    personViewController.displayedProperties  = displayedProperties;
    personViewController.navigationItem.title = NSLocalizedString(@"CONTACT_DETAILS", nil);
    personViewController.allowsActions        = YES;
    personViewController.allowsEditing        = YES; // if NO, no back button is shown
    personViewController.personViewDelegate   = self;
    personViewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"ADDRESSES",nil) style:UIBarButtonItemStylePlain target:self action:@selector(personviewDoneButtonPressed:)];

    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:personViewController];
    [self presentViewController:navigationController animated:YES completion:nil];

Bugs in iOS8:

  1. When allowsEditing is set to YES, the contact is shown, but only the name is displayed. The navigation bar shows left the back button (named "Addresses") and right the edit button. When the edit button is pressed, the contact is displayed with all fields empty except the name, and the edit button is displayed as a done button. If this done button is pressed without any editing before, all information about the contact is displayed.
  2. When allowsEditing is set to NO, no back button is shown, so that the screen can no longer be left.

Has anybody a workaround?

UPDATE:

I realized by now, that problem 1 only sometimes occurs on the simulator, although always on my device.

2

There are 2 best solutions below

5
Reinhard Männer On BEST ANSWER

montuno (thanks again), who commented my first answer, got a hint from Apple technical support how to solve the 1st problem, and I understand now also the 2nd problem:

1) Only part of the contact displayed:

Apple claimed that the following code is wrong:

ABRecordRef contactRef = ABAddressBookGetPersonWithRecordID (addressBook, recordID);
ABPersonViewController *personViewController = [[ABPersonViewController alloc] init];
personViewController.addressBook     = addressBook;
personViewController.displayedPerson = contactRef;
CFRelease(addressBook);

Here addressBook is erroneously released, after it has been assigned to the ABPersonViewController.

In my original code, I did not have the CFRelease statement. But then the static analyzer warned of a „Potential leak of an object stored in addressBook“. After I inserted the release statement, the build succeeded without warning, so I assumed that the personViewController retained the addressBook, which is apparently not the case, at least not with iOS8. I removed now the release statement again, got the warning back, but everything works fine.

2) Missing back button:
The docs of the ABPersonViewController say „IMPORTANT Person view controllers must be used with a navigation controller in order to function properly.

So what I did was the following:
I set up an ABPersonViewController, assigned to its navigationItem.backBarButtonItem a „Done“ button, and initialized a new UINavigationController with this personViewController, using initWithRootViewController:. Then I presented the new navigationController modally.
The personViewController with the back button was displayed, and when pressed, the presenting view controller dismissed the modally presented navigation controller.
This worked fine on iOS < 8, but in iOS8, the back button was not displayed, when allowsEditing was set to NO.

But this is apparently not how Apple wants to display the personViewController:
I think Apple assumes that a navigationController already exists, and presents the current view, so that a new personViewController is just pushed onto the stack of the navigationController. In this case, the personViewController is not the rootViewController of the navigationController, and the navigationController displays automatically a back button (which is not done for the rootViewController). If implemented this way, everything works fine also for iOS8.

9
Reinhard Männer On

UPDATE: This hack does no longer work and is completely useless (see my new answer)

If somebody has the same problem 1) described above, here is a really dirty workaround until Apple fixes the bug (my bug reports are still open):
Since it is in certain situations required to toggle the edit button twice after the ABPersonViewController screen is shown, one can do this programmatically:

Define a property for the UINavigationController that presents the ABPersoViewController:

@property (nonatomic, strong) UINavigationController *  navigationControllerForPersonViewController;

After the navigation controller has presented the person view controller, perform a selector to do the toggling:

[self performSelector:@selector(toggleEditButton) withObject:nil afterDelay:0.7];  

The delay is set so that the person view controller screen can appear with animation. The method toggleEditButton is defined as:

- (void)toggleEditButton
{
    UIBarButtonItem *editButton  = self.navigationControllerForPersonViewController.topViewController.navigationItem.rightBarButtonItem;
    id target = editButton.target;
    SEL editAction = editButton.action;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [target performSelector:editAction withObject:editButton];
    [target performSelector:editAction withObject:editButton];
#pragma clang diagnostic pop
}

The #pragmas are required to suppress compiler warnings for potential memory leaks.
The result is that all selected information of the person view controller is actually displayed.