UITableViewCell weird behavior when table is scrolling out of view - iphone

I have a class which extends UITableViewCell. For the purpose of this exercise, let's call it "CustomCell". In CustomCell, I have a UIImageView IBOutlet setup. The image instance name in this case is myImage. I wish to display this image based on certain criteria that's coming back from a server. That data is a dictionary which in this exercise we'll call "serverData". At first, the UITableView renders just fine with the UIImageView showing up in cells which it should. The problem occurs when I start scrolling the actual UITableView, the image gets lost. Somehow it's not properly being either cached or dequeued. Not sure where the problem is or how to better improve on this code. Here's an excerpt:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CustomCellIdentifier = #"CustomCellIdentifier";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:CustomCellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell"
owner:self options:nil];
cell = (CustomCell *)[nib objectAtIndex:0];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
NSDictionary *serverData = myData // previously defined.
if ([[serverData valueForKey:#"foo"] isEqualToString:#"0"])
cell.myImage.hidden = YES;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
return cell;
}

For memory reasons UITableView reuses cells when scrolling (e.g the dequeueReusableCellWithIdentifier) call.
What this means is that the cell you receive could have been configured in anyway that is valid for use with that identifier so you must reset all these properties.
In your case I suspect you are being given a cell with an image that had been hidden so this will fix it:
NSDictionary *serverData = myData // previously defined.
if ([[serverData valueForKey:#"foo"] isEqualToString:#"0"])
cell.myImage.hidden = YES;
else
cell.myImage.hidden = NO;

Remember that your cells are being reused, so you need to reset the cell.myImage.hidden value each time you use that cell
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell"
owner:self options:nil];
cell = (CustomCell *)[nib objectAtIndex:0];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
} else {
cell.myImage.hidden = NO;
}

Related

"Load more.." on tableView with custom cells - cell reusing is causing me some issues

I'm trying to implement "Load more..." on a tableView. I've done it, but I don't know if it's efficient.
The thing is that I have custom cells, and if I do like this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
ArticlesCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell==nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ArticlesCell" owner:self options:NULL];
cell = (ArticlesCell *) [nib objectAtIndex:0];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
tableView.backgroundColor = cell.backgroundColor;
if (indexPath.row <= (self.bookmarks.count - 1)) {
[self configureCell:cell atIndexPath:indexPath];
}else{
cell.textLabel.text = #"Load more...";
}
return cell;
}
It works great but what happens is it's reusing the cells, and if I scroll, every fifth cell (this is height 77.0) will have the label "Load more...", but actually do it's job as normal.
I found this workaround, but I don't know is it good and efficient.
Here it is:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
if (indexPath.row <= (self.bookmarks.count - 1)) {
ArticlesCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell==nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ArticlesCell" owner:self options:NULL];
cell = (ArticlesCell *) [nib objectAtIndex:0];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
tableView.backgroundColor = cell.backgroundColor;
[self configureCell:cell atIndexPath:indexPath];
return cell;
}else{
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.textLabel.text = #"Load more...";
return cell;
}
}
As you can see I'm making the "Load more..." cell a simple UITableViewCell, and not reusing it, since it's only one. Is this good approach? Can you advice me in something better?
Thank you!
Another approach would be to use 2 different cell identifiers, one to identify and reuse (once initially created) an ArticlesCell and another to identify and reuse (once initially created) a "Load more..." UITableViewCell. At least then you will only create the "Load more..." UITableViewCell once rather than every time it scrolls into view.
static NSString *ArticleCellIdentifier = #"ArticleCell";
static NSString *LoadMoreCellIdentifier = #"LoadMoreCell";
The LazyTableImages Apple iOS sample project uses a similar approach (see the Classes/ RootViewController.m).
When you are click on loadmore button then increase the number of rows and reload the tableview . i.e in the method numberofrowsinsection.Let me know if you need any more

UITableViewCell is hidden.

I have a UITableView in my controller. The cells for the UITableView have xib.
For some reason, when the table is loaded, the views of the cells are hidden.
I can select the cell, I see that the cell is not nil, and the views are not nil as well, still the cell is hidden.
Here is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
NSString *cellIdentifier = #"CategoryCell";
CategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil){
NSArray* topObjects = [[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil];
for (id obj in topObjects){
if ([obj isKindOfClass:[CategoryCell class]]){
cell = (CategoryCell*)obj;
}
}
}
id object;
if (indexPath.row < items.count)
object = [items objectAtIndex:indexPath.row];
if ([object isKindOfClass:[MenuCategory class]]) {
// Configure the cell
MenuCategory *cellInfo = (MenuCategory *)object;
[cell setCategory:cellInfo];
}
else if([object isKindOfClass:[MenuSubCategory class]]){
// Configure the cell
MenuSubCategory *cellInfo = [self.items objectAtIndex:indexPath.row];
[cell setSubCategory:cellInfo];
}
return cell;
}
First:
NSArray* topObjects = [[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil];
for (id obj in topObjects){
if ([obj isKindOfClass:[CategoryCell class]]){
cell = (CategoryCell*)obj;
}
}
Replace this with
[[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil];
and declare an IBOutlet in your class that becomes the owner of the xib. This outlet can point to your cell. You then have to connect the Files Owner (has to be of the class the above code is in) within the Cell XIB to your cell. You can then reference your cell (after having declared a property for example) by
self.myCellOutlet;
and don't have to enumerate on all the objects within the xib.
Are you sure that this
[cell setSubCategory:cellInfo];
works?
If you really think your cell is just hidden, did you try to send it a
[cell setHidden:NO];
just in order to see if that's the case and if the error isn't different from the visibility state?
O so sorry wasting your time.
The solution is nothing to do with code.
The table was narrower then the cells. So the text (that was written on the left side of the cell) was outside of the table bounds. And that is why it was missing from the screen.

Reusing of Custom Cells

I taken a custom cell and i am not reusing the identifier over there. I am using the identifier in the table view where i am using that custom cells. But when i run my application and entering the data in textfield my data getting erased when i scroll the view.and My cell are not reusing that data.Can any one help me out in this thing. Thanks!
Custom cell code
-(UITableViewCell *)returnCellForEach:(UITableView *)tableView
{
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
myLabel = (UILabel *)[customCell.contentView viewWithTag:10];
return customCell;
}
Table view 'cellForRowAtIndexPath' code
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"Hello"] autorelease];
}
whats mistake i this thing. Thanks!
Cells should not be used to store your data. If the user enters something in the edit field, you need to copy the content to e.g. a member variable in your view controller. If the cell is scrolled out of view, it is either destroyed, or it MIGHT be kept for reuse. Either way, you can not rely on that edit field to exist anymore.
I dont get your question actually, But I have done like following :
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = (CustomCell *)[[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil] objectAtIndex:0];
}
myLabel = (UILabel *)[customCell.contentView viewWithTag:10];
//myLabel = cell.labelWithTag10;
myLable.text = #"Text";
And It works fine for me;

