web-dev-qa-db-ja.com

SwiftでABPeoplePickerNavigationControllerで連絡先を選択するにはどうすればよいですか?

最初のビューコントローラにABPeoplePickerNavigationControllerを追加しました。連絡先を選択したときに他のビューコントローラーに表示する情報を表示したいのですが、コードを使用しようとしていますが、連絡先をクリックしても表示されません。これにより、連絡先がネイティブアプリABPeoplePickerNavigationControllerにのみ開かれます。

var people = ABPeoplePickerNavigationController()
var addressBook: ABAddressBookRef?

func extractABAddressBookRef(abRef: Unmanaged<ABAddressBookRef>!) -> ABAddressBookRef? {
    if let ab = abRef {
        self.view.addSubview(people.view)
        return Unmanaged<NSObject>.fromOpaque(ab.toOpaque()).takeUnretainedValue()
    }
    return nil
}

この機能を試しました

func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!,didSelectPerson person: ABRecordRef!) {

    var unmanagedEmails = ABRecordCopyValue(people, kABPersonEmailProperty)
    let emailObj: ABMultiValueRef = Unmanaged.fromOpaque(unmanagedEmails.toOpaque()).takeUnretainedValue() as NSObject as ABMultiValueRef

    var index = 0 as CFIndex

    var unmanagedEmail = ABMultiValueCopyValueAtIndex(emailObj, index)
    var emailAddress:String = Unmanaged.fromOpaque(unmanagedEmail.toOpaque()).takeUnretainedValue() as NSObject as String

    println(emailAddress)      
}

ありがとう!

17
user3745888

いくつかの考え:

  1. peoplePickerDelegateピッカーコントローラーのpeopleプロパティを設定しましたか?そうしないと、クラスでこれらのメソッドを呼び出そうとしてもわかりません。したがって:

    people.peoplePickerDelegate = self
    presentViewController(people, animated: true, completion: nil)
    
  2. メソッドの例では、peopleを呼び出すときにABRecordCopyValueを参照しています。それがピッカーコントローラーです。パラメータとして渡されたABRecordRef!personを参照するつもりだったと思います。

    また、メールアドレスにアクセスする前に、実際にメールアドレスがあることを確認することもできます。 ABMultiValueGetCountを使用できます。

    fromOpaque/toOpaqueダンスも排除できると思います。

    これにより、

    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. IOS 7もサポートする必要がある場合は、次を使用します。

    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. ただし、ユーザーが最初のメールアドレスしか必要としない場合は、代わりに、クリックして連絡先が持つ可能性のある複数のメールアドレスの1つを選択するようにしてください。したがって、最初に、ピッカーにメールアドレスのみを表示したいことを伝えることにより、「ノイズ」の一部を排除することができます。

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

    次に、これまで説明してきた以前のメソッドを削除し、代わりに実装します。

    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)")
    }
    

    また、iOS 7,0もサポートするために、次のように追加します。

    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. ちなみに、iOS 8には、連絡先を有効にするかどうかを制御する機能があります。 iOS 7および8をサポートしているので、次のように条件付きで使用する必要があります。

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

    これにより、ユーザーにメールアドレスが存在するかどうかをユーザーに視覚的に示し、メールアドレスのないエントリを選択できないようにします。

明らかに、iOS 9以降を使用している場合は、これらすべてを廃止し、ContactsUIフレームワークを使用する必要があります。これにより、コードがさらに簡素化されます。

17
Rob

こちらがiOS 9の最新フレームワークです-ContactsUI

  1. contactsUIをインポートする

  2. CNContactPickerDelegate に準拠(必要なメソッドはありません)

  3. 連絡先ピッカーオブジェクトを作成して表示します。

    let peoplePicker = CNContactPickerViewController()
    
    peoplePicker.delegate = self
    
    self.presentViewController(peoplePicker, animated: true, completion: nil)
    
  4. ContactPickerDidCancelデリゲート関数のCNContactPickerViewControllerを閉じます

    func contactPickerDidCancel(picker: CNContactPickerViewController) {
        picker.dismissViewControllerAnimated(true, completion: nil)
    }
    
  5. 次に、didSelectContactデリゲート関数を使用して連絡先の名前、電話番号、電話番号ラベル、写真にアクセスする方法を示します。

    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")
        }
    }
    }
    
17
Jon Vogel

Swift3 IOS10Jon Vogel の作業バージョンSwift 3およびIOS 10および複数の連絡先の選択をサポートします。

//
//  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)

                }
            }
        }

    }

}
7
Caiçara