ABPeoplePickerNavigationController’s SelectPerson event

MarcoSeraphinMarcoSeraphin Marco SeraphinDEMember, University
edited September 2014 in Xamarin.iOS

Hi,

I am using the ABPeoplePickerNavigationController to show up all contacts for choosing a contact.

BUT I did not get a xxx.SelectPerson event at all !!!

Tried the Xamarin sample here : http://developer.xamarin.com/recipes/ios/shared_resources/contacts/choose_a_contact
But it didn't work also.

Here's my code:

        this.btSendMail.TouchUpInside += delegate
                    {
                        this.PresentViewController(this.contactController, true, null);
                    };

                    this.contactController.Cancelled += delegate 
                    {
                        this.DismissViewController(true, null);
                    };

                    this.contactController.SelectPerson += delegate(object sender, ABPeoplePickerSelectPersonEventArgs e)
                {
                    this.selectedPerson = e.Person;
                    this.DismissViewController (true, null);
                };

Anything changed with iOS 8 ?

The contact view controller appears, but when I can only choose a contact, see its detail information, but how do I select this contact to fire up an .SelectPerson event ?

Best

Marco

Posts

  • JoeAshbrookJoeAshbrook Joe Ashbrook USMember
    edited September 2014

    Well I wish I had a fix but Apple seems to confirm this has changed.

    https://developer.apple.com/library/ios/releasenotes/General/RN-iOSSDK-8.0/

    [Excerpt]
    "The Address Book UI people picker has been changed for iOS 8. A new mode with new API has been added where the app does not need access to the user’s contacts and the user will not be prompted for access. A temporary copy of the selected person is returned to the app. See ABPeoplePickerNavigationController.h for more details.

    See the new PeoplePicker: Picking a Person or Property sample project demonstrating usage of the new mode."

    So I assume something may have also changed for ABPeoplePickerSelectPersonEventArgs

    Nothing has been documented on http://iosapi.xamarin.com/ for this...

  • XamTwoXamTwo Xam Two USMember

    We are seeing the same issue. Any fixes yet?

  • CarlJamesonCarlJameson Carl Jameson USMember

    Getting the same issue here, is there any fix yet?

  • CagatayOzgurCagatayOzgur Cagatay Ozgur USUniversity

    Same issue here, any suggestions to a workaround much appreciated

  • CagatayOzgurCagatayOzgur Cagatay Ozgur USUniversity

    Seems like implementing the ABPeoplePickerNavigationControllerDelegate is a probable workaround

  • Chris.4142Chris.4142 Chris USMember

    Does anyone have a solution to this? I have tried using a delegate but that also does not seem to be working.

  • MarcoSeraphinMarcoSeraphin Marco Seraphin DEMember, University

    Hi all,

    I did not wait and currently I a switched to a Xamarin.Forms based app, although I first develope the iOS version.

    I am using the Xamarin.Mobile component for accessing the address book and I am showing the contacts in a Xamarin.Forms page.

    Here is the code for reading all address book contacts with at least one eMail address.

    public List<ContactItem> GetContactList()
            {
                var contactList = new List<ContactItem>();
                var addressBook = new AddressBook ();
    
                if (!addressBook.RequestPermission().Result)
                {
                    // We don't have the permission to access user's contacts: check if the READ_CONTACTS has been set
                    UIAlertView noAddressBookAccess = new UIAlertView("Address book access", "Without address book access you can not choose a contact", null, "Ok", null);
                    noAddressBookAccess.Show();
                }
                else
                {
                    var contacts = addressBook.Where(c => c.Emails.Any(o => o.Address != String.Empty)).OrderBy(c => c.LastName);
                    if (contacts.Any())
                    {
                        foreach (var contact in contacts)
                        {
                            foreach (var eMailContact in contact.Emails)
                            {
                                var newContact = new ContactItem();
                                newContact.Name = contact.FirstName + " " + contact.LastName;
                                newContact.eMailAddress = eMailContact.Address;
    
                                int foundContactCount = (from c in contactList
                                    where c.Name.ToUpperInvariant().Equals(newContact.Name.ToUpperInvariant()) && 
                                    c.eMailAddress.Equals(newContact.eMailAddress)
                                    select c).Count();
    
                                if (foundContactCount == 0)
                                {
                                    contactList.Add(newContact);
                                }
                            }
                        }
                    }                    
                }
    
                return contactList;
            }
    

    ContactItem is just a simple class with a Name and eMailAdresse string property.

    I am using this code using a DependencyService call.

    Best

    Marco

  • HenningMosandStephansenHenningMosandStephansen Henning Mosand Stephansen USMember

    We just stumbled upon this issue this morning. I wrote this quicky prototype class in Notepad for dealing with it asynchronously:

    public class AsyncPeoplePicker
        {
            private ABPeoplePickerNavigationController _pickerController;
            private PickerDelegate _pickerDelegate;
    
            public AsyncPeoplePicker()
            {
                _pickerController = new ABPeoplePickerNavigationController();
    
                _pickerController.Delegate = _pickerDelegate = new PickerDelegate();
            }
    
            public Task<ABPerson> SelectPersonAsync(UIViewController presentingController)
            {
                var taskCompletionSource = new TaskCompletionSource<ABPerson>();
    
                PickerDelegate eventHandler = null;
    
                eventHandler = ((sender, args) =>
                {
                    _pickerDelegate.SelectPerson -= eventHandler;
    
                    taskCompletionSource.TrySetResult(args.SelectedPerson);
                });
    
                _pickerDelegate.SelectPerson += eventHandler;
    
                presentingController.PresentViewController(_pickerController, true, null);
    
                return taskCompletionSource.Task;
            }
    
            private class PickerEventArgs
            {
                public object Sender { get; set; }
                public ABPerson SelectedPerson { get; set; }
    
                public PickerEventArgs(object sender, ABPerson selectedPerson)
                {
                    Sender = sender;
                    SelectedPerson = selectedPerson;
                }
            }
    
            private class PickerDelegate : ABPeoplePickerNavigationControllerDelegate
            {
                public delegate void SelectPersonEventHandler(PickerEventArgs eventArgs);
                public event SelectPersonEventHandler SelectPerson;
    
                public override void DidSelectPerson(ABPeoplePickerNavigationController peoplePicker, ABPerson selectedPerson)
                {
                    peoplePicker.DismissViewController(true, null);
    
                    if (SelectPerson != null)
                    {
                        SelectPerson(new PickerEventArgs(this, selectedPerson));
                    }
    
                }
    
                public override void Cancelled(ABPeoplePickerNavigationController peoplePicker)
                {
                    peoplePicker.DismissViewController(true, null);
                }
            }
    

    Then adjust it to your use and add some error handling.

    Usage:

    var peoplePicker = new AsyncPeoplePicker();
    
    var person = await peoplePicker.SelectPersonAsync(thisViewController);
    
  • JoeAshbrookJoeAshbrook Joe Ashbrook USMember
    edited September 2014

    Using Henning Mosand Stephansen code I was able to get this working again. Thx man.

    Notes for others

    I had to modify this method so it would run on the main thread, otherwise the picker controller would never be visible. Also be sure that eventHandler is actually an event handler by adding SelectPersonEventHandler:

    `public Task SelectPersonAsync(UIViewController presentingController)
    {
    var taskCompletionSource = new TaskCompletionSource ();
    UIApplication.SharedApplication.InvokeOnMainThread (new NSAction (() => {

                PickerDelegate.SelectPersonEventHandler eventHandler = null;
    
                eventHandler = ((args) => {
                    _pickerDelegate.SelectPerson -= eventHandler;
    
                    taskCompletionSource.TrySetResult (args.SelectedPerson);
                });
    
                _pickerDelegate.SelectPerson += eventHandler;
    
                presentingController.PresentViewController (_pickerController, true, null);
            }));
    
            return taskCompletionSource.Task;
        }`
    

    Use the Task.Factory.StartNew() method to use it:

    Task.Factory.StartNew(async () => { var person = await peoplePicker.SelectPersonAsync(this); Console.WriteLine(person.FirstName); });

  • HenningMosandStephansenHenningMosandStephansen Henning Mosand Stephansen USMember

    I see, in the case you are running this on another thread then yes, that is a requirement :)

  • XamTwoXamTwo Xam Two USMember

    FYI - After implementing this (THANK YOU for the solution!) we've found it does not work on iOS 7. Just so everyone's aware :)

  • softlionsoftlion Benjamin Mayrargue FRBeta ✭✭✭

    A versions which works on uis7+8

        /// <summary>
        /// IOS 8 compatible ABPeoplePickerNavigationController
        /// </summary>
        public class AsyncPeoplePicker
        {
            public ABPeoplePickerNavigationController PickerController { get; private set; }
            private readonly PickerDelegate pickerDelegate;
            private TaskCompletionSource<ABPerson> tcs;
            private readonly bool isIos8 = UIDevice.CurrentDevice.CheckSystemVersion(8, 0);
    
            public AsyncPeoplePicker()
            {
                PickerController = new ABPeoplePickerNavigationController();
                pickerDelegate = new PickerDelegate();
                pickerDelegate.SelectPerson += PickerDelegateOnSelectPerson;
                pickerDelegate.Cancel += PickerDelegateOnCancel;
    
                if (isIos8)
                {
                    PickerController.Delegate = pickerDelegate;
                }
                else
                {
                    PickerController.SelectPerson += (sender, args) =>
                    {
                        pickerDelegate.FireSelectPerson(args.Person);
                        // Don't show contact's property page
                        args.Continue = false;
                    };
    
                    PickerController.Cancelled += (sender, args) =>
                    {
                        pickerDelegate.FireCancelled();
                    };
                }
            }
    
            public Task<ABPerson> SelectPersonAsync(UIViewController presentingController)
            {
                tcs = new TaskCompletionSource<ABPerson>();
                presentingController.PresentViewController(PickerController, true, null);
                return tcs.Task;
            }
    
            private void PickerDelegateOnSelectPerson(object sender, ABPeoplePickerSelectPersonEventArgs args)
            {
                if (tcs != null)
                {
                    tcs.TrySetResult(args.Person);
                    tcs = null;
                }
            }
    
            private void PickerDelegateOnCancel(object sender, EventArgs eventArgs)
            {
                if (tcs != null)
                {
                    tcs.TrySetResult(null);
                    tcs = null;
                }
            }
    
            /// <summary>
            /// iOS 8: delegate replaced ios7 event
            /// </summary>
            private class PickerDelegate : ABPeoplePickerNavigationControllerDelegate
            {
                public event EventHandler<ABPeoplePickerSelectPersonEventArgs> SelectPerson;
                public event EventHandler<EventArgs> Cancel;
    
                public void FireSelectPerson(ABPerson person)
                {
                    var handler = SelectPerson;
                    if(handler!=null)
                        handler(this, new ABPeoplePickerSelectPersonEventArgs(person));
                }
    
                public void FireCancelled()
                {
                    var handler = Cancel;
                    if(handler!=null)
                        handler(this, EventArgs.Empty);
                }
    
                public override void DidSelectPerson(ABPeoplePickerNavigationController peoplePicker, ABPerson selectedPerson)
                {
                    FireSelectPerson(selectedPerson);
                }
    
                public override void Cancelled(ABPeoplePickerNavigationController peoplePicker)
                {
                    FireCancelled();
                }
    
                /// <summary>
                /// Don't show contact's property page
                /// </summary>
                /// <param name="peoplePicker"></param>
                /// <param name="selectedPerson"></param>
                /// <returns></returns>
                public override bool ShouldContinue(ABPeoplePickerNavigationController peoplePicker, IntPtr selectedPerson)
                {
                    return false;
                }
            }
        }
    
Sign In or Register to comment.