简体   繁体   中英

“Attempt to insert row 0 into section 0, but there are only 0 rows in section 0 after the update” Error

I have an app that is selecting a person from their contacts list and takes their First name, last name and email. It then saves the first name to a nsmutablearray and puts it into a uitableview cell. My problem occurs once the contact is selected in the simulator.

Code:

.h:

#import <UIKit/UIKit.h>
#import <AddressBookUI/AddressBookUI.h>

@interface FirstViewController : UIViewController <    ABPeoplePickerNavigationControllerDelegate, UITableViewDelegate, UITableViewDataSource>

- (IBAction)showPicker:(id)sender;

@property (weak, nonatomic) IBOutlet NSString *firstName;

@property (weak, nonatomic) IBOutlet NSString *email;

@property (weak, nonatomic) IBOutlet NSString *lastName;


@property (weak, nonatomic) IBOutlet UITableView *myTableView;

@property (strong, nonatomic) NSMutableArray *contacts;

@end

.m:

#import "FirstViewController.h"

@interface FirstViewController ()

@end

@implementation FirstViewController

@synthesize firstName;

@synthesize email;

@synthesize lastName;

@synthesize contacts;

@synthesize myTableView;


- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    contacts = [[NSMutableArray alloc]initWithObjects:nil];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - UITableView Datasource

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
} 

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return contacts.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath  *)indexPath
{
    static NSString *cellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault  reuseIdentifier:cellIdentifier];

    }

    cell.textLabel.text = [contacts objectAtIndex:indexPath.row];

    return cell;
}

- (IBAction)showPicker:(id)sender {

    ABPeoplePickerNavigationController *picker =
    [[ABPeoplePickerNavigationController alloc] init];
    picker.peoplePickerDelegate = self;

    [self presentModalViewController:picker animated:YES];
}

- (void)peoplePickerNavigationControllerDidCancel:
(ABPeoplePickerNavigationController *)peoplePicker
{
    [self dismissModalViewControllerAnimated:YES];
}


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

    [self displayPerson:person];
    [self dismissModalViewControllerAnimated:YES];

    return NO;
}

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





    return NO;

}


- (void)displayPerson:(ABRecordRef)person
{
    NSString* name = (__bridge_transfer NSString*)ABRecordCopyValue(person,
                                                                    kABPersonFirstNameProperty);
    self.firstName = name;

    NSString* last = (__bridge_transfer NSString*)ABRecordCopyValue(person,
                                                                    kABPersonLastNameProperty);
    self.lastName = last;


    ABMultiValueRef  emails = ABRecordCopyValue(person, kABPersonEmailProperty);
    NSString *emailId = (__bridge NSString *)ABMultiValueCopyValueAtIndex(emails, 0);//0     for "Home Email" and 1 for "Work Email".

    self.email = emailId;

    if (!(contacts))
    {

        contacts = [[NSMutableArray alloc]init];

    }

    [contacts insertObject:firstName atIndex:0];

    NSIndexPath * indexPath = [NSIndexPath indexPathForRow:0 inSection:0];

    [self.myTableView insertRowsAtIndexPaths:@[indexPath]  withRowAnimation:UITableViewRowAnimationAutomatic];
}




@end

UITableView must be kept in sync with the data source at all times. Special care must be taken if the data source can change in a background thread.

When something is added to the data source, call beginUpdate/insert/endUpdate as soon as possible. You don't have to worry about caching these, the UITableView will cache changes to be executed when it determines there is enough cpu time and resources.

The moment endUpdates is called, the UITable will ask the dataSource for the number of sections and rows again. If your number of sections and row feeds directly from the dataSource, then number sections and rows, plus insertions, minus deletions must equal the numbers returned by the end calls for numberOfSections and numberOfRowsInSection.

One last tip: avoid mixing calls to 'reloadData' and beginUpdate/endUpdate pairs. Use one or the other, not both.

I have encountered same problem as this. All you have to do is change

[self.myTableView insertRowsAtIndexPaths:@[indexPath]withRowAnimation:UITableViewRowAnimationAutomatic];`

to

 [self.myTableView beginUpdates];

 [self.myTableView insertRowsAtIndexPaths:@[indexPath]withRowAnimation:UITableViewRowAnimationAutomatic];

 [self.myTableView endUpdates];

From UITableView Documentation

beginUpdates

Begin a series of method calls that insert, delete, or select rows and sections of the receiver.

when you use beginUpdates , you must call endUpdates and not reloadData .

You can check https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html for more UITableView information.

Hope this helps!

The comments above about implementing -tableView:numberOfRowsInSection: are correct. The assertion is checking the returned value expecting it will increase.

However since you didn't set your dataSource on the UITableView its calling (or not calling) a method that doesn't exist and getting 0 .

You need to set the myTableView.dataSource and (since you also implement the delegate protocol) myTableView.delegate to self .

You're also likely to need something like

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.myTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
    ...
}

Unless you're registering that somewhere else or your storyboard has a "Prototype Cell" with the identifier "Cell" which your code asks for.

I find this problem commonly occurs when I am placing a table view inside of a View Controller. If you're using a UITableViewController jump to 3.

These steps may help:

1: In your View Controller .h file make sure you add the following:

@interface YourViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>

2: Next create an IBOutlet for your table view by ctrl + drag to your .h class. It should look like:

@property (weak, nonatomic) IBOutlet UITableView *tableView;

3: Next step is to ctrl + drag to your View Controllers icon (see image)

在此输入图像描述

You need to do this twice selecting:

- delegate
- datasource

Finally, in your .m file, you should have the following method:

- (void) viewWillAppear:(BOOL)animated{
    //[self.tableView beginUpdates];
    //[self.tableView endUpdates];

    [self.tableView reloadData];
}

You can use either beginUpDates/endUpdates or reloadData, however Apple docs recommend reloadData.

Once done your table should work fine.

You need to maintain count for contacts array and increment accordingly.
and while creating indexPath you need to set appropriate indexPathForRow: and section count(if required).

- (void)displayPerson:(ABRecordRef)person
{

  ..........

[contacts insertObject:firstName atIndex:0];

NSIndexPath * indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; // you cant do this increment appropriately

 .........
}

Please check your tableview datasource and delegate methods. You may be passing empty array to datasource methods. And don't forget to reload tableview after getting data.

I know it's stupid - I got the same error because I forgot to set the delegate and dataSource.

So after inserting rows and doing tableView.endUpdates() the tableView thought it must have some rows - but due to the unlinked dataSource , it has not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM