App crash on use of PeoplePicker, but not in same view - ios

Update 2, I hope this helps someone, there is a solutions at the following link: https://discussions.apple.com/thread/5498630?start=0&tstart=0 , evidently this is an iOS bug and this work around works. I can create the new sharedPicker, but I cannot get anything from it or dismiss it, I'm not sure how to format beyond what is supplied at the link
Any help on that is very welcome.
So my question now is how to take the following code and actually create the code for peoplePickerNavigationControllerDidCancel: and peoplePickerNavigationController:shouldContinueAfterSelectingPerson:
Thanks. I've left most of my original post in case someone has a similar vague issue.
// Convoluted workaround for the iPhone 4S crash
+ (ABPeoplePickerNavigationController *)sharedPeoplePicker {
static ABPeoplePickerNavigationController *_sharedPicker = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedPicker = [[ABPeoplePickerNavigationController alloc] init];
});
return _sharedPicker;
}
// then later on, use
[YourController sharedPeoplePicker].delegate = self;
// etc.
My current code:
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
[self displayPerson:person];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
return NO;
}
- (void)peoplePickerNavigationControllerDidCancel:
(ABPeoplePickerNavigationController *)peoplePicker
{
//[self dismissViewControllerAnimated:YES completion:nil];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)pick1:(id)sender
{
ABPeoplePickerNavigationController *picker1 =[[ABPeoplePickerNavigationController alloc] init];
picker1.peoplePickerDelegate = self;
[self presentViewController:picker1 animated:YES completion:nil];
x=1;
}
Update 1, this app crashes on iPhone 4/4s, but runs in the simulator and iPhone5 if that means anything. I'm thinking its just they have enough power to get past whatever leak I have created
I have an iOS app with a view controller where the user can chose contacts for the app using ABPeoplePickerNavigationController or enter numbers manually. If numbers are entered manually there are no issues. If the user opens and:
Picks a new contact from the address book
Updates a contact from the address book to use in the app
Opens and cancels the address book (all without saving the action)
Then I cannot navigate to one particular view in my app without a crash. I am at a loss why I cannot go to this one view controller, or why it causes the crash.
I am using 5 different pickers, one for each contact I want to add and potentially save. I save as NSUserDefaults, but like I said, the crash persists even if the picker selection is never saved. I can navigate to all view in the app from a sidebar navigation without incident, the only thing different about the view I fail on is that it is presented from one of the main view controllers and not my sidebar.
I appreciate any help or thoughts. This was the first app I wrote and I'm trying to update it and failing. I want to get it functional again so I can come back and refactor it.
My implementation:
- (IBAction)pick1:(id)sender
{
ABPeoplePickerNavigationController *picker1 =
[[ABPeoplePickerNavigationController alloc] init];
picker1.peoplePickerDelegate = self;
[self presentViewController:picker1 animated:YES completion:nil];
x = 1;
}
- (IBAction)pick2:(id)sender
{
ABPeoplePickerNavigationController *picker2 =
[[ABPeoplePickerNavigationController alloc] init];
picker2.peoplePickerDelegate = self;
[self presentViewController:picker2 animated:YES completion:nil];
x=2;
}
- (IBAction)pick3:(id)sender
{
ABPeoplePickerNavigationController *picker3 =
[[ABPeoplePickerNavigationController alloc] init];
picker3.peoplePickerDelegate = self;
[self presentViewController:picker3 animated:YES completion:nil];
x=3;
}
- (IBAction)pick4:(id)sender
{
ABPeoplePickerNavigationController *picker4 =
[[ABPeoplePickerNavigationController alloc] init];
picker4.peoplePickerDelegate = self;
[self presentViewController:picker4 animated:YES completion:nil];
x=4;
}
- (IBAction)pick5:(id)sender
{
ABPeoplePickerNavigationController *picker5 =
[[ABPeoplePickerNavigationController alloc] init];
picker5.peoplePickerDelegate = self;
[self presentViewController:picker5 animated:YES completion:nil];
x=5;
}
- (void)peoplePickerNavigationControllerDidCancel:
(ABPeoplePickerNavigationController *)peoplePicker
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person {
[self displayPerson:person];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
return NO;
}
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
return NO;
}
- (void)displayPerson:(ABRecordRef)person
{
NSString* name = (__bridge_transfer NSString*)ABRecordCopyValue(person,
kABPersonFirstNameProperty);
NSString* phone = nil;
ABMultiValueRef phoneNumbers = ABRecordCopyValue(person,
kABPersonPhoneProperty);
if (ABMultiValueGetCount(phoneNumbers) > 0) {
phone = (__bridge_transfer NSString*)
ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
} else {
phone = #"[None]";
}
if (x==1){
firstName1.text = name;
contact1.text = phone;
}
if (x==2){
firstName2.text = name;
contact2.text = phone;
}
if (x==3){
firstName3.text = name;
contact3.text = phone;
}
if (x==4){
firstName4.text = name;
contact4.text = phone;
}
if (x==5){
firstName5.text = name;
contact5.text = phone;
}
}

