问题
I'm trying to implement the func personViewController(personViewController: ABPersonViewController!,
shouldPerformDefaultActionForPerson person: ABRecord!,
property property: ABPropertyID,
identifier valueIdentifier: ABMultiValueIdentifier) -> Bool
function, which is part of the ABPersonViewControllerDelegate
protocol and is called whenever the user clicks on an item in the ABPersonViewController
, such that any information the user selects will be copied to a [String : String]
dictionary such that the property name will be the key for the property value: [..."kABPersonFirstNameProperty" : "Alexander"...]
, say.
I also want to avoid a switch or a long list of conditions testing for if a property is this or that; I'd rather handle it as generally as possible—I'm trying for only two different cases: if the property is a single value or a multi value. If it's a multi-value, I'd like to copy down all the information available.
For example, if the user clicks on the address, the result might be such: [..."kABPersonAddressStreetKey" : "1 Infinite Loop", "kABPersonAddressCityKey" : "Cupertino" , "kABPersonAddressStateKey", "California (or CA?)"...]
.
This is all I have, after various hours of scouring the Apple Developer Library and related SO questions (pathetic, I know):
func personViewController(personViewController: ABPersonViewController!,
shouldPerformDefaultActionForPerson person: ABRecord!,
property property: ABPropertyID,
identifier valueIdentifier: ABMultiValueIdentifier) -> Bool {
s["kABPersonFirstNameProperty"] = ABRecordCopyValue(person, kABPersonFirstNameProperty) as! String //the name can't actually be selected by the user, but I want to capture it anyway
s["kABPersonLastNameProperty"] = ABRecordCopyValue(person, kABPersonLastNameProperty) as! String
if valueIdentifier == 0 { //property is a single property, not a multivalue property
let record = ABRecordCopyValue(person, property)
s[property as! String!] = record as! String
} else { //property is an ABMultiValue
let multiRecord = ABRecordCopyValue(person, property) as! ABMultiValueRef
s[property as! String] = ABMultiValueGetIndexForIdentifier(multiRecord, valueIdentifier)
}
return false
}
I can tell there are plenty of pieces missing—is it even possible to condense this into a dictionary?
Thanks in advance (and 100 reputation to whomever provides a complete, correct answer).
回答1:
Using vadian's answer as a starting point, I compiled the following, which will gather and copy to a dictionary almost all of the information a user can select (and some information which a user can't click on—first name, last name, organization—automatically).
It's a work in progress, which I'll update as I continue to add support for more properties, but at present it works for first name, last name, email, phone numbers, address, and some social profiles.
I had to use indirect methods in some cases: storing phone numbers whose labels didn't have a designated key as kAB_Label
s, for example, and it doesn't yet take the label of an email address.
With s
as of type Dictionary<String, AnyObject>
, here it is:
func personViewController(personViewController: ABPersonViewController!, shouldPerformDefaultActionForPerson person: ABRecord!, property: ABPropertyID, identifier: ABMultiValueIdentifier) -> Bool {
addPropertyForKey("kABPersonFirstNameProperty", person : person, property: kABPersonFirstNameProperty)
addPropertyForKey("kABPersonLastNameProperty", person : person, property: kABPersonLastNameProperty)
addPropertyForKey("kABPersonOrganizationProperty", person: person, property: kABPersonOrganizationProperty)
if (property == kABPersonAddressProperty) {
let multiRecord : ABMultiValueRef = ABRecordCopyValue(person, property).takeUnretainedValue()
let index = ABMultiValueGetIndexForIdentifier(multiRecord, identifier)
let addressRecord :AnyObject = ABMultiValueCopyValueAtIndex(multiRecord, index).takeUnretainedValue()
addValuesFromRecord(addressRecord, forKeys:["Street", "City", "State", "ZIP", "Country", "CountryCode"])
}
if (property == kABPersonEmailProperty) {
let multiRecord: ABMultiValueRef = ABRecordCopyValue(person, property).takeUnretainedValue()
let index = ABMultiValueGetIndexForIdentifier(multiRecord, identifier)
let email = ABMultiValueCopyValueAtIndex(multiRecord, index).takeUnretainedValue() as! String
if (s["kABPersonEmailProperty(1)"] == nil) {
s["kABPersonEmailProperty(1)"] = email
} else {
s["kABPersonEmailProperty(2)"] = email
}
}
if (property == kABPersonSocialProfileProperty) {
let multiRecord: ABMultiValueRef = ABRecordCopyValue(person, property).takeUnretainedValue()
let index = ABMultiValueGetIndexForIdentifier(multiRecord, identifier)
let profile = ABMultiValueCopyValueAtIndex(multiRecord, index).takeUnretainedValue()
let profileType = profile["service"] as! String
let profileName = profile["username"] as! String
switch profileType {
case "facebook" : s["kABPersonSocialProfileServiceFacebook"] = profileName
case "twitter" : s["kABPersonSocialProfileServiceTwitter"] = profileName
case "sinaweibo": s["kABPersonSocialProfileServiceSinaWeibo"] = profileName
default: break
}
}
if (property == kABPersonPhoneProperty) {
let multiRecord : ABMultiValueRef = ABRecordCopyValue(person, property).takeUnretainedValue()
let index = ABMultiValueGetIndexForIdentifier(multiRecord, identifier)
let number = ABMultiValueCopyValueAtIndex(multiRecord, index).takeUnretainedValue() as! String
let locLabel: CFStringRef = (ABMultiValueCopyLabelAtIndex(multiRecord, index) != nil) ? ABMultiValueCopyLabelAtIndex(multiRecord, index).takeUnretainedValue() as CFStringRef : ""
let customLabel = String (stringInterpolationSegment: ABAddressBookCopyLocalizedLabel(locLabel))
var cfStr:CFTypeRef = locLabel
var nsTypeString = cfStr as! NSString
var a:String = nsTypeString as String
var b = a
if (a.rangeOfString("_$!<") != nil) {
b = a.substringFromIndex(a.rangeOfString("_$!<")!.endIndex)
b = b.substringToIndex(b.rangeOfString(">!$_")!.startIndex)
}
switch b {
case "Mobile" : s["kABPersonPhoneMobileLabel"] = number
case "iPhone" : s["kABPersonPhoneIPhoneLabel"] = number
case "Main" : s["kABPersonPhoneMainLabel"] = number
case "Home" : s["kABHomeLabel"] = number
case "Work" : s["kABWorkLabel"] = number
case "Other" : s["kABOtherLabel"] = number
default: break
}
}
println(s)
return false
}
Please, anyone should feel free to copy any/all parts of it which would be helpful.
回答2:
I'm afraid there can't be a generic method to retrieve information from AddressBook because there are too many different collection and property types.
This is an example to get first name
, last name
and address
information when the user taps on an address field.
It's assumed that the dictionary s
is declared as
var s = Dictionary<String, AnyObject>()
func personViewController(personViewController: ABPersonViewController!, shouldPerformDefaultActionForPerson person: ABRecord!, property: ABPropertyID, identifier: ABMultiValueIdentifier) -> Bool {
addPropertyForKey("FirstName", person : person, property: kABPersonFirstNameProperty)
addPropertyForKey("LastName", person : person, property: kABPersonLastNameProperty)
if (property == kABPersonAddressProperty) {
let multiRecord : ABMultiValueRef = ABRecordCopyValue(person, property).takeUnretainedValue()
let index = ABMultiValueGetIndexForIdentifier(multiRecord, identifier)
let addressRecord :AnyObject = ABMultiValueCopyValueAtIndex(multiRecord, index).takeUnretainedValue()
addValuesFromRecord(addressRecord, forKeys:["Street", "City", "State", "ZIP", "Country", "CountryCode"])
println(s)
}
return false
}
func addPropertyForKey(key : String, person: ABRecord, property : ABPropertyID)
{
let value : AnyObject = ABRecordCopyValue(person, property).takeUnretainedValue()
s[key] = value
}
func addValuesFromRecord(record : AnyObject, forKeys keys : [String])
{
for key in keys {
if let value : AnyObject = record[key] {
s[key] = value
}
}
}
to be as generic as possible, all values are declared as AnyObject
来源:https://stackoverflow.com/questions/31822329/swift-copy-information-selected-by-user-in-abpersonviewcontroller-to-dictionary