2015-07-28 54 views
2

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

但由于某种原因,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 

联系是有两个的NSString(全名和电话)

回答

1

方法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]; 
}); 
} 
+0

一切正常,谢谢你的回答和解释。然而,为什么你坚持主线程而@Dharmesh Dhorajiya没有?你能解释你的答案之间的区别吗? – Gannicus

+1

假设您从Web上获取您的地址簿数据,但您不想阻止UI。您可以异步获取它,以便用户仍然可以使用您的应用程序。异步执行某种操作意味着将其从主线程中删除。完成后,告诉操作系统您的主线程已准备好数据。 – ezcoding

+0

好吧我想我知道了谢谢你 – Gannicus

3

一个简单的模型类连接成功后,并获取联系人列表,刷新表视图。调用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 
} 
相关问题