18

I am using a Storyboard's prototype UITableViewCell and getting nil when dequeueReusableCellWithIdentifier is called in cellForRowAtIndexPath. I have triple checked that the Xcode's Identifier for the prototype tableviewcell is "PersonCell", deleted the prototype cell and added it again, commented out the UISearchDisplyDelegate and UISearchBarDelegate inheritance for the UITableViewController, and still get nil. I am stumped. Has anyone run into this?

#import "PeopleGroupPickerViewController.h"
#import "DAL.h"
#import "Person.h"

@interface PeopleGroupPickerViewController ()

@end

@implementation PeopleGroupPickerViewController
{
 NSArray *people;
 NSArray *searchResults;
}

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

- (void)viewDidLoad
{
    [super viewDidLoad];

    // 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;
 people = [[DAL sharedInstance] getPeople:false];
 [self.tableView registerClass:[GenericDetailCell class] forCellReuseIdentifier:@"PersonCell"];
}

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

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
 NSString *lastNameSearch = searchText.lowercaseString;
 NSString *firstNameSearch = @"";
 if ([lastNameSearch rangeOfString:@","].length > 0)
 {
  NSArray *names = [lastNameSearch componentsSeparatedByString:@","];
  //NSLog(@"names count",names.count);
  if(names.count > 1)
  {
   lastNameSearch = names[0];
   firstNameSearch = names[1];
   //NSLog(@"first %@ last %@",firstNameSearch,lastNameSearch);
  }
 }

 NSMutableString *predicateText = [[NSMutableString alloc] initWithFormat:@"(sLastName contains[c] '%@')",lastNameSearch];
 if(![firstNameSearch isEqualToString:@""])
 {
  [predicateText appendFormat:@" AND (sFirstName contains[c] '%@')",firstNameSearch];
 }
 NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:predicateText.copy];
 searchResults = [people filteredArrayUsingPredicate:resultPredicate];
 NSLog(@"filterContentForSearchText, searching for %@, # results: %d",predicateText, searchResults.count);
}

-(BOOL) searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
 [self filterContentForSearchText:searchString scope:nil];
 return YES;
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
 if(tableView == self.searchDisplayController.searchResultsTableView)
 {
  return 1;
 }
 else
 {
  return 1;
 }
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
 if(tableView == self.searchDisplayController.searchResultsTableView)
 {
  return searchResults.count;
 }
 else
 {
  return people.count;
 }
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
 GenericDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PersonCell" forIndexPath:indexPath];

 Person_ *thisPerson;
 if(tableView == self.searchDisplayController.searchResultsTableView)
 {
  thisPerson = (Person_ *) searchResults[indexPath.row];
 }
 else
 {
  thisPerson = (Person_ *) people[indexPath.row];
 }
 Person_ *thisSpouse = [[DAL sharedInstance] getSpouse:thisPerson People:people];

 cell.fieldName.text = thisPerson.sName;
 cell.fieldValue.text = thisSpouse.sName;

 return cell;
}
idmean
  • 14,540
  • 9
  • 54
  • 83
MarkF
  • 952
  • 2
  • 8
  • 25
  • 6
    Getting `nil` from `dequeueReusableCellWithIdentifier:` is perfectly normal and expected. If it's `nil` you must create the cell instance. If you registered a cell then use `dequeueReusableCellWithIdentifier:forIndexPath:`. That won't return `nil`. – rmaddy Aug 05 '14 at 02:31
  • rmaddy, the question you referred me to says "With storyboards and tableviews that have prototype cells, [tableView dequeueReusableCellWithIdentifier:] should not return nil.". That question's problem was related to application delegate re-initializing the tableviewcontroller. I am using a plain vanilla UITableViewController in a storyboard, so I'm not sure it is the same problem. – MarkF Aug 05 '14 at 03:52

5 Answers5

23

As maddy stated in the comments, you should create the cell yourself if it is nil, alternatively in your viewDidLoad or where appropriate, you can call one of these methods to make the tableview create the cells for you

Objective-c

[self.tableView registerClass:<#(__unsafe_unretained Class)#> forCellReuseIdentifier:<#(NSString *)#>]
[self.tableView registerNib:<#(UINib *)#> forCellReuseIdentifier:<#(NSString *)#>]

Swift

tableView.register(MyTableViewCell.self, forCellReuseIdentifier: "CellID1")
tableView.register(UINib(nibName: "yourNibName", bundle: nil), forCellReuseIdentifier: "CellID2")
Fonix
  • 11,447
  • 3
  • 45
  • 74
  • [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"PeopleCell"]; gets past the nil error and binds the table, but it binds the wrong cell class. The prototype cell style is "Right Detail" but it binds a generic UITableViewCell class which I can't bind the detailTextLabel . I worked around this by creating my own custom UITableViewCell subclass and connecting the labels to its outlets. – MarkF Aug 05 '14 at 04:14
  • Just another note: I had to register the custom UITableViewCell subclass for both the regular UITableView (self.tableView) and the search results UITableView (self.searchDisplayController.searchResultsTableView) in viewDidLoad. – MarkF Aug 05 '14 at 04:27
  • Just finished working though a similar situation.. Added code instantiated TableViewController to a parent UIViewController object and then attaching the UITableView within my nib to the UITableViewController that I had just instantiated. Here's the trick I found, #1) Create and add the UITableViewController to your parent view controller. 2) Then find the child UITableView that's in the container view and use the '[childTableVC setTableView:myTableView]'. 3) Then register the cells with the table view. Doesn't work for me when registering cells before adding tableViewController. – Dan Devine Jul 18 '17 at 08:16
6

I had this issue after setting the "Restoration ID" in the identity inspector. The correct place to set the reuseIdentifier is the "Identifier" field found in the Attributes inspector.

ChrisBob
  • 329
  • 5
  • 12
1

Adding to the @Fonix answer

Make sure that Inherit Module From Target is enabled

enter image description here

arthankamal
  • 6,341
  • 4
  • 36
  • 51
1

In my case the problem was from here:

I thought should set Restoration ID in identity inspector:

enter image description here

Whereas should set identifier in attribute inspector:

enter image description here

my code eventually became:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TrueOne") as! TrueOneCell
cell.setup(card: cards[indexPath.row])
return cell

and NO NEED to create instance. If it returns nil, the problem is probably from somewhere else.

Mahdi Moqadasi
  • 2,029
  • 4
  • 26
  • 52
0

This can happen if the tableView housing your cell is not assigning your viewcontroller as the delegate and datasource for the tableview as well.

enter image description here

ScottyBlades
  • 12,189
  • 5
  • 77
  • 85