Assertion failure in dequeueReusableCellWithIdentifier:forIndexPath: - ios

So I was making an rss reader for my school and finished the code. I ran the test and it gave me that error. Here is the code it's referring to:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
if (cell == nil) {
cell =
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
here's the error in the output:
2012-10-04 20:13:05.356 Reader[4390:c07] * Assertion failure in
-[UITableView dequeueReusableCellWithIdentifier:forIndexPath:], /SourceCache/UIKit_Sim/UIKit-2372/UITableView.m:4460 2012-10-04
20:13:05.357 Reader[4390:c07] * Terminating app due to uncaught
exception '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'
* First throw call stack: (0x1c91012 0x10cee7e 0x1c90e78 0xb64f35 0xc7d14 0x39ff 0xd0f4b 0xd101f 0xb980b 0xca19b 0x6692d 0x10e26b0
0x228dfc0 0x228233c 0x228deaf 0x1058cd 0x4e1a6 0x4ccbf 0x4cbd9 0x4be34
0x4bc6e 0x4ca29 0x4f922 0xf9fec 0x46bc4 0x47311 0x2cf3 0x137b7 0x13da7
0x14fab 0x26315 0x2724b 0x18cf8 0x1becdf9 0x1becad0 0x1c06bf5
0x1c06962 0x1c37bb6 0x1c36f44 0x1c36e1b 0x147da 0x1665c 0x2a02 0x2935)
libc++abi.dylib: terminate called throwing an exception
and here's the code it shows in the error screen:
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
please help!

You're using the dequeueReusableCellWithIdentifier:forIndexPath: method. The documentation for that method says this:
Important: You must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling this method.
You didn't register a nib or a class for the reuse identifier "Cell".
Looking at your code, you seem to expect the dequeue method to return nil if it doesn't have a cell to give you. You need to use the dequeueReusableCellWithIdentifier: for that behavior:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Notice that dequeueReusableCellWithIdentifier: and dequeueReusableCellWithIdentifier:forIndexPath: are different methods. See doc for the former and the latter.
If you want to understand why you'd want to ever use dequeueReusableCellWithIdentifier:forIndexPath:, check out this Q&A.

I think this error is about registering your nib or class for the identifier.
So that you may keep what you are doing in your tableView:cellForRowAtIndexPath function and just add code below into your viewDidLoad:
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
It's worked for me. Hope it may help.

Although this question is fairly old, there is another possibility:
If you are using Storyboards, you simply have to set the CellIdentifier in the Storyboard.
So if your CellIdentifier is "Cell", just set the "Identifier" property:
Make sure to clean your build after doing so. XCode sometimes has some issues with Storyboard updates

i had the same problem replacing with
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell==nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
solved

The problem is most likely because you config custom UITableViewCell in storyboard but you do not use storyboard to instantiate your UITableViewController which uses this UITableViewCell. For example, in MainStoryboard, you have a UITableViewController subclass called MyTableViewController and have a custom dynamic UITableViewCell called MyTableViewCell with identifier id "MyCell".
If you create your custom UITableViewController like this:
MyTableViewController *myTableViewController = [[MyTableViewController alloc] init];
It will not automatically register your custom tableviewcell for you. You have to manually register it.
But if you use storyboard to instantiate MyTableViewController, like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
MyTableViewController *myTableViewController = [storyboard instantiateViewControllerWithIdentifier:#"MyTableViewController"];
Nice thing happens! UITableViewController will automatically register your custom tableview cell that you define in storyboard for you.
In your delegate method "cellForRowAtIndexPath", you can create you table view cell like this :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//Configure your cell here ...
return cell;
}
dequeueReusableCellWithIdentifier will automatically create new cell for you if there is not reusable cell available in the recycling queue.
Then you are done!

I'll just add that Xcode 4.5 includes the new dequeueReusableCellWithIdentifier:forIndexPath:
in its default template code - a potential gotcha for developers expecting the older dequeueReusableCellWithIdentifier: method.

Swift 2.0 solution:
You need to go into your Attribute Inspector and add a name for your cells Identifier:
Then you need to make your identifier match with your dequeue like this:
let cell2 = tableView.dequeueReusableCellWithIdentifier("ButtonCell", forIndexPath: indexPath) as! ButtonCell
Alternatively
If you're working with a nib you may need to register your class in your cellForRowAtIndexPath:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "SwitchCell")
// included for context
let cell = tableView.dequeueReusableCellWithIdentifier("SwitchCell", forIndexPath:indexPath) as! SwitchCell
//... continue
}
Apples's UITableView Class Reference says:
Prior to dequeueing any cells, call this method or the
registerNib:forCellReuseIdentifier: method to tell the table view how
to create new cells. If a cell of the specified type is not currently
in a reuse queue, the table view uses the provided information to
create a new cell object automatically.
If you previously registered a class or nib file with the same reuse
identifier, the class you specify in the cellClass parameter replaces
the old entry. You may specify nil for cellClass if you want to
unregister the class from the specified reuse identifier.
Here's the code from Apples Swift 2.0 framework:
// Beginning in iOS 6, clients can register a nib or class for each cell.
// If all reuse identifiers are registered, use the newer -dequeueReusableCellWithIdentifier:forIndexPath: to guarantee that a cell instance is returned.
// Instances returned from the new dequeue method will also be properly sized when they are returned.
#available(iOS 5.0, *)
func registerNib(nib: UINib?, forCellReuseIdentifier identifier: String)
#available(iOS 6.0, *)
func registerClass(cellClass: AnyClass?, forCellReuseIdentifier identifier: String)

