Reuse UITableViewCell's without data repetition - ios

I'm having some problem with multiple custom UITableViewCell reuse. I have 3 types of custom UITableViewCell. I tried to reuse the UITableViewCell using dequeueReusableCellWithIdentifier.
I wrote the prepareForReuse and made all possible subviews data nil. But still I was getting some repetitions of data in some UITableViewCell's. So I was thinking of reusing just the xib without any data.
I made up the following code. The app seems to have some freezing problem now. Somebody please show me the correct way to reuse tableview UITableViewCells without data repetition.
static NSString *cellIdentifier = #"SystemListCell";
IXSystemMessageCustomCell *cell = (IXSystemMessageCustomCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:#"IXSystemMessageCustomCell" owner:self options:nil] objectAtIndex:0];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
IXSystemMessageCustomCell *cellToSave = cell;
[self configureSystemCell: cellToSave atIndexPath:indexPath];
return cellToSave;

Do not modify the data in prepareForReuse
A better way is to
dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath
We should always modify the data in cellForRowAtIndexPath as this helps in reuse of cells.
Also, since you have created IXSystemMessageCustomCell class it is better to use this :
[[IXSystemMessageCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
and write the setup code in initWithStyle:reuseIdentifier:
instead of this:
cell = [[[NSBundle mainBundle] loadNibNamed:#"IXSystemMessageCustomCell" owner:self options:nil] objectAtIndex:0];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
Cheers!

You can use 3 different reuse identifier for your 3 cell types:
IXSystemMessageCustomCell *cell;
if (some condintion) {
cell = (IXSystemMessageCustomCell *)[tableView dequeueReusableCellWithIdentifier:"CellReuseIdentifierType1"];
} else if (some condition 2) {
cell = (IXSystemMessageCustomCell *)[tableView dequeueReusableCellWithIdentifier:"CellReuseIdentifierType2"];
} else {
cell = (IXSystemMessageCustomCell *)[tableView dequeueReusableCellWithIdentifier:"CellReuseIdentifierType3"];
}
In this case you don't need to re-configure cell each time for each cell type

Try this...
IXSystemMessageCustomCell *cell = nil;
cell = (IXSystemMessageCustomCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
By setting your cell to nil first, you are clearing out previous data associated with the reused cell.
Also register your nib once in your viewDidLoad or similar table view lifecycle method, so that this is not required at every call to tableView:cellForRowAtIndexPath.
So extract your static call from any method to be a private declaration...
static NSString *cellIdentifier = #"SystemListCell";
and adjust your methods to read...
- (void)viewDidLoad {
UINib *nib = [UINib nibWithNibName:#"IXSystemMessageCustomCell" bundle:nil];
[<<self.tableView>> registerNib:nib
forCellReuseIdentifier:cellIdentifier];
<< ... other code ... >>
}
and
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
IXSystemMessageCustomCell *cell = nil;
cell = (IXSystemMessageCustomCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[self configureSystemCell:cell atIndexPath:indexPath];
return cell;
}

Related

Show data in Custom tableview Cell

I am new to developing. i have some experience but i'm not great at it (yet). I'm working on this tableview app which shows data. I want to show that data in a custom cell. But i don't know how to do that.
This is the code i use for my tableview:
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
HistoryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"HistoryViewTableCell"];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"HistoryViewTableCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
NSDictionary * tempDictionary = [tableData objectAtIndex:indexPath.row];
cell.textLabel.text = [tempDictionary objectForKey:#"PickupAddress"];
return cell;
}
I need to show this line of code in my custom cell.
cell.textLabel.text = [tempDictionary objectForKey:#"PickupAddress"];
I already made a class and a xib file for the custom cell
This may solve the problem:
HistoryTableViewCell *cell = (HistoryTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"HistoryViewTableCell"];
your written kind of code will not work. if you are using custom XIB than add a UILable in the xib. and than assign them tag number.
now from ViewController do the following:
write this after #implementation
enum {
CELL_TITLE = 2,
CELL_SUBTITLE_1 = 3,
};
Load the Custom XIB File in cellforrowatindexpath method.
// Get table View Cell
static NSString *CellIdentifier = #"customCell";
UITableViewCell *cell = [atableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib;
nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
cell = (UITableViewCell *) [nib objectAtIndex:0];
}
Now Add text on Lable with the below methods;
UILabel *titleLable = (UILabel *) [cell.contentView viewWithTag:CELL_TITLE];
titleLable.text = #"This is your text";

Empty prototype cell for a UITableView

I have a new project where I had to drag a prototype cell to a existing table view. I then
added some labels to the prototype cell with appropriate tags
set a identifier for the cell
and then in my tableview delegate when I get the delegate callback :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"myCustomCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
UILabel *nameLabel = (UILabel *)[cell viewWithTag:11];
I see that nameLabel is nil. I have double checked and tripled checked the tag and reusable identifier with no luck. In the storyboard, I see that the tableview has the pro type cell as its cell with the contentView showing my labels. What am I missing?
Do you ever create a new cell in the if (cell == nil) branch? If yes, you are creating a regular UITAbleViewCell there. Do not expect any custom lable there, because you don't load it from any nib file nor from the storyboard.
Of what type is the cell object? NSLog it or have a look in the debugger which type is actually created.
You are allocating UITableViewCell which don't contain your custom label. If you have created a View using Storyboard then you should allocate that view using following method.
You can create a Utility method to get Class instance from NibName
+ (id)loadNibNamed:(NSString *)nibName ofClass:(Class)objClass
{
if (nibName && objClass)
{
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:nibName
owner:nil
options:nil];
for (id currentObject in objects )
{
if ([currentObject isKindOfClass:objClass])
return currentObject;
}
}
return nil;
}
Use this in your code like
CustomViewCell *cell = (CustomViewCell *)[tableView dequeueReusableCellWithIdentifier:#"CustomViewCell"];
if (cell == nil)
{
cell = [Utility loadNibNamed:#"CustomViewCell" ofClass:[CustomViewCell class]];
}
cell.yourLabel.text = #"Dummy Text";
Hopefully this will help you.

Getting EXC_Bad_Access while scrolling tableview

I am getting EXC_BadAccess error message while i am scrolling tableview.
the following is the code i have done in cellForRowAtIndexPath.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier=#"customCellHistory";
customCellHistory *cell=(customCellHistory*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects=[[NSBundle mainBundle]loadNibNamed:#"customCellHistory" owner:self options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]] ) {
cell=(customCellHistory*)currentObject;
break;
}
}
}
cell.lb11.text=[cellArray1 objectAtIndex:indexpath.row];
cell.lbl2.text=[cellArray2 objectAtIndex:indexpath.row];
return cell;
}
I can sense the problem is arising due to some mistake in the above code.
I used CustomCell in the above code to display a customized cell.
can anyone tell me what wrong have i done in this code
Hey try the following code, dont forget to set the cell identifier in you custom XIB to customCellHistory
At the top
#import "customeCellHistory.h"
then
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"customCellHistory";
customCellHistory *cell = (customCellHistory *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"customCellHistory" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.lb11.text=[cellArray1 objectAtIndex:indexpath.row];
cell.lbl2.text=[cellArray2 objectAtIndex:indexpath.row];
return cell;
}
The problem seems to arise from the strange loop you have. Use the last object method to set cell from the nib.
You are reusing a cell and then changing the cell in your loop which probably leads to a cell not existing where you are looking.
if (cell == nil) {
NSArray *topLevelObjects=[[NSBundle mainBundle]loadNibNamed:#"customCellHistory" owner:self options:nil];
You don't need that code. Just register the nib with the table view, earlier on:
[self.tableView registerNib:[UINib nibWithNibName:#"customCellHistory" bundle:nil]
forCellReuseIdentifier:#"customCellHistory"];
Now when you call dequeueReusableCellWithIdentifier, the nib will be loaded and the cell will be delivered, if there is no spare cell in the reuse pile. This ensures that you consistently have a cell and that it is the right kind of cell. Use the methods the framework gives you.

Can't get my custom UITableViewCell to show

I am using a TableView xib and all the delegates and datasource seem to be running fine.
If I set self.textLabel.text, it displays the generic number of tableviews correctly, but I need to have my custom TableViewCell showing.
I created a HistoryCell.xib that has just a tableviewcell in it.
I created a UITableViewCell class "HistoryCell.h/HistoryCell.m" and it is set as the file owner of HistoryCell.xib.
I connected the UILabels to the HistoryCell.h
UILabel statusLabel
UILabel nameLabel
UILabel timeLabel
In my main ViewController class int he cellForRowAtIndexPath
I am putting in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"historyCellType";
HistoryCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[HistoryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.nameLabel.text = #"Donald Duck";
return cell;
}
What am I doing wrong?
Thanks!
You should register the nib like this (probably in viewDidLoad):
[self.tableView registerNib:[UINib nibWithNibName:#"HistoryCell" bundle:nil ] forCellReuseIdentifier:#"historyCellType"];
Then in your cellForRowAtIndexPath method, use this new way where you don't need the if cell == nil clause:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
HistoryCell *cell = [tableView dequeueReusableCellWithIdentifier:#"historyCellType" forIndexPath:indexPath];
cell.nameLabel.text = #"Donald Duck";
return cell;
}
You need to deserialize the actual XIB data:
HistoryCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *cellnib = [[NSBundle mainBundle] loadNibNamed:#"HistoryXIBName" owner:self options:nil];
cell = (HistoryCell *)[cellnib objectAtIndex:0];
}
Otherwise it's just using a "Default" cell.
EDIT: Also, if you are using storyboards, dynamic cell prototypes remove the need to create cells from NIB files (in case that's an option for you.)
EDIT The 2nd:
You can try this as well (this assumes you are using ARC):
HistoryCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
HistoryCell *aNewCell = [[HistoryCell alloc] initWithNibName:#"HistoryXIBName" bundle:nil];
cell = (HistoryCell *)*aNewCell.view;
}
There's another method using outlets to your cell in the containing view's XIB file that's a bit more involved that I used to use when iOS4 was current. If the edited version isn't' working for you, I'll dig out an old project and remember how I did it.
When you use tableViewCell with xib, while instantiation load from xib.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"historyCellType";
HistoryCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[NSBundle mainBundle]loadNibNamed:#"HistoryCell"
owner:nil
options:nil]lastObject];
}
cell.nameLabel.text = #"Donald Duck";
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellidentifier=#"cell1";
customcell *cell =
(customcell*)[tableView dequeueReusableCellWithIdentifier:cellidentifier];
if (cell==nil) {
[tableView registerNib:[UINib nibWithNibName:#"Customcell" bundle:nil]
forCellReuseIdentifier:cellidentifier];
cell =
(customcell*)[tableView dequeueReusableCellWithIdentifier:cellidentifier
forIndexPath:[NSIndexPath indexPathForItem:indexPath.row
inSection:indexPath.section]];
}
return cell;
}

How to set and dequeue custom UITableViewCell with dynamic reuse identifier?

Here's what I am trying to do ultimately. I want to display a menu of items in a UITableView, but dynamically, so that the type of item displayed determines the custom cell view loaded. For example, let's say the menu item type is 'switch', then it will load a nib named 'switch.xib' and the state will be on/off depending on that particular menu item's value. There may be 5 items that are "switch" type, but different values. So I want to use the same xib for each one, but 5 instances.
So, long way around to the question. When I load the cell view from the nib, I would think it would need unique reuse identifiers for the dequeue for when it scrolls back on the screen, right? (Unique for each instance, i.e. each menu item.) In the UITableViewCell in Interface Builder, I see where I can set a reuse identifier property, but I want to set it at run time for each instance of the switch. For example, Menu Item #1 is a switch, #2 is a text field, #3 is a switch, etc. So #1 and #3 both need unique cell ID's to dequeue.
Here's what my cellForRowAtIndexPath looks like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Cells are unique; dequeue individual cells not generic cell formats
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell%d", indexPath.row];
ITMenuItem *menuItem = [menu.menuItems objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// Load cell view from nib
NSString *cellNib = [NSString stringWithFormat:#"MenuCell_%#", menuItem.type];
[[NSBundle mainBundle] loadNibNamed:cellNib owner:self options:nil];
cell = myCell;
self.myCell = nil;
}
// Display menu item contents in cell
UILabel *cellLabel = (UILabel *) [cell viewWithTag:1];
[cellLabel setText:menuItem.name];
if ([menuItem.type isEqualToString:#"switch"]) {
UISwitch *cellSwitch = (UISwitch *) [cell viewWithTag:2];
[cellSwitch setOn:[menuItem.value isEqualToString:#"YES"]];
}
else if ([menuItem.type isEqualToString:#"text"]) {
UITextField *textField = (UITextField *) [cell viewWithTag:2];
[textField setText:menuItem.value];
}
return cell;
}
You can set the reuse identifier in the nib file. So for switch.xib you would use 'switch' as the reuse identifier. Then just change
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell%d", indexPath.row];
to
NSString *CellIdentifier = menuItem.type;
assuming that menuItem.type is 'switch'
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil)
{
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
TextFieldFormElement *item = [self.formItems objectAtIndex:indexPath.row];
cell.labelField.text = item.label;
return cell;
}
For my idea, if your types are not too much design each cell in different xib and swift file. Mainly for performance issues.
If not possible to dequeue, give them different identifier. You can register more than one identifier for tableview or collection view (one of our apps using 12 different cells this way.)
But handling IBActions will be little messy this way.
Your custom cell from xib file can be loaded in a way like this. The xib file name is the same as the cell class name and the reuse id.
- (YourCell *)tableView:(UITableView *)_tableView getCellWithId:(NSString *)cellId
{
YourCell *cell;
/* Cell id and xib have the same name. */
cell = [_tableView dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:cellId owner:self options:nil];
cell = [nib objectAtIndex:0];
}
return cell;
}

Resources