AlexQueue
AlexQueue

Reputation: 6551

Inserting items in UITableView crashes application

I'm making an application with a tableview list of contacts that can be reached via a tab-controller at the bottom.

I copied (literally copy/pasted) from the example Master Detail Application and tried to make sure all storyboard references lined up.

#import "ContactsTableViewController.h"

#import "ContactViewController.h"

@interface ContactsTableViewController () {
    NSMutableArray *_objects;
}

@end

@implementation ContactsTableViewController

- (void)awakeFromNib
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
        self.clearsSelectionOnViewWillAppear = NO;
        self.preferredContentSize = CGSizeMake(320.0, 600.0);
    }
    [super awakeFromNib];

//    [self.tableView setDelegate:self]; // From (unsuccesfully) trying https://stackoverflow.com/questions/16311393/how-to-insert-items-to-a-uitableview-when-a-uibutton-is-clicked-in-ios
//    [self.tableView setDataSource:self];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
    self.navigationItem.rightBarButtonItem = addButton;
    self.contactViewController = (ContactViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];    
}

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

- (void)insertNewObject:(id)sender
{
    if (!_objects) {
        _objects = [[NSMutableArray alloc] init];
    }
    [_objects insertObject:[NSDate date] atIndex:0];
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; // Crashes here
}

#pragma mark - Table View

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

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; // Crashes here

    NSDate *object = _objects[indexPath.row];
    cell.textLabel.text = [object description];
    return cell;
}

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [_objects removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
    }
}

/*
 // Override to support rearranging the table view.
 - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
 {
 }
 */

/*
 // Override to support conditional rearranging of the table view.
 - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
 {
 // Return NO if you do not want the item to be re-orderable.
 return YES;
 }
 */

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
        NSDate *object = _objects[indexPath.row];
        self.contactViewController.detailItem = object;
    }
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showDetail"]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        NSDate *object = _objects[indexPath.row];
        [[segue destinationViewController] setDetailItem:object];
    }
}

@end

It crashes on the first line in CellRowAtIndexPath. Since I was having trouble I also took the advice in How to insert items to a UITableView when a UIButton is clicked in iOS but it didn't solve my problem.

This is just incredibly frustrating, because as far as I can tell my code is (except for names) exactly the same as the example application.

edit: Exception message is

'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

Upvotes: 0

Views: 321

Answers (3)

noobzilla
noobzilla

Reputation: 1155

Alex - When using storyboards and the new prototype cell feature in xCode, you have to set an identifier value in Interface Builder whose value should match what is in your code.

So notice you have this line:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

In this case, your cell identifier is "Cell"

Can you confirm that in Interface Builder/Storyboard, your table view cell's identifier is set to the same?

As an example, here's a screenshot from a sample app I was building (Notice my Indentifier field on the right):

enter image description here

Upvotes: 2

Oleg Sobolev
Oleg Sobolev

Reputation: 3376

You should not use [self.tableView insertRowsAtIndexPaths: withRowAnimation:];. Instead of this you should call [talbeView reloadData] method.

Upvotes: 0

Hakim
Hakim

Reputation: 1314

I would recommend this

-(void)ViewDidLoad{

    [tableView registerClass:[MyCell class] forCellReuseIdentifier:@"Cell"];
}

- (void)insertNewObject:(id)sender
{
    if (!_objects) {
        _objects = [[NSMutableArray alloc] init];
    }
    [_objects insertObject:[NSDate date] atIndex:0];
    [tableView reloadData]; //this will trigger cellForRowAtIndexPath again with the updated array
}

and would you please post your error.

Upvotes: 0

Related Questions