【问题标题】:ABRecordRef fields don't seem to be populated?ABRecordRef 字段似乎没有被填充?
【发布时间】:2014-02-18 05:54:36
【问题描述】:

[背景:iOS 7、Xcode 5,都是截至 2014 年 2 月的最新版本。测试数据是 iPhone 5(真实设备,而非模拟器)]

我的目标是使用 AddressBookUI 方法允许用户指定联系人,然后使用各种字段(地址、电话号码等)在我的代码中填充 GUI。 ABPeoplePickerNavigationController 是允许用户按姓名选择联系人的标准机制。这样做会导致调用此委托方法:

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker 
shouldContinueAfterSelectingPerson:(ABRecordRef)person

但是,如果我此时检查人员记录,则多值字段都没有任何数据。所以我提取了RecordID,检索了它,结果ABRecordRef也没有填写多值字段。

如果我从委托方法返回 YES,则会向用户显示另一个 UI,并显示联系人详细信息。触摸任何字段都会导致此委托方法调用

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person 
property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier

并且ABRecordRef 已填写所有多值字段。

我找不到有关延迟加载记录的任何信息,或者存在所需的延迟或权限问题会阻止填写字段。我用来检查记录的代码是方法,因此在第二个实例中查找值的完全相同的代码无法在第一个实例中找到它。

关于可能发生的事情有什么建议,或者我如何在不向用户显示第二个 UI 的情况下检索完整记录?

我正在使用 Apple 的 QuickContacts 示例代码。以下是我所做的添加和更改。

+(NSMutableDictionary *)convertABRecordRef:(ABRecordRef)person
{
    // Initialize a mutable dictionary and give it initial values.
    NSMutableDictionary *contactInfoDict = [[NSMutableDictionary alloc] initWithCapacity:12];

    // Use a general Core Foundation object.
    CFTypeRef generalCFObject = ABRecordCopyValue(person, kABPersonFirstNameProperty);

    ABRecordID foundId = ABRecordGetRecordID(person);
    NSNumber *personIDNum = [NSNumber numberWithInteger:foundId];
    [contactInfoDict setObject:personIDNum forKey:@"recordID"];


    // Get the first name.
    if (generalCFObject)
    {
        [contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:@"firstName"];
        CFRelease(generalCFObject);
    }

    // Get the last name.
    generalCFObject = ABRecordCopyValue(person, kABPersonLastNameProperty);
    if (generalCFObject) {
        [contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:@"lastName"];
        CFRelease(generalCFObject);
    }

    generalCFObject = ABRecordCopyValue(person, kABPersonOrganizationProperty);
    if (generalCFObject) {
        [contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:@"companyName"];
        CFRelease(generalCFObject);
    }

    //ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
    //NSArray *numbers = (NSArray *)ABMultiValueCopyArrayOfAllValues(phones);


    // Get the phone numbers as a multi-value property.
    ABMultiValueRef phonesRef = ABRecordCopyValue(person, kABPersonPhoneProperty);
    CFIndex phoneCount = ABMultiValueGetCount(phonesRef);
    for (CFIndex i=0; i<ABMultiValueGetCount(phonesRef); i++) {

        CFStringRef currentPhoneLabel = ABMultiValueCopyLabelAtIndex(phonesRef, i);
        CFStringRef currentPhoneValue = ABMultiValueCopyValueAtIndex(phonesRef, i);

        if (CFStringCompare(currentPhoneLabel, kABPersonPhoneMobileLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentPhoneValue forKey:@"mobileNumber"];
        }

        if (CFStringCompare(currentPhoneLabel, kABHomeLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentPhoneValue forKey:@"homeNumber"];
        }

        if (CFStringCompare(currentPhoneLabel, kABWorkLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentPhoneValue forKey:@"workNumber"];
        }

        CFRelease(currentPhoneLabel);
        CFRelease(currentPhoneValue);
    }
    CFRelease(phonesRef);


    // Get the e-mail addresses as a multi-value property.
    ABMultiValueRef emailsRef = ABRecordCopyValue(person, kABPersonEmailProperty);
    for (int i=0; i<ABMultiValueGetCount(emailsRef); i++)
    {
        CFStringRef currentEmailLabel = ABMultiValueCopyLabelAtIndex(emailsRef, i);
        CFStringRef currentEmailValue = ABMultiValueCopyValueAtIndex(emailsRef, i);

        if (CFStringCompare(currentEmailLabel, kABHomeLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentEmailValue forKey:@"homeEmail"];
        }

        if (CFStringCompare(currentEmailLabel, kABWorkLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentEmailValue forKey:@"workEmail"];
        }

        CFRelease(currentEmailLabel);
        CFRelease(currentEmailValue);
    }
    CFRelease(emailsRef);


    // Get the first street address among all addresses of the selected contact.
    ABMultiValueRef addressRef =  ABRecordCopyValue(person, kABPersonAddressProperty);
    if (ABMultiValueGetCount(addressRef) > 0)
    {
        CFIndex numberOfAddresses = ABMultiValueGetCount(addressRef);
        for (CFIndex i=0; i<numberOfAddresses; i++)
        {
            CFStringRef label = ABMultiValueCopyLabelAtIndex(addressRef, i);
            if (label)
            {
                if (CFEqual(label, kABHomeLabel))
                {
                    NSDictionary *addressDict = (__bridge NSDictionary *)ABMultiValueCopyValueAtIndex(addressRef, 0);

                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressStreetKey] forKey:@"homeAddress"];
                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressZIPKey] forKey:@"homeZipCode"];
                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressCityKey] forKey:@"homeCity"];

                }
                else if (CFEqual(label, kABWorkLabel))
                {
                    NSDictionary *addressDict = (__bridge NSDictionary *)ABMultiValueCopyValueAtIndex(addressRef, 0);

                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressStreetKey] forKey:@"workAddress"];
                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressZIPKey] forKey:@"workZipCode"];
                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressCityKey] forKey:@"workCity"];
                }
                CFRelease(label);
            }
        }
    }
    CFRelease(addressRef);


    // If the contact has an image then get it too.
    if (ABPersonHasImageData(person)) {
        NSData *contactImageData = (__bridge NSData *)ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);

        [contactInfoDict setObject:contactImageData forKey:@"image"];
    }


    return contactInfoDict;
}