In your storyboard you should set the 'Identifier' of your prototype cell to be the same as your CellReuseIdentifier "Cell". Then you won't get that message or need to call that registerClass: function.

If you are going with Custom Static Cells just comment this method:
//- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// static NSString *CellIdentifier = #"notificationCell";
// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// return cell;
//}
and give the cells an identifier at "Attributes Inspector" in storyboard.

I give you the answer in both Objective C and Swift.Before that I want to say
If we use the dequeueReusableCellWithIdentifier:forIndexPath:,we must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling this method as
Apple Documnetation Says
So we add registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier:
Once we registered a class for the specified identifier and a new cell must be created, this method initializes the cell by calling its initWithStyle:reuseIdentifier: method. For nib-based cells, this method loads the cell object from the provided nib file. If an existing cell was available for reuse, this method calls the cell’s prepareForReuse method instead.
in viewDidLoad method we should register the cell
Objective C
OPTION 1:
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"cell"];
OPTION 2:
[self.tableView registerNib:[UINib nibWithNibName:#"CustomCell" bundle:nil] forCellReuseIdentifier:#"cell"];
in above code nibWithNibName:#"CustomCell" give your nib name instead of my nib name CustomCell
SWIFT
OPTION 1:
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
OPTION 2:
tableView.registerNib(UINib(nibName: "NameInput", bundle: nil), forCellReuseIdentifier: "Cell")
in above code nibName:"NameInput" give your nib name

Working with Swift 3.0:
override func viewDidLoad() {
super.viewDidLoad()
self.myList.register(UINib(nibName: "MyTableViewCell", bundle: nil), forCellReuseIdentifier: "Cell")
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = myList.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath) as! MyTableViewCell
return cell
}

I spent hours last night working out why my programmatically generated table crashed on [myTable setDataSource:self]; It was OK commenting out and popping up an empty table, but crashed every time I tried to reach the datasource;
I had the delegation set up in the h file: #interface myViewController : UIViewController
I had the data source code in my implementation and still BOOM!, crash every time! THANK YOU to "xxd" (nr 9): adding that line of code solved it for me! In fact I am launching a table from a IBAction button, so here is my full code:
- (IBAction)tapButton:(id)sender {
UIViewController* popoverContent = [[UIViewController alloc]init];
UIView* popoverView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 300)];
popoverView.backgroundColor = [UIColor greenColor];
popoverContent.view = popoverView;
//Add the table
UITableView *table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 200, 300) style:UITableViewStylePlain];
// NEXT THE LINE THAT SAVED MY SANITY Without it the program built OK, but crashed when tapping the button!
[table registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
table.delegate=self;
[table setDataSource:self];
[popoverView addSubview:table];
popoverContent.contentSizeForViewInPopover =
CGSizeMake(200, 300);
//create a popover controller
popoverController3 = [[UIPopoverController alloc]
initWithContentViewController:popoverContent];
CGRect popRect = CGRectMake(self.tapButton.frame.origin.x,
self.tapButton.frame.origin.y,
self.tapButton.frame.size.width,
self.tapButton.frame.size.height);
[popoverController3 presentPopoverFromRect:popRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
#Table view data source in same m file
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"Sections in table");
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Rows in table");
// Return the number of rows in the section.
return myArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSString *myValue;
//This is just some test array I created:
myValue=[myArray objectAtIndex:indexPath.row];
cell.textLabel.text=myValue;
UIFont *myFont = [ UIFont fontWithName: #"Arial" size: 12.0 ];
cell.textLabel.font = myFont;
return cell;
}
By the way: the button must be linked up with as an IBAction and as a IBOutlet if you want to anchor the popover to it.
UIPopoverController *popoverController3 is declared in the H file directly after #interface between {}

FWIW, I got this same error when I forgot to set the cell identifier in the storyboard. If this is your issue then in the storyboard click the table view cell and set the cell identifier in the attributes editor. Make sure the cell identifier you set here is the same as
static NSString *CellIdentifier = #"YourCellIdenifier";

I had the same issue, was having the same error and for me it worked like this:
[self.tableView registerNib:[UINib nibWithNibName:CELL_NIB_HERE bundle: nil] forCellReuseIdentifier:CELL_IDENTIFIER_HERE];
Maybe it will be usefull for someone else.

I setup everything correctly in the Storyboard and did a clean build but kept getting the error " must register a nib or a class for the identifier or connect a prototype cell in a storyboard"
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
Corrected the error but i'm still at a loss. I'm not using a 'custom cell', just a view with a tableview embeded. I have declared the viewcontroller as delegate and datasource and made sure the cell identifier matches in file. whats going on here?

This might seem stupid to some people but it got me. I was getting this error and the problem for me was that I was trying to use static cells but then dynamically add more stuff. If you are calling this method your cells need to be dynamic prototypes. Select the cell in storyboard and under the Attributes inspector, the very first thing says 'Content' and you should select dynamic prototypes not static.

Just a supplement of the answers:
There may be a time you set all things right, but you may accidentally add some other UIs in you .xib, like a UIButton:
Just delete the extra UI, it works.

Make sure that the CellIdentifier == identifier of the cell in a storyboard, both names are same. Hope this works for u

In my case, the crash happened when I calleddeselectRowAtIndexPath:
The line was [tableView deselectRowAtIndexPath:indexPath animated:YES];
Changing it to [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; FIXED MY PROBLEM!
Hope this helps anyone

In Swift this problem can be solved by adding the following code in your
viewDidLoad
method.
tableView.registerClass(UITableViewCell.classForKeyedArchiver(), forCellReuseIdentifier: "your_reuse_identifier")

you have to be aware that when using interface builder and creating a Xib (nib) containing one cell that there is also a placeholder created that points to the class that will be used. Meaning, when you place two UITableViewCell in one Xib file you possibly run into the exact same trouble causing an *** Assertion failure .... The placeholder mechanism doesn't work adn will be confused then. Instead placing different placeholders in one Xib read on..
The most easy solution (even if that seems a bit to simple) is to place one Cell at a time in one Xib. IB will create a placeholder for you and all works as expected then. But this then leads directly to one extra line of code, because then you need to load the correct nib/xib asking for the reuseIdentified Cell where it resides in.
So the following example code focuses the use of multiple Cell Indentifiers in one tableview where an Assertion Failure is very common.
// possibly above class implementation
static NSString *firstCellIdentifier = #"firstCellIdentifier";
static NSString *secondCellIdentifier = #"secondCellIdentifier";
// possibly in -(instancetype)init
UINib *firstNib = [UINib nibWithNibName:#"FirstCell" bundle:nil];
[self.tableView registerNib:firstNib forCellReuseIdentifier:firstCellIdentifier];
UINib *secondNib = [UINib nibWithNibName:#"SecondCell" bundle:nil];
[self.tableView registerNib:secondNib forCellReuseIdentifier:secondCellIdentifier];
Another trouble with the use of two CellIdentifier's in one UITableView is that row height and/or section height have to be taken care of. Two cells can have different heights of course.
When registering classes for reuse the code should look different.
The "simple solution" looks also much different when your cells reside inside a Storyboard instead of Xib's. Watch out for the placeholders.
Keep also in mind that interface builder files have version wise variations and need to be set to a version that your targeted OS version supports. Even if you may be lucky that the particular feature you placed in your Xib is not changed since the last IB version and does not throw errors yet. So a Xib made with IB set to be compatible to iOS 13+ but used in a target that is compiled on an earlier version iOS 12.4 will cause also trouble and can end up with Assertion failure.

Ran into this error bc cell re-use identifier was wrong-- a rookie mistake but it happens:
1. Makes SURE cell re-use idenifier has no misspellings or missing letters.
2. Along same line, don't forget capitalization counts.
3. Zeroes are not "O"s (Ohs)

Related

Declaring array error in swift2 [duplicate]

So I was making an rss reader for my school and finished the code. I ran the test and it gave me that error. Here is the code it's referring to:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
if (cell == nil) {
cell =
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
here's the error in the output:
2012-10-04 20:13:05.356 Reader[4390:c07] * Assertion failure in
-[UITableView dequeueReusableCellWithIdentifier:forIndexPath:], /SourceCache/UIKit_Sim/UIKit-2372/UITableView.m:4460 2012-10-04
20:13:05.357 Reader[4390:c07] * Terminating app due to uncaught
exception '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'
* First throw call stack: (0x1c91012 0x10cee7e 0x1c90e78 0xb64f35 0xc7d14 0x39ff 0xd0f4b 0xd101f 0xb980b 0xca19b 0x6692d 0x10e26b0
0x228dfc0 0x228233c 0x228deaf 0x1058cd 0x4e1a6 0x4ccbf 0x4cbd9 0x4be34
0x4bc6e 0x4ca29 0x4f922 0xf9fec 0x46bc4 0x47311 0x2cf3 0x137b7 0x13da7
0x14fab 0x26315 0x2724b 0x18cf8 0x1becdf9 0x1becad0 0x1c06bf5
0x1c06962 0x1c37bb6 0x1c36f44 0x1c36e1b 0x147da 0x1665c 0x2a02 0x2935)
libc++abi.dylib: terminate called throwing an exception
and here's the code it shows in the error screen:
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
please help!
You're using the dequeueReusableCellWithIdentifier:forIndexPath: method. The documentation for that method says this:
Important: You must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling this method.
You didn't register a nib or a class for the reuse identifier "Cell".
Looking at your code, you seem to expect the dequeue method to return nil if it doesn't have a cell to give you. You need to use the dequeueReusableCellWithIdentifier: for that behavior:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Notice that dequeueReusableCellWithIdentifier: and dequeueReusableCellWithIdentifier:forIndexPath: are different methods. See doc for the former and the latter.
If you want to understand why you'd want to ever use dequeueReusableCellWithIdentifier:forIndexPath:, check out this Q&A.
I think this error is about registering your nib or class for the identifier.
So that you may keep what you are doing in your tableView:cellForRowAtIndexPath function and just add code below into your viewDidLoad:
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
It's worked for me. Hope it may help.
Although this question is fairly old, there is another possibility:
If you are using Storyboards, you simply have to set the CellIdentifier in the Storyboard.
So if your CellIdentifier is "Cell", just set the "Identifier" property:
Make sure to clean your build after doing so. XCode sometimes has some issues with Storyboard updates
i had the same problem replacing with
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell==nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
solved
The problem is most likely because you config custom UITableViewCell in storyboard but you do not use storyboard to instantiate your UITableViewController which uses this UITableViewCell. For example, in MainStoryboard, you have a UITableViewController subclass called MyTableViewController and have a custom dynamic UITableViewCell called MyTableViewCell with identifier id "MyCell".
If you create your custom UITableViewController like this:
MyTableViewController *myTableViewController = [[MyTableViewController alloc] init];
It will not automatically register your custom tableviewcell for you. You have to manually register it.
But if you use storyboard to instantiate MyTableViewController, like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
MyTableViewController *myTableViewController = [storyboard instantiateViewControllerWithIdentifier:#"MyTableViewController"];
Nice thing happens! UITableViewController will automatically register your custom tableview cell that you define in storyboard for you.
In your delegate method "cellForRowAtIndexPath", you can create you table view cell like this :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//Configure your cell here ...
return cell;
}
dequeueReusableCellWithIdentifier will automatically create new cell for you if there is not reusable cell available in the recycling queue.
Then you are done!
I'll just add that Xcode 4.5 includes the new dequeueReusableCellWithIdentifier:forIndexPath:
in its default template code - a potential gotcha for developers expecting the older dequeueReusableCellWithIdentifier: method.
Swift 2.0 solution:
You need to go into your Attribute Inspector and add a name for your cells Identifier:
Then you need to make your identifier match with your dequeue like this:
let cell2 = tableView.dequeueReusableCellWithIdentifier("ButtonCell", forIndexPath: indexPath) as! ButtonCell
Alternatively
If you're working with a nib you may need to register your class in your cellForRowAtIndexPath:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "SwitchCell")
// included for context
let cell = tableView.dequeueReusableCellWithIdentifier("SwitchCell", forIndexPath:indexPath) as! SwitchCell
//... continue
}
Apples's UITableView Class Reference says:
Prior to dequeueing any cells, call this method or the
registerNib:forCellReuseIdentifier: method to tell the table view how
to create new cells. If a cell of the specified type is not currently
in a reuse queue, the table view uses the provided information to
create a new cell object automatically.
If you previously registered a class or nib file with the same reuse
identifier, the class you specify in the cellClass parameter replaces
the old entry. You may specify nil for cellClass if you want to
unregister the class from the specified reuse identifier.
Here's the code from Apples Swift 2.0 framework:
// Beginning in iOS 6, clients can register a nib or class for each cell.
// If all reuse identifiers are registered, use the newer -dequeueReusableCellWithIdentifier:forIndexPath: to guarantee that a cell instance is returned.
// Instances returned from the new dequeue method will also be properly sized when they are returned.
#available(iOS 5.0, *)
func registerNib(nib: UINib?, forCellReuseIdentifier identifier: String)
#available(iOS 6.0, *)
func registerClass(cellClass: AnyClass?, forCellReuseIdentifier identifier: String)
In your storyboard you should set the 'Identifier' of your prototype cell to be the same as your CellReuseIdentifier "Cell". Then you won't get that message or need to call that registerClass: function.
If you are going with Custom Static Cells just comment this method:
//- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// static NSString *CellIdentifier = #"notificationCell";
// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// return cell;
//}
and give the cells an identifier at "Attributes Inspector" in storyboard.
I give you the answer in both Objective C and Swift.Before that I want to say
If we use the dequeueReusableCellWithIdentifier:forIndexPath:,we must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling this method as
Apple Documnetation Says
So we add registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier:
Once we registered a class for the specified identifier and a new cell must be created, this method initializes the cell by calling its initWithStyle:reuseIdentifier: method. For nib-based cells, this method loads the cell object from the provided nib file. If an existing cell was available for reuse, this method calls the cell’s prepareForReuse method instead.
in viewDidLoad method we should register the cell
Objective C
OPTION 1:
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"cell"];
OPTION 2:
[self.tableView registerNib:[UINib nibWithNibName:#"CustomCell" bundle:nil] forCellReuseIdentifier:#"cell"];
in above code nibWithNibName:#"CustomCell" give your nib name instead of my nib name CustomCell
SWIFT
OPTION 1:
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
OPTION 2:
tableView.registerNib(UINib(nibName: "NameInput", bundle: nil), forCellReuseIdentifier: "Cell")
in above code nibName:"NameInput" give your nib name
Working with Swift 3.0:
override func viewDidLoad() {
super.viewDidLoad()
self.myList.register(UINib(nibName: "MyTableViewCell", bundle: nil), forCellReuseIdentifier: "Cell")
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = myList.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath) as! MyTableViewCell
return cell
}
I spent hours last night working out why my programmatically generated table crashed on [myTable setDataSource:self]; It was OK commenting out and popping up an empty table, but crashed every time I tried to reach the datasource;
I had the delegation set up in the h file: #interface myViewController : UIViewController
I had the data source code in my implementation and still BOOM!, crash every time! THANK YOU to "xxd" (nr 9): adding that line of code solved it for me! In fact I am launching a table from a IBAction button, so here is my full code:
- (IBAction)tapButton:(id)sender {
UIViewController* popoverContent = [[UIViewController alloc]init];
UIView* popoverView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 300)];
popoverView.backgroundColor = [UIColor greenColor];
popoverContent.view = popoverView;
//Add the table
UITableView *table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 200, 300) style:UITableViewStylePlain];
// NEXT THE LINE THAT SAVED MY SANITY Without it the program built OK, but crashed when tapping the button!
[table registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
table.delegate=self;
[table setDataSource:self];
[popoverView addSubview:table];
popoverContent.contentSizeForViewInPopover =
CGSizeMake(200, 300);
//create a popover controller
popoverController3 = [[UIPopoverController alloc]
initWithContentViewController:popoverContent];
CGRect popRect = CGRectMake(self.tapButton.frame.origin.x,
self.tapButton.frame.origin.y,
self.tapButton.frame.size.width,
self.tapButton.frame.size.height);
[popoverController3 presentPopoverFromRect:popRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
#Table view data source in same m file
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"Sections in table");
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Rows in table");
// Return the number of rows in the section.
return myArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSString *myValue;
//This is just some test array I created:
myValue=[myArray objectAtIndex:indexPath.row];
cell.textLabel.text=myValue;
UIFont *myFont = [ UIFont fontWithName: #"Arial" size: 12.0 ];
cell.textLabel.font = myFont;
return cell;
}
By the way: the button must be linked up with as an IBAction and as a IBOutlet if you want to anchor the popover to it.
UIPopoverController *popoverController3 is declared in the H file directly after #interface between {}
FWIW, I got this same error when I forgot to set the cell identifier in the storyboard. If this is your issue then in the storyboard click the table view cell and set the cell identifier in the attributes editor. Make sure the cell identifier you set here is the same as
static NSString *CellIdentifier = #"YourCellIdenifier";
I had the same issue, was having the same error and for me it worked like this:
[self.tableView registerNib:[UINib nibWithNibName:CELL_NIB_HERE bundle: nil] forCellReuseIdentifier:CELL_IDENTIFIER_HERE];
Maybe it will be usefull for someone else.
I setup everything correctly in the Storyboard and did a clean build but kept getting the error " must register a nib or a class for the identifier or connect a prototype cell in a storyboard"
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
Corrected the error but i'm still at a loss. I'm not using a 'custom cell', just a view with a tableview embeded. I have declared the viewcontroller as delegate and datasource and made sure the cell identifier matches in file. whats going on here?
This might seem stupid to some people but it got me. I was getting this error and the problem for me was that I was trying to use static cells but then dynamically add more stuff. If you are calling this method your cells need to be dynamic prototypes. Select the cell in storyboard and under the Attributes inspector, the very first thing says 'Content' and you should select dynamic prototypes not static.
Just a supplement of the answers:
There may be a time you set all things right, but you may accidentally add some other UIs in you .xib, like a UIButton:
Just delete the extra UI, it works.
Make sure that the CellIdentifier == identifier of the cell in a storyboard, both names are same. Hope this works for u
In my case, the crash happened when I calleddeselectRowAtIndexPath:
The line was [tableView deselectRowAtIndexPath:indexPath animated:YES];
Changing it to [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; FIXED MY PROBLEM!
Hope this helps anyone
In Swift this problem can be solved by adding the following code in your
viewDidLoad
method.
tableView.registerClass(UITableViewCell.classForKeyedArchiver(), forCellReuseIdentifier: "your_reuse_identifier")
you have to be aware that when using interface builder and creating a Xib (nib) containing one cell that there is also a placeholder created that points to the class that will be used. Meaning, when you place two UITableViewCell in one Xib file you possibly run into the exact same trouble causing an *** Assertion failure .... The placeholder mechanism doesn't work adn will be confused then. Instead placing different placeholders in one Xib read on..
The most easy solution (even if that seems a bit to simple) is to place one Cell at a time in one Xib. IB will create a placeholder for you and all works as expected then. But this then leads directly to one extra line of code, because then you need to load the correct nib/xib asking for the reuseIdentified Cell where it resides in.
So the following example code focuses the use of multiple Cell Indentifiers in one tableview where an Assertion Failure is very common.
// possibly above class implementation
static NSString *firstCellIdentifier = #"firstCellIdentifier";
static NSString *secondCellIdentifier = #"secondCellIdentifier";
// possibly in -(instancetype)init
UINib *firstNib = [UINib nibWithNibName:#"FirstCell" bundle:nil];
[self.tableView registerNib:firstNib forCellReuseIdentifier:firstCellIdentifier];
UINib *secondNib = [UINib nibWithNibName:#"SecondCell" bundle:nil];
[self.tableView registerNib:secondNib forCellReuseIdentifier:secondCellIdentifier];
Another trouble with the use of two CellIdentifier's in one UITableView is that row height and/or section height have to be taken care of. Two cells can have different heights of course.
When registering classes for reuse the code should look different.
The "simple solution" looks also much different when your cells reside inside a Storyboard instead of Xib's. Watch out for the placeholders.
Keep also in mind that interface builder files have version wise variations and need to be set to a version that your targeted OS version supports. Even if you may be lucky that the particular feature you placed in your Xib is not changed since the last IB version and does not throw errors yet. So a Xib made with IB set to be compatible to iOS 13+ but used in a target that is compiled on an earlier version iOS 12.4 will cause also trouble and can end up with Assertion failure.
Ran into this error bc cell re-use identifier was wrong-- a rookie mistake but it happens:
1. Makes SURE cell re-use idenifier has no misspellings or missing letters.
2. Along same line, don't forget capitalization counts.
3. Zeroes are not "O"s (Ohs)

Failed to obtain a cell from its DataSource

What is wrong with the below code
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text = #"hello";
return cell;
}
And
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 2;
}
but I'm getting failed to obtain a cell from its dataSource
The entire exception is
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView (<UITableView: 0x7ffe0b092800; frame = (0 97; 414 590); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x7ffe0acf3b10>; layer = <CALayer: 0x7ffe0aceb6e0>; contentOffset: {0, 0}; contentSize: {414, 88}>) failed to obtain a cell from its dataSource (<AllContactsViewController: 0x7ffe0ace60f0>)'
I have set delegate and data source for the table view
Using Swift 3 I encountered this error when I forgot to add the UITableViewDataSource and UITableViewDelegate protocols to the ViewController declaration.
class DataViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
Adding them fixed the problem.
Your error suggests that cellForRowAtIndexPath is returning nil for some reason, and I'm guessing it's because you are failing to dequeue a reusable cell. If you want to confirm this, just set a breakpoint after your current dequeue call: I expect you'll find cell is set to nil.
If you're using modern Xcode templates where you get a prototype cell made for you, you should probably be using this instead:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
If you aren't using an Xcode template, use that line of code anyway then register your own re-use identifier like this:
[self.tableView registerClass:[UITableViewCell self] forCellReuseIdentifier:#"Cell"];
All being well that should resolve the problem. I wrote this up in more detail for Swift users.
Swift 3.0
You just need to add UITableViewDelegate and UITableViewDataSource
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
Below image is just for your reference.
You have to give the cell an identifier. "Cell" give to the identifier of cell as in the attributes inspector of cell in the identifier field.
I re-produced your error and it is due to you have not given an identifier to your cell.
I had the same issue and it was because I forgot to implement UITableViewDataSource and UITableViewDelegate protocols
just add them to your class declaration after the inherited class
xcode must warn me about that, because I used UITableViewDataSource and UITableViewDelegate methods in my code
In my case, I was forgot to declare UITableViewDataSource, UITableViewDelegate for UITableview.
in my case, I forgot to change from dynamic prototype to static cell, check this part in your interface builder
Careful when you are migrating
In case anyone is migrating their code from older swift versions to newer ones, this may occur if you unintentionally don't change UITableViewDataSource functions' syntax to the newer swift syntax.
Unfortunately the compiler won't complain about it.
I faced this when I was migrating my code from swift 2 to swift 4. I didn't change cellForRow DataSource method syntax from:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
to its swift 4 version:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
and I got the error:
"Failed to obtain a cell from its DataSource"
Also please try the below code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
// Reuse and create cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Update cell data contents
cell.textLabel.text = #"Your text here";
cell.detailTextLabel.text=#"Your detailed text label";
return cell;
}
After having done the steps suggested in the other posts, and checking if my cell was nil, it my problem was not resolved and my cell was not showing as nil when I checked it. However, adding this handy code snippet in there did resolve the issue, so it must have been nil at some point where I wasn't catching it.
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ClassNameOfMyCustomCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
Check if have added UITableViewDataSource were added. I missed this, so that app was crashing
Make sure you have not forgotten to implement the UITableViewDelegate and UITableViewDataSource Protocols.
class ExampleViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
Probably you are not using the same Identifier you have given in the main story board for prototype cell. Try to telly the name for Upper case lower case as well to make sure you are using correct identifier.
Good Luck:)
If you are not loading Nib then you have to first register Nib in viewDidLoad.
[self.tableView registerNib:[UINib nibWithNibName:#"RecruitmentDetailViewC" bundle:nil] forCellReuseIdentifier:#"RecruitmentDetailViewC"];
When using tableViewCell apart from the UITableView you have to register
the NIB yourself. When the NIB is not registered the "Failed to obtain a cell from its DataSource" error may occur.
From the error description I always find it hard to figure out, what is wrong.
This error can easily be solved by registering:
[self registerNib:[customerButtonTableViewCell class]
forCellReuseIdentifier:customerButtonCellIdentifier forTableView:tableView];
You must override 'cellForRowAt' method. Take care if you didn't do it
I know this question is quite old already, but for my end, the problem was that the cell isn't part of the target membership.
So the fix I did was to include it in the target membership.

Why does dequeueReusableCellWithIdentifier cause my whole app to crash/hang?

I've read all the relevant other questions on this topic and tried the fixes, none of which have worked. My app crashes/hangs to the extent that I have to force quit Xcode in order to restart working, when dequeueReusableCellWithIdentifier: is called.
It makes no difference if I use dequeueReusableCellWithIdentifier:, or dequeueReusableCellWithIdentifier:forIndexPath: , and I HAVE set the class with registerClass:forCellReuseIdentifier: , as you can see in the code below.
Registering the class in my ViewController:
#implementation LWSFlavourMatchesViewController
-(void)viewDidLoad
{
[super viewDidLoad];
_flavourMatchesView = [LWSFlavourMatchesView flavourMatchesViewWithDataSource:self.flavourMatchesDataSource andDelegate:self.flavourMatchesDelegate];
self.tableView = _flavourMatchesView;
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"flavourCell"];
}
And trying to dequeue cell in tableView:cellForRowAtIndexPath in my dataSource:
#implementation LWSFlavourMatchesDataSource
// other methods...
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *flavourCellIdentifier = #"flavourCell";
NSString *currentSelectedFlavour = [self.flavourWheel selectedFlavour];
UITableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:flavourCellIdentifier forIndexPath:indexPath];
if(tableViewCell == nil)
{
tableViewCell = [[UITableViewCell alloc ]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:flavourCellIdentifier];
}
[tableViewCell.textLabel setText: currentSelectedFlavour];
return tableViewCell;
// return [UITableViewCell new];
}
If I remove all other code but un-comment out return [UITableViewCell new]; then the app does not crash. What is it about my dequeuing that is causing this problem?!
I refactored your tableview delegate. You do not need to check if the cell is nil because you registered the class with [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"flavourCell"];.
I made your cellIdentifier static. But to remove the duplication on the registerClass function may you make a #define REUSE_IDENTIFIER #"flavourCell".
If this is still slow, than is the [self.flavourWheel selectedFlavour]; the cause. Check out the instruments tutorial for performance improvements: http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *flavourCellIdentifier = #"flavourCell";
NSString *currentSelectedFlavour = [self.flavourWheel selectedFlavour];
UITableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:flavourCellIdentifier forIndexPath:indexPath];
[tableViewCell.textLabel setText: currentSelectedFlavour];
return tableViewCell;
}
try removing the class registration:
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"flavourCell"];
You shouldn't need to register the class if you are instantiating a generic UITableViewCell class cell from a storyboard
Another cause for this error can be-
invalid nib registered for identifier (Cell) - nib must contain exactly one top level object which must be a UICollectionReusableView instance
I had a UIView in my xib instead of a collectionViewCell. Of course, if you have multiple top level objects in the .xib, the same crash will show. Hope this helps.
Take care that if your cell is an object in a XIB, and you are using something like :
[self.tableView registerNib:[UINib nibWithNibName:#"cell_class_name" bundle:nil] forCellReuseIdentifier:#"cell_reuse_name"];
to register the cell, be sure the identifier in the attributes inspector in Interface Builder is correct, in this case #"cell_reuse_name".
If the identifier isn't the same, you may be stuck with an odd situation where creating new cells from the nib each time, i.e.
NSArray *objects = [bundle loadNibNamed:#"cell_nib_name"
owner:nil
options:nil];
cell = (UITableViewCell *)[objects safeObjectAtIndex:0];
seems to work fine, but trying to use
[tableView dequeueReusableCellWithIdentifier:#"cell_reuse_name"];
crashes.
In practice it's often easiest to use the same name for the XIB, custom class, and reuse identifier. Then if there are issues, you can just make sure they are all the same.
I had completely different issue. Mismatch between String/XIB based localization. It did help to enable/disable+remove unneeded localizations.
Try this:
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"NIB_NAME" owner:self options:nil];
If it hangs, there's something wrong with your XIB (like in my case).
Try this:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"flavourCellIdentifier"]; }

