Devj
Devj

Reputation: 19

I have an iPad app that keeps getting Terminating app due to uncaught exception 'NSInternalInconsistencyException'

The first button displays a list of presidents and when you click on them you get their wikipedia page. All worked well until I put in another button that let you change the language of the page from English to German, etc. I keep getting this error in the debugger:

Assertion failure in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:6061 2012-04-30 17:30:01.293 Presidents[11151:f803] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:' * First throw call stack:

I'm going through a book tutorial and I think once again they are missing code. Any ideas would be awesome!

EDIT: Noob move on me guys I pasted the wrong code section: This new section is a custom popover that I added after the BIDMasterViewController, which was working fine. This new section is where the problems arrived:

   #import "BIDLanguageListController.h"
#import "BIDDetailViewController.h"

@interface BIDLanguageListController ()

@end

@implementation BIDLanguageListController

@synthesize languageNames;
@synthesize languageCodes;
@synthesize detailViewController;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.languageNames = [NSArray arrayWithObjects:@"English", @"French",
                          @"German", @"Spanish", nil];
    self.languageCodes = [NSArray arrayWithObjects:@"en", @"fr", @"de", @"es", nil];
    self.clearsSelectionOnViewWillAppear = NO;
    self.contentSizeForViewInPopover = CGSizeMake(320.0, [self.languageCodes count] * 44.0);

    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

- (void)viewDidUnload
{
    [super viewDidUnload];

    self.detailViewController = nil;
    self.languageNames = nil;
    self.languageCodes = nil;
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return YES;
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{

    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

    // Return the number of rows in the section.
    return [self.languageCodes count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // Configure the cell...
    cell.textLabel.text = [languageNames objectAtIndex:[indexPath row]];

    return cell;
}

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

/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject: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;
}
*/

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
   detailViewController.languageString = [self.languageCodes objectAtIndex:
                                          [indexPath row]];
}

@end

Upvotes: 0

Views: 1068

Answers (3)

Elise van Looij
Elise van Looij

Reputation: 4232

Noticing that Xcode's templates make the same mistake as the tutorial referred to, I decided to investigate a bit further and came to a different conclusion. The problem is not in the instantiation of the cell, but in the cellIdentifier. The problem occurs when one changes the cellIdentifier in either the .xib or in the class, but not in both. When the .xib is loaded the first time, apparently the cell is queued with the identifier specified in the .xib. Then, when the code is run, the cell can always be dequeued, unless the code insists on a different cellIdentifier such as @"Cell". Checking whether the cell is nil and instantiating the cell rather defeats the purpose of a reusable cell. So, my answer is, strip out all if(cell == nil) statements and check your cell identifiers instead.

Upvotes: 0

Andy Obusek
Andy Obusek

Reputation: 12842

You are never actually instantiating the cell! There's so much good documentation around the web on how to reuse UITableViewCells.

Apple's Table View Programming Guide is amazing and definitely a must read: http://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7-SW1

In the meantime, for a quick fix, change your method to something like:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    //THIS IS THE IMPORTANT PART YOU ARE MISSING
    if(cell == nil) {
        //AND IF YOU PUT AN NSLOG IN HERE, YOU'LL SEE THIS IS CALLED ABOUT THE
        //NUMBER OF TIMES FOR THE INITIALLY VISIBLE CELLS ON THE SCREEN, PLUS 1 OR 2
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    }

    // Configure the cell...
    cell.textLabel.text = [languageNames objectAtIndex:[indexPath row]];

    return cell;
}

Upvotes: 1

Michael Frederick
Michael Frederick

Reputation: 16714

You have to initiate a UITableViewCell if one cannot be dequeued from your UITableView.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    }

    // Configure the cell...
    cell.textLabel.text = [languageNames objectAtIndex:[indexPath row]];

    return cell;
}

Upvotes: 0

Related Questions