// Displays the information of a selected person
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
    // only returns a few fields, and none of the multi value ones  :-(
    NSMutableDictionary *results = [QuickContactsViewController convertABRecordRef:person];

    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
    ABRecordID foundId = ABRecordGetRecordID(person);
    ABRecordRef fullPerson = ABAddressBookGetPersonWithRecordID(addressBook, foundId);

    // also only returns a few fields!?
    NSMutableDictionary *selectedFromID = [QuickContactsViewController convertABRecordRef:fullPerson];


    return YES;
}

// Does not allow users to perform default actions such as dialing a phone number, when they select a person property.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person 
                                property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
    // returns all simple and multi-value fields!?   
    NSMutableDictionary *results = [QuickContactsViewController convertABRecordRef:person];

    return NO;
}

编辑:添加我的解决方案(感谢 Thorsten!)。

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
    NSArray *allPersonRecords = (NSArray *)CFBridgingRelease(ABPersonCopyArrayOfAllLinkedPeople(person));
    NSLog(@"Count Linked People: %i", allPersonRecords.count);

    NSMutableDictionary *results = [[NSMutableDictionary alloc]initWithCapacity:12];
    for (int x=0; x<[allPersonRecords count]; x++)
    {
        ABRecordRef user = CFBridgingRetain([allPersonRecords objectAtIndex:x]);
        NSMutableDictionary *userFromArray = [QuickContactsViewController convertABRecordRef:user];
        [results addEntriesFromDictionary:userFromArray];  // mush them together
        CFRelease(user);
    }

    NSLog(@"finished! Total number of contact fields found:%d", [results count]);
    // do something with the data here...

    return NO;
}

【问题讨论】:

  • 我用模拟器的 Xcode 5.0.1 通讯录测试了你的代码,它工作正常:即使是第一次调用 convertABRecordRef: 也会返回所有字段。那么,这可能是一个多线程问题,例如您是否打开不在主线程上的地址簿并将此引用设置为 ABPeoplePickerNavigationController 的地址簿属性,然后在主线程上使用?
  • Reinhard,我知道代码在模拟器中工作,这就是为什么我在第一句话中提到我在真实设备上运行的原因。在模拟器上运行的完全相同的 Apple 示例代码在设备上失败。我不认为这可能是一个多线程问题,因为它没有在不同的线程上运行选择器。
  • 我刚刚在仍然装有 iOS 5 的设备上运行代码,使用 XCode 4 构建,链接到 ios 6.1,观察到的行为没有变化。很郁闷
  • 非常有趣:我在真实设备上运行您的代码低于 7.0.4。如果我单步执行 convertABRecordRef:,一切正常。如果我在从 convertABRecordRef: 返回后停在断点处,则结果 NSMutableArray 在这两种情况下都是 nil。所以这似乎是苹果的问题,而不是你的!
  • @softwareevolved:我的回答能解决你的问题吗?如果是,请考虑接受。

标签: ios abaddressbook


【解决方案1】:

一个人可以由多个人组成。遍历所有人以获取您的数据:

NSArray *people = (NSArray *)CFBridgingRelease(ABPersonCopyArrayOfAllLinkedPeople(person));
...
ABRecordRef user = CFBridgingRetain([people objectAtIndex:i]);
...
valuesRef = ABRecordCopyValue(user, kABPersonPhoneProperty);
valuesCount = 0;
if (valuesRef != nil) valuesCount = ABMultiValueGetCount(valuesRef);
...

HTH

【讨论】:

  • Thorsten,这个例子中的样本数据是一个人,而不是一个“由多人组成”的人——我什至不知道那是什么,除非你指的是群体?不属于此错误或代码
  • 虽然您在联系人应用程序中只看到一个联系人,但该联系人的数据可能存储在不同的“卡片”上,例如第一张“卡”上的姓名,第二张“卡”上的手机号码和地址等等。这些“卡”是“关联的人”(尽管只有一个人)。在您的代码中,您只检查第一张“卡片”,因此您无法获得该人可用的所有数据。使用“ABPersonCopyArrayOfAllLinkedPeople(person)”,您可以获得一个人的所有“卡片”以及所有数据。
  • 简单测试:将这两行放在您的 convertABRecordRef 方法之上: NSArray *people = (NSArray *)CFBridgingRelease(ABPersonCopyArrayOfAllLinkedPeople(person)); NSLog(@"Count Linked People: %i", people.count);并运行您的代码。如果人数 > 1,则您有多个该联系人的“卡片”。
  • 索斯滕——我很抱歉!我刚刚有机会尝试你的建议,你是对的。如果选择器仅显示一个条目(使用搜索时),我仍然对为什么/如何可以有多个人进行记录感到惊讶,但完全如此。你得到了赏金和我最诚挚的感谢。
  • PS -- 我刚刚注意到您对“多人”情况的解释。也谢谢你!
猜你喜欢
  • 1970-01-01
  • 2020-07-14
  • 2013-06-13
  • 2010-12-11
  • 2015-12-28
  • 2019-01-01
  • 2016-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多