How to select a contact with ABPeoplePickerNavigationController in Swift?

后端 未结 3 1238
予麋鹿
予麋鹿 2021-02-05 08:25

I have added the ABPeoplePickerNavigationController into my first view controller. I want that when I select a contact show the info to show in other view controlle

相关标签:
3条回答
  • 2021-02-05 08:30

    A couple of thoughts:

    1. Have you set the peoplePickerDelegate property of the people picker controller? If you don't do that, it won't know to try to call these methods in your class. Thus:

      people.peoplePickerDelegate = self
      presentViewController(people, animated: true, completion: nil)
      
    2. Your example method is referencing people when you call ABRecordCopyValue. That's your picker controller. I assume you meant to reference person, the ABRecordRef! that was passed as a parameter.

      You might also want to make sure you actually have an email address before trying to access it. You can use ABMultiValueGetCount.

      I also think you can also eliminate that fromOpaque/toOpaque dance.

      This yields:

      func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController, didSelectPerson person: ABRecord) {
          let emails: ABMultiValueRef = ABRecordCopyValue(person, kABPersonEmailProperty).takeRetainedValue()
          if ABMultiValueGetCount(emails) > 0 {
              let index = 0 as CFIndex
              let emailAddress = ABMultiValueCopyValueAtIndex(emails, index).takeRetainedValue() as! String
      
              print(emailAddress)
          } else {
              print("No email address")
          }
      }
      
    3. If you need to support iOS 7, too, use:

      func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController, shouldContinueAfterSelectingPerson person: ABRecord, property: ABPropertyID, identifier: ABMultiValueIdentifier) -> Bool {
          let multiValue: ABMultiValueRef = ABRecordCopyValue(person, property).takeRetainedValue()
          let index = ABMultiValueGetIndexForIdentifier(multiValue, identifier)
          let email = ABMultiValueCopyValueAtIndex(multiValue, index).takeRetainedValue() as! String
      
          print("email = \(email)")
      
          peoplePicker.dismissViewControllerAnimated(true, completion: nil)
      
          return false
      }
      
    4. You might, though, rather than assuming the user only wanted the first email address, instead, let them click through and pick one of the possible multiple email addresses the contact had. So, first, you might want to eliminate some of the "noise", by telling the picker that you only want to see email addresses:

      people.peoplePickerDelegate = self
      people.displayedProperties = [NSNumber(int: kABPersonEmailProperty)]
      presentViewController(people, animated: true, completion: nil)
      

      And then, remove the prior method we've been discussing, and instead implement:

      func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson person: ABRecordRef!, property: ABPropertyID, identifier: ABMultiValueIdentifier) {
          let multiValue: ABMultiValueRef = ABRecordCopyValue(person, property).takeRetainedValue()
          let index = ABMultiValueGetIndexForIdentifier(multiValue, identifier)
          let email = ABMultiValueCopyValueAtIndex(multiValue, index).takeRetainedValue() as String
      
          println("email = \(email)")
      }
      

      And to support iOS 7,0, too, you'd add:

      func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController, shouldContinueAfterSelectingPerson person: ABRecord, property: ABPropertyID, identifier: ABMultiValueIdentifier) -> Bool {
          let multiValue: ABMultiValueRef = ABRecordCopyValue(person, property).takeRetainedValue()
          let index = ABMultiValueGetIndexForIdentifier(multiValue, identifier)
          let email = ABMultiValueCopyValueAtIndex(multiValue, index).takeRetainedValue() as! String
      
          print("email = \(email)")
      
          peoplePicker.dismissViewControllerAnimated(true, completion: nil)
      
          return false
      }
      
    5. By the way, iOS 8 offers a feature to control whether a contact is enabled or not. Since you're supporting iOS 7 and 8, you'd want to employ that conditionally, such as:

      if people.respondsToSelector(Selector("predicateForEnablingPerson")) {
          people.predicateForEnablingPerson = NSPredicate(format: "emailAddresses.@count > 0")
      }
      

      This gives the user visual indication whether there is even an email address for the individual, and prevents them from selecting entry without email address.

    Obviously, if using iOS 9 and later, you should retire all of this and use the ContactsUI framework, which simplifies the code further.

    0 讨论(0)
  • 2021-02-05 08:38

    Here is the latest framework for iOS 9 - ContactsUI

    1. import ContactsUI

    2. Conform to the CNContactPickerDelegate (No required methods)

    3. Create a contacts picker object and present it:

      let peoplePicker = CNContactPickerViewController()
      
      peoplePicker.delegate = self
      
      self.presentViewController(peoplePicker, animated: true, completion: nil)
      
    4. Dismiss the CNContactPickerViewController in the contactPickerDidCancel delegate function

      func contactPickerDidCancel(picker: CNContactPickerViewController) {
          picker.dismissViewControllerAnimated(true, completion: nil)
      }
      
    5. Here is how I Accessed a contacts name, phone numbers, phone number labels, and photo using the didSelectContact delegate function:

      func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {
      //Dismiss the picker VC
      picker.dismissViewControllerAnimated(true, completion: nil)
      //See if the contact has multiple phone numbers
      if contact.phoneNumbers.count > 1 {
          //If so we need the user to select which phone number we want them to use
          let multiplePhoneNumbersAlert = UIAlertController(title: "Which one?", message: "This contact has multiple phone numbers, which one did you want use?", preferredStyle: UIAlertControllerStyle.Alert)
          //Loop through all the phone numbers that we got back
          for number in contact.phoneNumbers {
              //Each object in the phone numbers array has a value property that is a CNPhoneNumber object, Make sure we can get that
              if let actualNumber = number.value as? CNPhoneNumber {
                  //Get the label for the phone number
                  var phoneNumberLabel = number.label
                  //Strip off all the extra crap that comes through in that label
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("_", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("$", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("!", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("<", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString(">", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                  //Create a title for the action for the UIAlertVC that we display to the user to pick phone numbers
                  let actionTitle = phoneNumberLabel + " - " + actualNumber.stringValue
                  //Create the alert action
                  let numberAction = UIAlertAction(title: actionTitle, style: UIAlertActionStyle.Default, handler: { (theAction) -> Void in
                      //Create an empty string for the contacts name
                      var nameToSave = ""
                      //See if we can get A frist name
                      if contact.givenName == "" {
                          //If Not check for a last name
                          if contact.familyName == "" {
                              //If no last name set name to Unknown Name
                              nameToSave = "Unknown Name"
                          }else{
                              nameToSave = contact.familyName
                          }
                      }else{
                          nameToSave = contact.givenName
                      }
      
                      // See if we can get image data
                      if let imageData = contact.imageData {
                          //If so create the image
                          let userImage = UIImage(data: imageData)
                      }
                      //Do what you need to do with your new contact information here!
                      //Get the string value of the phone number like this:
                      actualNumber.stringValue  
                  })
                  //Add the action to the AlertController
                  multiplePhoneNumbersAlert.addAction(numberAction)
              }
          }
          //Add a cancel action
          let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: { (theAction) -> Void in
              //Cancel action completion
          })
          //Add the cancel action
          multiplePhoneNumbersAlert.addAction(cancelAction)
          //Present the ALert controller
          self.presentViewController(multiplePhoneNumbersAlert, animated: true, completion: nil)
      }else{
          //Make sure we have at least one phone number
          if contact.phoneNumbers.count > 0 {
              //If so get the CNPhoneNumber object from the first item in the array of phone numbers
              if let actualNumber = contact.phoneNumbers.first?.value as? CNPhoneNumber {
                  //Get the label of the phone number
                  var phoneNumberLabel = contact.phoneNumbers.first!.label
                  //Strip out the stuff you don't need
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("_", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("$", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("!", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("<", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                  phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString(">", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
      
                  //Create an empty string for the contacts name
                  var nameToSave = ""
                  //See if we can get A frist name
                  if contact.givenName == "" {
                      //If Not check for a last name
                      if contact.familyName == "" {
                          //If no last name set name to Unknown Name
                          nameToSave = "Unknown Name"
                      }else{
                          nameToSave = contact.familyName
                      }
                  }else{
                      nameToSave = contact.givenName
                  }
      
                  // See if we can get image data
                  if let imageData = contact.imageData {
                      //If so create the image
                      let userImage = UIImage(data: imageData)
                  }
                  //Do what you need to do with your new contact information here!
                  //Get the string value of the phone number like this:
                  actualNumber.stringValue
              }
          }else{
              //If there are no phone numbers associated with the contact I call a custom funciton I wrote that lets me display an alert Controller to the user
              self.displayAlert("Missing info", message: "You have no phone numbers associated with this contact")
          }
      }
      }
      
    0 讨论(0)
  • 2021-02-05 08:50

    SWIFT3 IOS10 Working version of Jon Vogel for Swift 3 and IOS 10 and support to multiple contacts selection.

    //
    //  Created by JEFFERSON A NEITZKE on 30/01/17.
    //  Copyright © 2017 JEFFERSON A NEITZKE. All rights reserved.
    //
    
    import UIKit
    import ContactsUI
    
    class Principal: UIViewController, CNContactPickerDelegate {
    
        var numeroADiscar: String = ""
        var userImage: UIImage? = nil
        var nameToSave = ""
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            let peoplePicker = CNContactPickerViewController()
            peoplePicker.delegate = self
            self.present(peoplePicker, animated: true, completion: nil)
    
        }
    
        override func didReceiveMemoryWarning() {
    
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
    
        }
    
        func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
    
            picker.dismiss(animated: true, completion: nil)
    
        }
    
        func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
    
            // I only want single selection
            if contacts.count != 1 {
    
                return
    
            } else {
    
                //Dismiss the picker VC
                picker.dismiss(animated: true, completion: nil)
    
                let contact: CNContact = contacts[0]
    
                //See if the contact has multiple phone numbers
                if contact.phoneNumbers.count > 1 {
    
                    //If so we need the user to select which phone number we want them to use
                    let multiplePhoneNumbersAlert = UIAlertController(title: "Which one?", message: "This contact has multiple phone numbers, which one did you want use?", preferredStyle: UIAlertControllerStyle.alert)
    
                    //Loop through all the phone numbers that we got back
                    for number in contact.phoneNumbers {
    
                        //Each object in the phone numbers array has a value property that is a CNPhoneNumber object, Make sure we can get that
                        let actualNumber = number.value as CNPhoneNumber
    
                        //Get the label for the phone number
                        var phoneNumberLabel = number.label
    
                        //Strip off all the extra crap that comes through in that label
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: "_", with: "")
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: "$", with: "")
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: "!", with: "")
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: "<", with: "")
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: ">", with: "")
    
                        //Create a title for the action for the UIAlertVC that we display to the user to pick phone numbers
                        let actionTitle = phoneNumberLabel! + " - " + actualNumber.stringValue
    
                        //Create the alert action
                        let numberAction = UIAlertAction(title: actionTitle, style: UIAlertActionStyle.default, handler: { (theAction) -> Void in
    
                            //See if we can get A frist name
                            if contact.givenName == "" {
    
                                //If Not check for a last name
                                if contact.familyName == "" {
                                    //If no last name set name to Unknown Name
                                    self.nameToSave = "Unknown Name"
                                }else{
                                    self.nameToSave = contact.familyName
                                }
    
                            } else {
    
                                self.nameToSave = contact.givenName
    
                            }
    
                            // See if we can get image data
                            if let imageData = contact.imageData {
                                //If so create the image
                                self.userImage = UIImage(data: imageData)!
                            }
    
                            //Do what you need to do with your new contact information here!
                            //Get the string value of the phone number like this:
                            self.numeroADiscar = actualNumber.stringValue
    
                        })
    
                        //Add the action to the AlertController
                        multiplePhoneNumbersAlert.addAction(numberAction)
    
                    }
    
                    //Add a cancel action
                    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: { (theAction) -> Void in
                        //Cancel action completion
                    })
    
                    //Add the cancel action
                    multiplePhoneNumbersAlert.addAction(cancelAction)
    
                    //Present the ALert controller
                    self.present(multiplePhoneNumbersAlert, animated: true, completion: nil)
    
                } else {
    
                    //Make sure we have at least one phone number
                    if contact.phoneNumbers.count > 0 {
    
                        //If so get the CNPhoneNumber object from the first item in the array of phone numbers
                        let actualNumber = (contact.phoneNumbers.first?.value)! as CNPhoneNumber
    
                        //Get the label of the phone number
                        var phoneNumberLabel = contact.phoneNumbers.first!.label
    
                        //Strip out the stuff you don't need
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: "_", with: "")
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: "$", with: "")
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: "!", with: "")
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: "<", with: "")
                        phoneNumberLabel = phoneNumberLabel?.replacingOccurrences(of: ">", with: "")
    
                        //Create an empty string for the contacts name
                        self.nameToSave = ""
                        //See if we can get A frist name
                        if contact.givenName == "" {
                            //If Not check for a last name
                            if contact.familyName == "" {
                                //If no last name set name to Unknown Name
                                self.nameToSave = "Unknown Name"
                            }else{
                                self.nameToSave = contact.familyName
                            }
                        } else {
                            nameToSave = contact.givenName
                        }
    
                        // See if we can get image data
                        if let imageData = contact.imageData {
                            //If so create the image
                            self.userImage = UIImage(data: imageData)
                        }
    
                        //Do what you need to do with your new contact information here!
                        //Get the string value of the phone number like this:
                        self.numeroADiscar = actualNumber.stringValue
    
                    } else {
    
                        //If there are no phone numbers associated with the contact I call a custom funciton I wrote that lets me display an alert Controller to the user
                        let alert = UIAlertController(title: "Missing info", message: "You have no phone numbers associated with this contact", preferredStyle: UIAlertControllerStyle.alert)
                        let cancelAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
                        alert.addAction(cancelAction)
                        present(alert, animated: true, completion: nil)
    
                    }
                }
            }
    
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题