如何在Swift中将CNContact电话号码作为字符串获得?

35

我正在尝试使用Swift在iOS中检索所有联系人的姓名和电话号码,并将它们放入数组中。我已经完成了这一步骤:

func findContacts() -> [CNContact] {

    marrContactsNumber.removeAllObjects()
    marrContactsName.removeAllObjects()

    let store = CNContactStore()

    let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]

    let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch)

    var contacts = [CNContact]()

    do {
        try store.enumerateContactsWithFetchRequest(fetchRequest, usingBlock: { (let contact, let stop) -> Void in
            contacts.append(contact)

            self.marrContactsName.addObject(contact.givenName + " " + contact.familyName)

            self.marrContactsNumber.addObject(contact.phoneNumbers)

            print(contact.phoneNumbers)
    }
    catch let error as NSError {
        print(error.localizedDescription)
    }

    print(marrContactsName.count)
    print(marrContactsNumber.count)

    return contacts
}
完成后,marrContactsName 包含了我所有联系人姓名的数组,正如预期的那样。例如:"John Doe"。然而,marrContactsNumber 返回一组类似于... 的值。 Once completed, marrContactsName 包含了我所有联系人姓名的数组,正如预期的那样。例如:"John Doe"。然而,marrContactsNumber 返回一组类似于... 的值。
[<CNLabeledValue: 0x158a19950: identifier=F831DC7E-5896-420F-AE46-489F6C14DA6E,
label=_$!<Work>!$_, value=<CNPhoneNumber: 0x158a19640: countryCode=us, digits=6751420000>>,
<CNLabeledValue: 0x158a19a80: identifier=ECD66568-C6DD-441D-9448-BDEDDE9A68E1,
label=_$!<Work>!$_, value=<CNPhoneNumber: 0x158a199b0: countryCode=us, digits=5342766455>>]

我想知道如何仅作为字符串值检索电话号码,即“XXXXXXXXXX”。基本上,如何调用数字值。谢谢!

11个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
40
我找到了解决方案:(contact.phoneNumbers[0].value as! CNPhoneNumber).valueForKey("digits") as! String

26
"valueForKey("digits")" 不是公共 API,你应该使用属性 "stringValue":(contact.phoneNumbers.first?.value as? CNPhoneNumber)?.stringValue - HamzaGhazouani
6
在Swift 3中,HamzaGhazouani的代码 ((contact.phoneNumbers.first?.value)! as CNPhoneNumber).stringValue 的翻译是:将联系人电话号码的第一个值转换成CNPhoneNumber类型,然后获取其字符串表示形式。 - Rajamohan S
这对我来说一直有效,除非号码前面有国家前缀('+1')。我一直在研究,但找不到我要的东西。有人知道如何仅获取10位美国号码吗? - Jake
算了,我决定这样做:var phoneNumber = (number.value as! CNPhoneNumber).value(forKey: "digits") as! String if phoneNumber .contains("+1") { phoneNumber = "\(phoneNumber.dropFirst(2))" } 然后我得到了想要的结果。 - Jake
大家好,你能告诉我这段用于Xamarin.ios的代码吗? - Sumit Mishra
就我个人而言,我发现使用 valueForKey("digits") 比 .stringvalue 在更干净的字符串格式化方面能够给我带来更好的结果。是的,这不是公共 API,但我现在愿意冒险一试。 - AMC08

23

您可以从CNLabeledValue中获取contact.phoneNumbers:

for phoneNumber in contact.phoneNumbers {
  if let number = phoneNumber.value as? CNPhoneNumber,
      let label = phoneNumber.label {
      let localizedLabel = CNLabeledValue.localizedStringForLabel(label)
      print("\(localizedLabel)  \(number.stringValue)")
  }
}

9
还有一个要跨过的障碍。第4行变成 let localizedLabel = CNLabeledValue<CNPhoneNumber>.localizedString(forLabel: label) - AlexT

15
/* Get only first mobile number */

    let MobNumVar = (contact.phoneNumbers[0].value as! CNPhoneNumber).valueForKey("digits") as! String
    print(MobNumVar)

/* Get all mobile number */

    for ContctNumVar: CNLabeledValue in contact.phoneNumbers
    {
        let MobNumVar  = (ContctNumVar.value as! CNPhoneNumber).valueForKey("digits") as? String
        print(MobNumVar!)
    }

 /* Get mobile number with mobile country code */

    for ContctNumVar: CNLabeledValue in contact.phoneNumbers
    {
        let FulMobNumVar  = ContctNumVar.value as! CNPhoneNumber
        let MccNamVar = FulMobNumVar.valueForKey("countryCode") as? String
        let MobNumVar = FulMobNumVar.valueForKey("digits") as? String

        print(MccNamVar!)
        print(MobNumVar!)
    }

2
非常方便(尽管有些不规范的变量命名方式) - woody121

4
这是在Swift 4中的操作方法:
func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {

    if let phoneNo = contactProperty.value as? CNPhoneNumber{
        txtPhone.text = phoneNo.stringValue
    }else{
        txtPhone.text=""
    }
}

我只使用了选择“numberProperty”的方法,这对我很有帮助。谢谢。 - Mahdi Moqadasi

4
这里是一个Swift 5的解决方案。
import Contacts

func sendMessageTo(_ contact: CNContact) {

    let validTypes = [
        CNLabelPhoneNumberiPhone,
        CNLabelPhoneNumberMobile,
        CNLabelPhoneNumberMain
    ]

    let numbers = contact.phoneNumbers.compactMap { phoneNumber -> String? in
        guard let label = phoneNumber.label, validTypes.contains(label) else { return nil }
        return phoneNumber.value.stringValue
    }

    guard !numbers.isEmpty else { return }

    // process/use your numbers for this contact here
    DispatchQueue.main.async {
        self.sendSMSText(numbers)
    }
}

您可以在CNPhoneNumber头文件中找到validTypes数组的可用值。

它们包括:

CNLabelPhoneNumberiPhone
CNLabelPhoneNumberMobile
CNLabelPhoneNumberMain
CNLabelPhoneNumberHomeFax
CNLabelPhoneNumberWorkFax
CNLabelPhoneNumberOtherFax
CNLabelPhoneNumberPager

1
在Swift 3中,您可以直接获取。
 if item.isKeyAvailable(CNContactPhoneNumbersKey){
        let phoneNOs=item.phoneNumbers
        let phNo:String
        for item in phoneNOs{
            print("Phone Nos \(item.value.stringValue)")
        }

1

CNLabeledValue的定义:

CNLabeledValue类是一个线程安全的类,它定义了一个不可变的值对象,将联系人属性值与标签结合在一起。例如,联系人电话号码可以有家庭、工作、iPhone等标签。

CNContact.phoneNumbers是一个CNLabeledValues数组,每个CNLabeledValue都有一个标签和一个值。

要打印与CNContact对应的phoneNumbers,您可以尝试:

for phoneNumber in contact.phoneNumbers {
    print("The \(phoneNumber.label) number of \(contact.givenName) is: \(phoneNumber.value)")
}

3
谢谢您的回复。现在,phoneNumber.value 返回 <CNPhoneNumber: 0x1502dcd00: countryCode=us, digits=+18062907644>。我该如何让它只显示号码? - Baylor Mitchell

1
保持简单:
let phoneNumbers: [String] = contact.phoneNumbers.compactMap { (phoneNumber: CNLabeledValue) in
    guard let number = phoneNumber.value.value(forKey: "digits") as? String else { return nil }
    return number
}

1

适用于 Swift 5+

func removeSpecialCharactersFromContactNumberOfUser(_ contactNo : String) -> String? {

    let digits = CharacterSet(charactersIn: "0123456789").inverted
    let modifiedContactNo = contactNo.components(separatedBy: digits).joined(separator: "")

    if modifiedContactNo.count > 9 {

        return modifiedContactNo

    } else {

        return nil
    }
}

var number = phone.value.stringValue
number = number.starts(with: "+91") ? number.replacingOccurrences(of: "+91", with: "") : number

if let formattedNumber = removeSpecialCharactersFromContactNumberOfUser(number)  {
    //use this formattedNumber                 
}

这是用来从你的电话号码中移除+91的代码,它可以正常运行。


0

Swift 3 "_$!<Mobile>!$_" 这个项目的编写旨在创造差异,并提供依赖于各种选项的机会。

for con in contacts
{
    for num in con.phoneNumbers
    {
        if num.label == "_$!<Mobile>!$_"    //Please Don't Change this!
        {
            self.contactNames.append(con.givenName)
            self.contactNums.append(num.value.stringValue)
            break
        }
        else
        {
            continue
        }
    }
}

这里我们有num.value.stringValue


2
嘿 @Aasim,如果你可能会像CNLabelPhoneNumberMobile、CNLabelPhoneNumberiPhone、CNLabelPhoneNumberHomeFax这样,把一个字符串放在编码中是一个不好的做法。 - Milan Kamilya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,