How can i create a custom UITableViewCell?

I want to know how can i create a custom UITableViewCell with three parts in the UITableViewCell like that:
Create a blank XIB with a UITableViewCell and design the cell dropping elements on it. Set arbitrary tags (1,2,3...) on each element so you can retrieve them later.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = #"xxx";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"NameOfTheBlankXIB" owner:self options:nil];
cell = [nibObjects objectAtIndex:0];
}
// get the elements inside the cell by tag and set a value on them
UILabel*label = (UILabel*)[cell viewWithTag: 1];
label.text = #"some text";
return cell;
}
Another way is to go to the NIB and set this view controller as the file owner and hook the the cell to a ivar of the view controller, like IBOutlet UITableViewCell *tableViewCell.
Now loading this with this instance as the owner automatically hooks the cell to the outlet tableViewCell.
[[NSBundle mainBundle] loadNibNamed:#"NameOfTheBlankXIB" owner:self options:nil];
// at this point the cell is hooked to the ivar so you get a reference to it:
cell = self.tableViewCell;

tableView cellForRowAtIndexPath called multiple times initially

I am trying to understand how the rendering of each cell works. Here's my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"QuestionCell";
static NSString *cellNib = #"QuestionsTableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
//cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:cellNib owner:self options:nil];
cell = [nib objectAtIndex:0];
NSLog(#"%#", #"--> load from nib");
}
return cell;
}
The NSLog in this code is called 8 times initially (because I have 8 rows visible initially). Then when I start scrolling, it's called once and then no more after. I thought it should've been called once and that's it since it should be re-using it after the first call? And why is it called once more after the initial 8? (scratching head...)
It's possible that there could be 9 cells on screen when you are scrolling the table view. This wasn't needed when you started but as you scroll, the 9th cell is required as the top one leaves, a new cell at bottom enters. A cell can't be reused in halves. :)