Reputation: 8608
When I select a contact using CNContactPickerViewController
that does not have a number associated with it this delegate method is never called.
/*!
* @abstract Invoked when the picker is closed.
* @discussion The picker will be dismissed automatically after a contact or property is picked.
*/
optional public func contactPickerDidCancel(picker: CNContactPickerViewController)
If I select a contact that does have a number it is called. However from the method documentation it seems it should be called no matter what.
My issue is that I need to present a UIAlertController
if the user selects a contact with no number. However I can only do this once the CNContactPickerViewController
is dismissed.
I could get really hacky by using some logic in viewDidAppear
but it seems there should be a cleaner way.
The only remaining delegate methods are:
/*!
* @abstract Singular delegate methods.
* @discussion These delegate methods will be invoked when the user selects a single contact or property.
*/
optional public func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact)
optional public func contactPicker(picker: CNContactPickerViewController, didSelectContactProperty contactProperty: CNContactProperty)
/*!
* @abstract Plural delegate methods.
* @discussion These delegate methods will be invoked when the user is done selecting multiple contacts or properties.
* Implementing one of these methods will configure the picker for multi-selection.
*/
optional public func contactPicker(picker: CNContactPickerViewController, didSelectContacts contacts: [CNContact])
optional public func contactPicker(picker: CNContactPickerViewController, didSelectContactProperties contactProperties: [CNContactProperty])
Which don't help in determining when the CNContactPickerViewController
has actually left the screen.
(Xcode8 / swift2.3 / iOS10)
Upvotes: 2
Views: 1667
Reputation: 861
I agree it's seems weird you need to programmatically dismiss off the picker when it dismisses on its own. BTW for macOS there are delegate methods you need:
- (void)contactPickerWillClose:(CNContactPicker *)picker; // In macOS, called when the contact picker’s popover is about to close.
- (void)contactPickerDidClose:(CNContactPicker *)picker; // In macOS, called when the contact picker’s popover has closed.
Anyway, for some reason these delegate methods are not available for iOS developers. Here is another one approach to deal with this situation: since CNContactPickerViewController
is descender from UIViewController
class, it automatically has such methods:
- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing
- (void)viewDidDisappear:(BOOL)animated; // Called after the view was dismissed, covered or otherwise hidden. Default does nothing
Need to create your own implementation of these methods which will use delegation to tell if CNContactPickerViewController
View
is dismissed or going to be dismissed. Here is my Objective-C example of such kind of implementation:
SKContactPickerViewController.h
#import <ContactsUI/ContactsUI.h>
@protocol SKContactPickerDelegate;
@interface SKContactPickerViewController : CNContactPickerViewController
@property (weak, nonatomic, nullable) id <CNContactPickerDelegate, SKContactPickerDelegate> delegate;
@end
@protocol SKContactPickerDelegate <CNContactPickerDelegate>
@required
- (void)contactPicker:(SKContactPickerViewController *)picker viewDidDisappear:(BOOL)animated;
@optional
- (void)contactPicker:(SKContactPickerViewController *)picker viewWillDisappear:(BOOL)animated;
@end
SKContactPickerViewController.m
#import "SKContactPickerViewController.h"
@implementation SKContactPickerViewController
@dynamic delegate;
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
SEL selector = @selector(contactPicker:viewWillDisappear:);
if ([self.delegate respondsToSelector:selector]) {
[self.delegate contactPicker:self
viewWillDisappear:animated];
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
SEL selector = @selector(contactPicker:viewDidDisappear:);
if ([self.delegate respondsToSelector:selector]) {
[self.delegate contactPicker:self
viewDidDisappear:animated];
}
}
@end
Somewhere in your .m file where you are using Contact Picker:
- (void)addFromContacts {
SKContactPickerViewController *contactPicker = [[SKContactPickerViewController alloc] init];
contactPicker.delegate = self;
// contactPicker.displayedPropertyKeys = @[CNContactEmailAddressesKey];
// etc.
// display controller
[self presentViewController:contactPicker
animated:YES
completion:nil];
}
and finally delegate methods:
- (void)contactPicker:(SKContactPickerViewController *)picker didSelectContact:(CNContact *)contact {
// your usual implementation
}
- (void)contactPicker:(SKContactPickerViewController *)picker viewDidDisappear:(BOOL)animated {
// Present your UIAlertController here
}
It will allow to present UIAlsertController
after contact picker is dismissed. Also this approach still give you ability to use regular delegate methods for fetching data from Contacts UI - delegate property remains from Apple implementation. That is @dynamic delegate;
using for saying this property will be implemented by its superclass. You just extend CNContactPickerDelegate
protocol by your own SKContactPickerDelegate
with two added methods.
Upvotes: 2
Reputation: 16327
You can pop up an alert like this. You could also add a button to "try again" and relaunch the picker.
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
let name = CNContactFormatter.string(from: contact, style: .fullName)
let phones = contact.phoneNumbers
if phones.count == 0 {
let alertController = UIAlertController(title: "Error", message: "\(name) has no phone numbers", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok", style: .default) { (action) in })
picker.dismiss(animated: false){
self.present(alertController, animated: true) {}
}
}
//Do stuff here
}
Upvotes: 3