Instruments shows leak when dequeuing Custom table view cells in cellForRowAtIndexPath

I am getting a memory leak in Instruments related to the table view delegate method cellForRowAtIndexPath when using custom table view cells. I am using XCode 5 but ARC is disabled. I have created my custom table view cell as a separate xib and I load that xib in my viewDidLoad method using nibWithNibName (which, if i remember, checks whether you have a cell or not so you dont have to check if cell != nil in the delegate method). Below are the sections of code that are relevant:
static NSString *const TransactionResultCellIdentifier =
#"TransactionResultCell";
- (void)viewDidLoad
{
[super viewDidLoad];
UINib *cellNib = [UINib nibWithNibName:TransactionResultCellIdentifier bundle:nil];
[self.transactionsTableView registerNib:cellNib forCellReuseIdentifier:TransactionResultCellIdentifier];
cellNib = [UINib nibWithNibName:LoadingCellIdentifier bundle:nil];
[self.transactionsTableView registerNib:cellNib forCellReuseIdentifier:LoadingCellIdentifier];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
TransactionResultCell *cell = (TransactionResultCell *)[tableView dequeueReusableCellWithIdentifier:TransactionResultCellIdentifier];
if(self.isLoading){
return [tableView dequeueReusableCellWithIdentifier:LoadingCellIdentifier];
}
else{
TransactionResult *transactionResult = [self.transactionResults objectAtIndex:indexPath.row];
cell.transactionDescriptionLabel.text = [NSString stringWithFormat:#"%#: %#", transactionResult.transactionID, transactionResult.transactionDescription];
cell.transactionPriceLabel.text = [#"Price: $" stringByAppendingString:transactionResult.transactionPrice];
self.totalPrice += [transactionResult.transactionPrice doubleValue];
}
return cell;
}
Instruments points me to the line up above where i am attempting to dequeue the custom table view cell along with obvious UILabel leaks that are part of the xib structure that i custom built in IB.
Can anyone point me to a solution here? Thanks...
The problem could be that first line in tableView:cellForRowAtIndexPath:. You create a TransactionResultCell that you don't do anything with if self.isLoading evaluates to true. That line should be inside the else clause (as well as the return cell line).
On viewWillDisappear:animated, call to nil out your nibs.
[self.tableView registerNib:nil forCellReuseIdentifier:[WIActivityNameLabelCell reuseIdentifier]];
I have 5 custom nibs in my view, and I was losing about 0.2mb per load, which added up. I suspect it is because I keep my nib and reuseIds in the actual custom table view cell, calling them as class methods. I think this was creating some kind of strong reference that was leaking. Anyway, nilling the nib solved it. Note that the apple documentation states:
If you previously registered a class or nib file with the same reuse identifier, the nib you specify in the nib parameter replaces the old entry. You may specify nil for nib if you want to unregister the nib from the specified reuse identifier.

Copying UITableViewCell

I'm reading a custom table cell in tableView:cellForRowAtIndexPath: from a nib file. This works great for my purposes, except it's quite slow.
Now, I know the right thing to do in the long term is to create the cell entirely in code, and to use a single view, and so on. But this is a prototype, and I don't want to put that much effort into it.
For now, I'd be happy if I was reading the nib only once in the UIViewController subclass, then tableView:cellForRowAtIndexPath: made copies of it. My assumption here is that copying would be faster than reading the nib.
Here's what I use to load the nib, which I call from viewDidLoad: (and retain after)
-(id)loadFromNamed:(NSString*)name {
NSArray *objectsInNib = [[NSBundle mainBundle] loadNibNamed:name
owner:self
options:nil];
assert( objectsInNib.count == 1 );
return [objectsInNib objectAtIndex:0];
}
All is good so far. But the question is: How do I copy this over and over? Is it even possible?
I tried [_cachedObject copy] and [_cachedObject mutableCopy] but UITableViewCell doesn't support either copy protocol.
If I have to, I can just tell them to ignore the speed until I'm prepared to remove the nib entirely, but I'd rather get it going a little faster if there's a low-hanging fruit here.
Any ideas?
I think coping of table cell can be used together with dequeuing mechanism, which will allow to create cell one time (from nib or programmatically or getting it loaded automatically from other nib and linking as an outlet in IB) and then clone it or dequeue it when needed.
UITableViewCell doesn't conform to NSCopying protocol, but it supports keyed archiving/unarchiving mechanism, so it can be used for cloning.
Based on answer "
How to duplicate a UIButton in Objective C? " my data source delegate method looks like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellID = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellID];
if (!cell) {
NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:self.tableViewCell];
cell = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
}
// ... config ...
return cell;
}
And in my case self.tableViewCell is a cell that was loaded one time from view's nib file.
I don't tested what will be faster: "archive + unarchive" to clone or "load nib file + unarchive" which framework will do in case of -loadNibNamed:owner:options:, I used this method only with convenience considerations, but good chances that memory operation vs file operation will be faster.
EDIT: It appears not as easy as it seemed at first. As UIImage doesn't conforms to NSCoding, cells with configured UIImageViews can't be just copied without additional code. Yep, copying whole image is definitely not a good practice, cheers to Apple for pointing this.
Use the cell cloning built into the table view. Apple knew generating a lot of table cells was slow. Check out the docs for this method:
- (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier
You create the cell once, then as new cells are requested, use that method to clone the existing cells. Then you just change what needs to be changed about the new cell and return the cell object.
Also check out the table view realted sample code provided by Apple that uses this method and show you the right way. The fact your cell was loaded from a nib shouldn't matter at all.
Minor clarification: I dont think the above method clone cells for you. Instead it takes cell object that have scrolled off the screen and simply moves them to a new spot. So it's literally reusing a cell. So be sure your custom table view can be set to all the new values it needs outside of the intialization.
Not proud of this solution, but it works with the maximum number of possible IB bindings:
Interface (AlbumTableViewCell is a subclass of UITableViewCell of which an instance is defined in AlbumViewController's XIB file):
#interface AlbumsViewController : UITableViewController {
IBOutlet AlbumTableViewCell *tableViewCellTrack;
}
#property (nonatomic, retain) AlbumTableViewCell *tableViewCellTrack;
Implementation (unarchive / archive makes a copy / clones the table view cell):
#implementation AlbumsViewController
#synthesize tableViewCellTrack;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
AlbumTableViewCell *cell = (AlbumTableViewCell *)[tableView dequeueReusableCellWithIdentifier: #"AlbumCell"];
if (cell == nil) {
AlbumsViewController *albumsViewController = [[[AlbumsViewController alloc] init] autorelease];
[[NSBundle mainBundle] loadNibNamed: #"AlbumsViewController" owner: albumsViewController options: nil];
cell = albumsViewController.tableViewCellTrack;
}
cell.labelTitle.text = ...;
cell.labelArtist.text = ...;
return cell;
}
Well, I'm not sure why all the tutorials out there doesn't specify this step.
When using your own custom UITableViewCell from Nib, calling dequeueReusableCellWithIdentifier is not enough. You have to specify the "Identifier" in the IB, just for for it in the Table View Cell tab section.
Then make sure the identifier you put in IB is the same as the identifier you use for the dequeueReusableCellWithIdentifier.
Here it is in Swift
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell : UITableViewCell?
let cellId = String(format: "Cell%d", indexPath.row)
cell = alertTable!.dequeueReusableCellWithIdentifier(cellId) as! UITableViewCell?
if cell == nil {
let archivedData = NSKeyedArchiver.archivedDataWithRootObject(masterTableCell!)
cell = NSKeyedUnarchiver.unarchiveObjectWithData(archivedData) as! UITableViewCell?
}
// do some stuff
return cell!
}

Resources