Sorry to answer my own question, but another work around to this that requires little change is to use CF Retain to correct the over release I was experiencing. I retained the person and the peoplePicker and all was resolved. Thanks for everyone who tried to help me solve this.
- (void)peoplePickerNavigationControllerDidCancel:
(ABPeoplePickerNavigationController *)peoplePicker
{
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
CFRetain((__bridge CFTypeRef)(peoplePicker));
}
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
[self displayPerson:person];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
CFRetain(person);
CFRetain((__bridge CFTypeRef)(peoplePicker));
return NO;
}

Thank You for the work around this issue. Your method #2 really works!!!
I have the similar situation discussed in https://discussions.apple.com/thread/5498630?start=0&tstart=0 : MKMapView is placed by IB in storyboard, no code except IBOutlet. I present ABPeoplePickerController then simply cancel it by dismiss and leave this view by navigation. Then return back and get memory issue: called method barStyle of zombie UINavigationBar at address of dismissed ABPeoplePickerController. This situation happened only on iOS 7 (iPad 3rd ten and iPhone 4S) but code works fine on iOS 6 (iPhone 3GS) and only with combination with MKMapView. I test with WebView instead of MapView and all works fine. I think it is real bug in iOS 7.
Kind regards
Alexey

Well, there is also a bit more simple solution to this. The actual problem is in using ABPeoplePickerNavigationController as a singleton object, setting its delegate to a view controller and then dismissing the view controller. So, in my case the solution that worked is this one:
- (void)peoplePickerNavigationControllerDidCancel:
(ABPeoplePickerNavigationController *)peoplePicker
{
peoplePicker.peoplePickerDelegate = nil; // clear delegate prior to dismissing self
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
[self displayPerson:person];
peoplePicker.peoplePickerDelegate = nil; // clear delegate prior to dismissing self
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
return NO;
}

Related

iOS 9 custom transition - animationControllerForDismissedController not called

