【问题标题】:Contacts not loaded in UITableViewUITableView 中未加载联系人
【发布时间】:2015-10-18 17:24:13
【问题描述】:

在我的 iOS 应用程序中,我有一个显示 UITableView 的 ViewController。一切(代表、数据源...)都已正确设置。我想做一件普通的事情:用手机中的联系人填充 TableView。

但是由于某种原因,TableView 总是为空,而我在 viewDidLoad 中成功获取了联系人。我注意到方法 tableView:numberOfRowsInSection: 在我得到我的联系人列表之前被调用,但我不知道是什么问题。这是我的 Controller.m:

@interface Controller () <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong) NSMutableArray *contacts;
@property(nonatomic, strong) IBOutlet UITableView *contactsTable;

@end

@implementation Controller

- (void)viewDidLoad {
[super viewDidLoad];
self.contacts = [[NSMutableArray alloc] init];
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();

if (status == kABAuthorizationStatusDenied || status == kABAuthorizationStatusRestricted) {
    // if you got here, user had previously denied/revoked permission for your
    // app to access the contacts, and all you can do is handle this gracefully,
    // perhaps telling the user that they have to go to settings to grant access
    // to contacts

    [[[UIAlertView alloc] initWithTitle:nil message:@"This app requires access to your contacts to function properly. Please visit to the \"Privacy\" section in the iPhone Settings app." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
    return;
}

CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);

if (!addressBook) {
    NSLog(@"ABAddressBookCreateWithOptions error: %@", CFBridgingRelease(error));
    return;
}

ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
    if (error) {
        NSLog(@"ABAddressBookRequestAccessWithCompletion error: %@", CFBridgingRelease(error));
    }

    if (granted) {
        // if they gave you permission, then just carry on

        [self listPeopleInAddressBook:addressBook];
    } else {
        // however, if they didn't give you permission, handle it gracefully, for example...

        dispatch_async(dispatch_get_main_queue(), ^{
            // BTW, this is not on the main thread, so dispatch UI updates back to the main queue

            [[[UIAlertView alloc] initWithTitle:nil message:@"This app requires access to your contacts to function properly. Please visit to the \"Privacy\" section in the iPhone Settings app." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
        });
    }

    CFRelease(addressBook);
});
}


- (void)listPeopleInAddressBook:(ABAddressBookRef)addressBook
{
NSArray *allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
NSInteger numberOfPeople = [allPeople count];
for (NSInteger i = 0; i < numberOfPeople; i++) {
    ABRecordRef person = (__bridge ABRecordRef)allPeople[i];

    NSString *firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
    NSString *lastName  = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
    NSString *fullname = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
    ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);

    CFIndex numberOfPhoneNumbers = ABMultiValueGetCount(phoneNumbers);
    for (CFIndex i = 0; i < numberOfPhoneNumbers; i++) {
        NSString *phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneNumbers, i));
        NSLog(@"  phone:%@", phoneNumber);
        GIContact *contact = [[GIContact alloc] initWithFullName:fullname telephone:phoneNumber];
        [self.contacts addObject:contact];
    }

    CFRelease(phoneNumbers);
}
}

//Called before I got the contacts
- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section
{
return [self.contacts count]; //This works when I replace it with a number like 2
}
- (UITableViewCell *)tableView:(UITableView *)tableView
     cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create an instance of UITableViewCell, with default appearance
UITableViewCell *cell =
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                       reuseIdentifier:@"ContactTableCell"];
// Set the text on the cell with the description of the item
// that is at the nth index of items, where n = row this cell
// will appear in on the tableview
   GIContact *contact = self.contacts[indexPath.row];
   cell.textLabel.text = contact.fullname;
   return cell;
}

@end

Contact 是一个简单的模型类,有两个 NSString(全名和电话)

【问题讨论】:

    标签: ios uitableview


    【解决方案1】:

    成功连接并检索联系人列表后,刷新表格视图。调用numberOfRowsInSection 是因为视图正在立即尝试显示数据,但该数据尚不存在。

    dispatch_async(dispatch_get_main_queue(), ^{
        [self.tableView reloadData];
    });
    

    【讨论】:

      【解决方案2】:

      tableView reloadData 方法调用后获得联系。

      - (void)listPeopleInAddressBook:(ABAddressBookRef)addressBook
      {
      NSArray *allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
      NSInteger numberOfPeople = [allPeople count];
      for (NSInteger i = 0; i < numberOfPeople; i++) {
          ABRecordRef person = (__bridge ABRecordRef)allPeople[i];
      
          NSString *firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
          NSString *lastName  = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
          NSString *fullname = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
          ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
      
          CFIndex numberOfPhoneNumbers = ABMultiValueGetCount(phoneNumbers);
          for (CFIndex i = 0; i < numberOfPhoneNumbers; i++) {
              NSString *phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneNumbers, i));
              NSLog(@"  phone:%@", phoneNumber);
              GIContact *contact = [[GIContact alloc] initWithFullName:fullname telephone:phoneNumber];
              [self.contacts addObject:contact];
          }
      
          CFRelease(phoneNumbers);
      }
      [self.tableView reloadData];// refresh table Data
      }
      

      【讨论】:

        【解决方案3】:

        方法ABAddressBookRequestAccessWithCompletion 在主线程之外完成它的工作。一个提示是尾随 Closure,它作为回调调用,只要方法完成它的工作。

        所以当(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section 被调用时,它仍然在主线程工作,或者数据还没有“到达”主线程。

        如果您想在完成获取地址簿请求后立即填充数据,您应该在主线程上重新加载 UITableView

        - (void)listPeopleInAddressBook:(ABAddressBookRef)addressBook
        {
        NSArray *allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
        NSInteger numberOfPeople = [allPeople count];
        for (NSInteger i = 0; i < numberOfPeople; i++) {
        ABRecordRef person = (__bridge ABRecordRef)allPeople[i];
        
        NSString *firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
        NSString *lastName  = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
        NSString *fullname = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
        ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
        
        CFIndex numberOfPhoneNumbers = ABMultiValueGetCount(phoneNumbers);
        for (CFIndex i = 0; i < numberOfPhoneNumbers; i++) {
            NSString *phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneNumbers, i));
            NSLog(@"  phone:%@", phoneNumber);
            GIContact *contact = [[GIContact alloc] initWithFullName:fullname telephone:phoneNumber];
            [self.contacts addObject:contact];
        }
        
        CFRelease(phoneNumbers);
        }
        // reload the tableview on the main thread here
        dispatch_async(dispatch_get_main_queue(), ^{
           [self.tableView reloadData];
        });
        }
        

        【讨论】:

        • 一切正常,感谢您的回答和解释。但是为什么你坚持主线而@Dharmesh Dhorajiya 不坚持?你能解释一下你的答案之间的区别吗?
        • 假设您正在从 Web 获取地址簿数据,但您不想阻止 UI。您将异步获取它,以便用户仍然可以使用您的应用程序。异步做某事意味着在主线程之外做。完成后,您告诉操作系统您已为主线程准备好数据。
        • 这里是一个关于从网络同步和异步获取东西的教程:codewithchris.com/…
        • ...如果您不使用 dispatch_async 呼叫,您的应用将需要一段时间才能对 reloadDatacall 做出“反应”
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-15
        • 1970-01-01
        • 2022-08-19
        相关资源
        最近更新 更多