I am a newbee in iOS development and recently run into this problem with customized transition in iOS 9.
I have an object conforms to UIViewControllerTransitioningDelegate protocol and implements animationControllerForDismissedController, something like:
#implementation MyCustomizedTransitioningDelegate
#pragma mark - UIViewControllerTransitioningDelegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
MyCustomizedTransitionAnimator *animator = [[MyCustomizedTransitionAnimator alloc] init];
animator.presenting = NO;
return animator;
}
#end
And the process that triggers the modal transition is something like:
#implementation MyViewController
#pragma mark - Initializers
+ (MyCustomizedTransitioningDelegate *)modalTransitioningDelegateSingletonInstance;
{
static MyCustomizedTransitioningDelegate *delegateInst = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
delegateInst = [[MyCustomizedTransitioningDelegate alloc] init];
});
return delegateInst;
}
#pragma mark - UIViewController
- (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion;
{
[self prepareForDismissViewControllerAnimated:animated completion:&completion];
[super dismissViewControllerAnimated:animated completion:completion];
}
- (void)prepareForDismissViewControllerAnimated:(BOOL)animated completion:(dispatch_block_t *)completion;
{
self.presentedViewController.modalPresentationStyle = UIModalPresentationCustom;
self.presentedViewController.transitioningDelegate = [[self class] modalTransitioningDelegateSingletonInstance];
}
#end
Since animationControllerForDismissedController method is not called, the MyCustomizedTransitionAnimator is not created, which leads to its animateTransition not called either, which causes unexpected problem in my app. (Sorry for my poor English...)
I am also attaching the screenshot of stack trace for both iOS8 & iOS9.
In iOS 8, animationControllerForDismissedController is called after the stack trace below.
But in iOS9, transitionDidFinish is called somehow in advance, which I guess probably prevent animationControllerForDismissedController being called?
I was wondering if this is an iOS 9 bug or not. Any explanation or work around solution will be greatly appreciated!
I faced the same issue.
I hope this will help someone.
What fixed it for me is to make the object which applies UIViewControllerTransitioningDelegate protocol as variable instance to keep strong relationship with it.
I think because it gets dismissed after the view is presented first time.
I had the same issue.
Turned out I needed to set the delegate on the navigationController of the UIViewController that contains the trigger button.
Having this old code that didn't work:
UIViewController *dvc = [self sourceViewController];
TransitionDelegate *transitionDelegate = [TransitionDelegate new];
dvc.modalPresentationStyle = UIModalPresentationCustom;
dvc.transitioningDelegate = transitionDelegate;
[dvc dismissViewControllerAnimated:YES completion:nil];
I changed the first line to:
UIViewController *dvc = [self sourceViewController].navigationController;
and it worked.
Hope this helps.
You need to say something like:
MyDestinationViewController *viewController = [[MyDestinationViewController alloc] init];
MyCustomizedTransitioningDelegate *transitioningDelegate = [[MyCustomizedTransitioningDelegate alloc]init];
viewController.transitioningDelegate = transitioningDelegate;
viewController.modalPresentationStyle = UIModalPresentationCustom;
[self presentViewController: viewController animated:YES completion:nil];
Or if you're using segues, in prepareForSegue say something like:
MyDestinationViewController *toVC = segue.destinationViewController;
MyCustomizedTransitioningDelegate *transitioningDelegate = [[MyCustomizedTransitioningDelegate alloc]init];
toVC.transitioningDelegate = transitioningDelegate;

UIDocumentInteractionController stopped working iOS 8

I have a PDF file saved in the document directory. The path to the file is stored in a NSString property 'self.URL'.
Here is my code to present the activity items:
-(void)presentDocumentInteractionController{
self.docController = [UIDocumentInteractionController interactionControllerWithURL:self.URL];
self.docController.delegate = self;
[_docController setUTI:#"com.adobe.pdf"];
[_docController presentOptionsMenuFromBarButtonItem:self.activityBarButton animated:YES];
}
Before iOS 8 this code worked fine. The user was presented with a list of items such as print, copy and email to choose from. After upgrading to iOS 8 /XCode 6, I'm getting this runtime error (it doesnt crash the app):
Unknown activity items supplied: (
"<UITextViewPrintFormatter: 0x7f908ba53ca0>"
)
How can I solve this problem?
I have the same problem and have switched to using UIActivityViewController. However this makes the apps capable of opening the PDF no longer show up, so maybe that's not what you want.
Minimal Solution:
If you want to do minimal work, you don't even need to read your PDF into NSData, use a NSURL as activity item and iOS seems to know what to do:
- (void)share:(id)sender
{
UIActivityViewController *activity =
[[UIActivityViewController alloc] initWithActivityItems:#[self.URL]
applicationActivities:nil];
if ([activity respondsToSelector:#selector(popoverPresentationController)]) {
activity.popoverPresentationController.barButtonItem = <# BAR BUTTON ITEM #>;
}
[self presentViewController:activity animated:YES completion:NULL];
}
Original Answer:
Make your view controller adhere to the UIActivityItemSource protocol, then you can do:
- (void)share:(id)sender
{
self.pdfData = [NSData dataWithContentsOfURL:self.URL];
UIActivityViewController *activity = [[UIActivityViewController alloc] initWithActivityItems:#[self] applicationActivities:nil];
if ([activity respondsToSelector:#selector(popoverPresentationController)]) {
activity.popoverPresentationController.barButtonItem = <# BAR BUTTON ITEM #>;
}
[self presentViewController:activity animated:YES completion:NULL];
}
Adhering to the protocol if you have a PDF file is relatively simple. You can of course optimize and return smaller NSData and even a preview image, but minimally do this:
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
return _pdfData;
}
- (id)activityViewController:(UIActivityViewController *)activityViewController
itemForActivityType:(NSString *)activityType
{
return _pdfData;
}
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController
subjectForActivityType:(NSString *)activityType
{
return self.title;
}
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController
dataTypeIdentifierForActivityType:(NSString *)activityType
{
return #"com.adobe.pdf";
}
Use
- (BOOL)presentOpenInMenuFromBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated;
Instead of
- (BOOL)presentOptionsMenuFromBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated;
This solved the problem for me:
ObjC:
dispatch_async(dispatch_get_main_queue(), ^() {
[_docController presentOptionsMenuFromRect:button.bounds inView:button animated:YES];
});
Swift:
if let docController = UIDocumentInteractionController(URL: url) {
dispatch_async(dispatch_get_main_queue()) {
docController.presentPreviewAnimated(true)
}
} else {
// handle nil docController
}
I don't use swift so this code might not work.

Custom text over ABPeoplePickerNavigationController "All Contacts" header

In Apple Calendar app, if you inviting people to event you will see specific text under header:
In my app I'm using people picker too. I would like to add a custom hint for a user over All Contacts header.
IBAction for a button:
- (IBAction)showPicker:(UIBarButtonItem *)sender {
self.addressBookController = [[ABPeoplePickerNavigationController alloc] init];
[self.addressBookController setPeoplePickerDelegate:self];
self.addressBookController.displayedProperties = [NSArray arrayWithObjects:
[NSNumber numberWithInt:kABPersonPhoneProperty],
nil];
[self presentViewController:self.addressBookController animated:YES completion:nil];
}
All delegate methods:
#pragma mark Address Book Delegate
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
[self.addressBookController dismissViewControllerAnimated:YES completion:nil];
}
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
return YES;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
ABMultiValueRef phonesRef = ABRecordCopyValue(person, property);
CFStringRef currentPhoneValue = ABMultiValueCopyValueAtIndex(phonesRef, identifier);
// Some custom code working with a phone number
return NO;
}
I don't think there is a way to customize the ABPeoplePickerNavigationController anyhow.
You can create a UIViewController with a UILabel (with your hint for users) and a container view; then embed ABPeoplePickerNavigationController into the container view. This can be easily achieved via Interface Builder and also can be done programmatically, see the Implementing a Container View Controller section in UIViewController's Class Reference Overview.
Update
Unfortunately it seems that ABPeoplePickerNavigationController can't be added directly to the Storyboard; please refer to this answer to see a workaround.

strange 48 byte leaks in app using ABPeoplePickerNavigationController and Core Data

At this point I'm not sure if these leaks might be CoreData related or what, since I've experienced 48 byte strdup leaks in other parts of this same app for apparently different reasons - see my other question:another stack overflow question
But, assuming no relation, I have a viewController which, based on the user selecting an option, presents an ABPeoplePicker. However, it seems like just by presenting the picker I'm leaking, regardless of choosing a contact or not.
The code for presenting the picker is:
- (void)showPeoplePickerController
{
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.displayedProperties = [NSArray arrayWithObject:[NSNumber numberWithInt:kABPersonEmailProperty]];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
[picker release];
}
And the delegate methods implemented as follow:
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
[self dismissModalViewControllerAnimated:YES];
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
return YES;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
ABMultiValueRef emails = ABRecordCopyValue(person, property);
if(userEmailString)
[userEmailString release];
userEmailString = (NSString*)ABMultiValueCopyValueAtIndex(emails, identifier);
CFRelease(emails);
[[NSNotificationCenter defaultCenter] postNotificationName:#"recipientEmailDidUpdateNotification"
object:self];
return NO;
}
And just in case, userEmailString is a retained NSString property of the controller (meaning I could also go for self.userEmailString = blah).
These are screenshots from Instruments, reporting the leak. But notice that it thinks its the picker not being released, though I am calling release after presenting it. And I've also tried doing CFRelease() instead... but still the same.
Anyway, yep.. the leaks are in the SDK.

Merge contact details with existing contact

I'm trying to select a user in the addressBook using ABPeoplePickerNavigationController then once a user has been selected, within the delegate method
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person;
I wish to add to the ABRecordRef and then edit/save this record with ABPersonViewController.
My problem is what navigationController to attach the ABPersonViewController to. Any help welcome.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
ABPersonViewController *personView = [ABPersonViewController new];
[self populate:person withData:self.personData];
personView.displayedPerson = person;
personView.personViewDelegate = self;
personView.allowsEditing = YES;
[peoplePicker.navigationController pushViewController:personView animated:YES];
// [self dismissPicker:peoplePicker];
return YES;
}
Were you able to resolve this? The Apple QuickContacts example presents ABPeoplepickerNavigationController and ABPersonViewController from separate buttons rather than combining them which suggests they were not meant to work together...

Resources