uitableview loading the cells with previous cells' content - iphone

in my table view the cell size fits to the whole screen size. so it creating only two cells but from the 3rd cell its using old cell's content...so i am getting the incorrect data..
how to resolve this...?
thanks.
- (UITableViewCell *)tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
cell = [[[UITableViewCell alloc] initWithFrame:CellFrame reuseIdentifier:CellIdentifier] autorelease];
for(UIView *v in [cell.contentView subviews])
[v removeFromSuperview];
curCellNo = indexPath.row;
if(curCellNo == 0){
min = 0;
max = 8;
x=0;
y=yForFirst;
col = 1;
}else{
if(curCellNo < prevCellNo){
min = min-12;
max = max-12;
}else{
min = max;
max = min+12;
}
x=0;
y=0;
}
prevCellNo = curCellNo;
NSLog(#"Max...%d",max);
NSLog(#"Min...%d",min);
NSLog(#"songsCount...%d",[songs count]);
for(int i=min; i<max && i<[songs count]; i++){
Song *thesong = [self.songs objectAtIndex:i];
CGRect frame;
frame.size.width=coverWidth; frame.size.height=coverHeight;
frame.origin.x=x; frame.origin.y=y;
LazyImageView* asyncImage = [[[LazyImageView alloc] initWithFrame:frame]autorelease];
NSURL* url = [NSURL URLWithString:thesong.cover];
[asyncImage loadImageFromURL:url];
UILabel *label = [[[UILabel alloc]initWithFrame:CGRectMake(x, y+artistLabelYPos, artistLabelWidth, artistLabelHeight)]autorelease];
label.text = [thesong.title stringByAppendingString:[#"\nby " stringByAppendingString:thesong.artist]];
[label setTextAlignment:UITextAlignmentLeft];
label.numberOfLines = 0;
label.font = [UIFont systemFontOfSize:artistLabelFontSize];
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor darkTextColor];
label.alpha = 0.75;
UIButton *playBtn = [UIButton buttonWithType:UIButtonTypeCustom];
playBtn.frame = CGRectMake(x+playBtnXPos, y+playBtnYPos, playBtnWidth, playBtnHeight);
[playBtn addTarget:self action:#selector(playBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
if(playingButton && streamer){
if(playingButtonTag == i && [streamer isPlaying]){
[playBtn setBackgroundImage:[UIImage imageNamed:pauseBtnimgName] forState:UIControlStateNormal];
playingButton = playBtn;
}else [playBtn setBackgroundImage:[UIImage imageNamed:playBtnimgName] forState:UIControlStateNormal];
}else [playBtn setBackgroundImage:[UIImage imageNamed:playBtnimgName] forState:UIControlStateNormal];
playBtn.tag = i;
[cell.contentView addSubview:asyncImage];
[cell.contentView addSubview:label];
[cell.contentView addSubview:playBtn];
label = nil;
asyncImage = nil;
if(curCellNo == 0){
y += estCoverHeight;
if(y>=(estCoverHeight*noOfRowsInCell)){
col++;
x += estCoverWidth;
if(col>=3)
y=0;
else
y=yForFirst;
}
}else{
col =3;
y += estCoverHeight;
if(y>=(estCoverHeight*noOfRowsInCell)){
x += estCoverWidth;
y=0;
}
}
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.contentView.transform = CGAffineTransformMakeRotation(M_PI/2);
return cell;
}

It's because you are never setting the contents of the re-used UITableViewCell's
You are only providing the conetent when (cell == nil) and a new cell is allocated

You're not clearing the contents of the cell when you reuse it. This is UITableView programming 101. Please, there are WWDC videos, Stanford videos all on iTunesU that you should be watching, tableview programming guides in text if you prefer, all that touch on cell reuse.
In a nutshell: Cells are reused to save on memory. You reuse old cells when they go off screen, allocate new ones if none can be reused. If you do reuse old cells, you have to clear any subviews you placed on them first.
On a side note, you probably want a custom cell where you do the bulk of the work you're doing in tableView:cellForRowAtIndexPath: I would strongly suggest you look into that as well.

I have made this some mistake - once you make it and fix it once, you never forget :)
Instances of UITableViewCell are cached for performance reasons. The UITableView class handles this all for you. It basically caches enough instances to cover a full screen's worth of cells plus a few. That means that a table with 100 rows will run off of the same number of instances as a table with 1000 rows.
That means it is your responsibility to update the contents as they appear.
(UITableViewCell *)tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is called when a cell is requested. In your implementation:
if(cell == nil) {
// Instantiate the UITableViewCell here
// Perform operations that need to occur on every cell, regardless of content
}
// Out here, perform content assignments
Hopefully that clears a few things up. You basically just need to shift most of the code that is inside of the conditional to outside of the conditional. That said, I'm not condoning your use of code. You really should be implementing a custom cell. It is easier than what you are doing :)

just try this
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
It worked for me.I had a UITableView with composed cells.I also faced the same problems.

Related

UITableViewCell image changes when scrolling

I have a UITableView with a title and an image on each cell. Some cells will have a default image and others will not. When I scroll the table, the image of some rows is not the expected and the image of another row gets displayed instead of the expected one. If I don't use dequeuereuseidentifier everything works fine, but I want to use it because I have lots of cells.
Any suggestion?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"];
CGRect titleRect = CGRectMake(60, 6, 200, 24);
UILabel *title = [[UILabel alloc] initWithFrame: titleRect];
title.tag = 1;
title.backgroundColor = [UIColor clearColor];
title.font = [UIFont fontWithName:#"AdelleBasic-Bold" size:15.5];
[cell.contentView addSubview:title];
UIImageView *defaultCellImage = [[UIImageView alloc] initWithFrame:CGRectMake(8, 10, 42, 42)];
defaultCellImage.tag = 2;
[defaultCellImage setImage:[UIImage imageNamed: #"Default_Row_Image"]];
[cell.contentView addSubview:defaultCellImage];
}
NSUInteger row = [indexPath row];
Movie *movie = [_movies objectAtIndex: row];
UILabel *titleRowLabel = (UILabel *) [cell.contentView viewWithTag:1];
titleRowLabel.text = [movie title];
UIImageView *cellImage = (UIImageView *) [cell.contentView viewWithTag:2];
if (![movie.imageName isEqualToString:#""])
[cellImage setImage:[UIImage imageNamed: [movie imageName]]];
return cell;
}
The first cells to be used in your table view will be properly loaded. Since there is no cell to dequeue, the if (cell == nil) will return YES and your cell will have its image set to be the default. Then, if your condition for setting a different image is satisfied later in the method, a different image will be shown. So far, so good.
However, when a reusable cell is dequeued, it already has an image set, which might not be the default. Since cell == nil will now return NO, this cell will never have its image reset to the default one, even if it is the image that should be shown.

Showing progress view in corresponding cell when click a download button in a tableview cell

I am new to iphone.I am struck in my project at some task (i.e),I have a table view with 66 rows.In that i am placed different book names for each cell and place a download button to each book.My requirement is when we click on download button it shows the progress view in that particular cell only but i am getting in that particular cell but when i am drag the tableview it will shows the progress views in some that cells also.It is because of dequeue reusability concept but i dont know how to avoid this problem.I want even after drag the tableview it shows the progress view on the cell which i am click the download button (cell)
here is my code below..
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 66;
}
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UIButton *downloadButton = nil;
CustomCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
//here custom cell is another class in that we have the title label declaration
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
downloadButton = [UIButton buttonWithType:UIButtonTypeCustom];
downloadButton.frame = CGRectMake(220,10,50,30);
[downloadButton setImage:[UIImage imageNamed:#"download.png"] forState:UIControlStateNormal];
[downloadButton addTarget:self action:#selector(downloadButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
downloadButton.backgroundColor = [UIColor clearColor];
downloadButton.userInteractionEnabled = YES;
downloadButton.highlighted = YES;
downloadButton.tag = indexPath.row;
NSLog(#"tag is %d",indexPath.row);
[cell.contentView addSubview:downloadButton];
}
NSString *titleLabel = [[appDelegate getBookNames]objectAtIndex:indexPath.row];
cell.TitleLabel.text = titleLabel;
return cell;
}
-(void)downloadButtonClicked:(id)sender{
int index = [sender tag];
NSLog(#"index of the cell is %d",index);
UIButton *button = (UIButton*)sender;
UITableViewCell *cell = (UITableViewCell *)[[button superview] superview];
UILabel *titleLabel = (UILabel *)[cell viewWithTag:100];
NSLog(#"label text =%#",titleLabel.text);
selectedBookTitle = titleLabel.text;
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSMutableArray *allDownloadLinks;
biblePlayerViewController = [[BiblePlayerViewController alloc]init];
allDownloadLinks = [biblePlayerViewController allDownloadLinks];
NSLog(#"all Download Links are %#",allDownloadLinks);
biblePlayerViewController.indexOfSelectedBookTitle = [[appDelegate getBookNames]indexOfObject:selectedBookTitle];
Download* download = [Download downloadWithTitle:selectedBookTitle url:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.audiotreasure.com/%#.zip",[allDownloadLinks objectAtIndex:(biblePlayerViewController.indexOfSelectedBookTitle)]]]PathtoSave:documentsPath];
[[DownloadManager sharedDownloadManager] queueDownload: download];
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
progressView.frame = CGRectMake(10, 40, 300, 20);
[tableViewCell.contentView addSubview:progressView];
}
screen shot of my project is [output of my above code which is in simulator]
you should nil the cell every time in cellForRow. This way it will not be re-used and allocated every time. It should work pretty fine in your case as your tableview is not very large. Just add the following line before cell == nil check:
cell = nil;
It should work now.
I have the same problem, one way to avoid it is to just lock the scrolling ability of the table during the download.

UITableView problem with adding extra cell

First question on this site, although I have been around for a while behind the scenes. I have a problem that I have been racking my head on for the last two days and I hope someone can shed some light on it for me.
I have a UITableView, which is loaded from a SQL database. It has 15 entries in it. I have inserted an extra cell at the beginning of the UITableView. This extra cell is for a UITextField and UIButton which adds an item into the database.
When the view is loaded, the first cell with the custom objects shows fine, and the rest of the table is filled with items from the database and looks just how it should. However, when the UITableView is scrolled down so the first cell is out of view, then back up, it takes the value of the 11th row item and shows it over top the first cell.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)popTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
static NSInteger NameTag = 1;
UITableViewCell *cell = [popTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
CGRect frame;
frame.origin.x =50;
frame.origin.y =10;
frame.size.height =22;
frame.size.width =275;
UILabel *nameLabel = [[UILabel alloc] initWithFrame:frame];
nameLabel.tag = NameTag;
nameLabel.opaque = YES;
nameLabel.textColor = [UIColor grayColor];
nameLabel.backgroundColor = [UIColor clearColor];
[cell.contentView addSubview:nameLabel];
[nameLabel release];
}
int row = [indexPath row];
if (row == 0) {
UIButton *buttonLeft = [UIButton buttonWithType:UIButtonTypeCustom];
[buttonLeft setFrame: CGRectMake( 205, 6, 40, 33)];
[buttonLeft addTarget:self action:#selector(addToList:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:buttonLeft];
//No Alloc for txtField, it is built in IB
[txtField setBorderStyle:UITextBorderStyleNone];
[txtField setFrame: CGRectMake( 17, 12, 180, 23)];
txtField.backgroundColor = [UIColor clearColor];
[txtField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
txtField.clearButtonMode = UITextFieldViewModeAlways;
}else{
UILabel * nameLabel = (UILabel *) [cell.contentView viewWithTag:NameTag];
Add *theObj = [self.theArray objectAtIndex:indexPath.row - 1];
[nameLabel setText:theObj.itemName];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView *imageView = [cell viewWithTag:kTagCellImageView];
if (imageView == nil) {
imageView = [[UIImageView alloc]initWithFrame:CGRectMake(10.0, 10.0, 13.0, 25.0)];
imageView.backgroundColor = [UIColor clearColor];
imageView.tag = kTagCellImageView;
[cell.contentView addSubview:imageView];
}
if([theObj.itemName isEqualToString:#"First Street"]){
imageView.frame = CGRectMake(14,10,13,25);
[imageView setImage:[UIImage imageNamed:#"firststreet"]];
}
else if([theObj.itemName isEqualToString:#"Second Street"]){
imageView.frame = CGRectMake(8,12,29,20);
[imageView setImage:[UIImage imageNamed:#"second"]];
}
else if([theObj.itemName isEqualToString:#"Main Street"]){
imageView.frame = CGRectMake(15,10,13,25);
[imageView setImage:[UIImage imageNamed:#"mainstreet"]];
}
else{
imageView.frame = CGRectMake(8,8,25,25);
[imageView setImage:[UIImage imageNamed:#"iconcustom"]];
}
NSLog(#"%#",itemName);
NSLog(#"%#",itemCategory);
}
return cell;
}
Also here is my cellForRow:
- (NSInteger)tableView:(UITableView *)popTableView numberOfRowsInSection:(NSInteger)section {
return [self.theArray count] + 1; //Add Extra cell to beginning
}
Any ideas would be greatly appreciated.
You need to use a different reuseIdentifier for your first cell. Try this:
NSString *cellIdentifier;
if (indexPath.row == 0) {
cellIdentifier = #"first";
} else {
cellIdentifier = #"not first";
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
// .. cell initialization
}
Obligatory tangential answer - have you thought about setting the tableHeaderView on the UITableView instead? I think that'd accomplish what you're trying to do in a cleaner way (in that it adds an arbitrary view to the top of the table).
Just create a UIView with your "add a new item" controls in it and then set it as the header view when first creating the table.
The issue is here
static NSString *CellIdentifier = #"Cell";
static NSInteger NameTag = 1;
UITableViewCell *cell = [popTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
you are dequeueing all of the cells with the same identifier. Row 1 (index 0) needs to have its own CellIdentifier. Also it looks like you keep adding subviews to the same cells that you dequeue. On your if(cell == nil) check you may want to decide if you want to remove all of the cells contentView subviews or reuse them.

Cant display button on specific rows of UITableView properly for iPhone

I am trying to have a button for selected rows of my table.
Here is the example code I am using:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ControlRowIdentifier = #"ControlRowIdentifier";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:ControlRowIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:ControlRowIdentifier] autorelease];
}
if ([indexPath row] > 5) {
UIImage *buttonUpImage = [UIImage imageNamed:#"button_up.png"];
UIImage *buttonDownImage = [UIImage imageNamed:#"button_down.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0.0, 0.0, buttonUpImage.size.width, buttonUpImage.size.height);
[button setBackgroundImage:buttonUpImage forState:UIControlStateNormal];
[button setBackgroundImage:buttonDownImage forState:UIControlStateHighlighted];
[button setTitle:#"Tap" forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.accessoryView = button;
}
NSUInteger row = [indexPath row];
NSString *rowTitle = [list objectAtIndex:row];
cell.textLabel.text = rowTitle;
return cell;
}
This code works absolutely fine when loaded for 1st time. So, as per the logic it shows 'Tap' button for all rows greater than 5.
Problem occurs when I scroll up and down. Once I do that it just starts putting that button at any random row. I dont understand why it does that and it'll be really helpful if someone can give some tips on this.
Thanks.
The problem is reusing cell's identifier. In your case, the cell with an index of less than 6 must be with one identifier, and the rest from other.
Table view cells is reusable objects and you must do some clean work with it. Try use next:
if ([indexPath row] > 5) {
...
} else {
cell.accessoryView = nil;
}

UITableView is loading every cell

UITableview should only load cells that is visible at first right? My tableview is loading every cell initially which slows it down a lot. I'm using around 1000 rows. Only want it to load a cell when it has to (like user scrolling down). Anyone have any ideas why it's doing this?
I know cellForRowAtIndexPath is getting called initially for every cell. The height of the cells is 89.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UILabel *textName = nil;
UIImageView* image = nil;
unsigned int DATA_TAG = 1001;
unsigned int IMG_TAG = 1002;
// Retrieve a cell is Available
cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Check if no new cell was available
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
// Set the Accessory Data
textName = [[[UILabel alloc]initWithFrame:CGRectMake(cell.frame.origin.x, 80, cell.frame.size.width, 20)]autorelease];
textName.tag = DATA_TAG;
textName.textAlignment = UITextAlignmentCenter;
textName.backgroundColor = [UIColor clearColor];
textName.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
textName.textColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0];
textName.lineBreakMode = UILineBreakModeWordWrap;
[cell.contentView addSubview:textName];
//Set the Image Data
image = [[[UIImageView alloc]initWithFrame:CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, 80)]autorelease];
image.tag = IMG_TAG;
image.contentMode= UIViewContentModeScaleAspectFit;
[cell.contentView addSubview:image];
}
Accessory* acc= [[AccessoryManager sharedManager].currentList objectAtIndex:indexPath.row];
if(acc == nil)
return cell;
textName= (UILabel*)[cell.contentView viewWithTag:DATA_TAG];
textName.text= acc.accessoryName;
image= (UIImageView*)[cell.contentView viewWithTag:IMG_TAG];
[image setImage:acc.accessoryImage];
return cell;
}
Can you please post some code? The method -tableView:cellForRowAtIndexPath is only called when a new "slot" for a potential cell opens up, so you'd need to be doing something majorly wrong to be "loading every cell" initially!
Do you perhaps mean that you're loading all the data initially, and wish to do that in batches?
That's right - it should only load the ones it has to. What is your UITableView's rowHeight? If that was extremely low, the Table might expect to have to load all the cells
If that's not the problem, can you paste in your tableView:cellForRowAtIndexPath: code?
Are you invoking cellForRowAtIndexPath yourself, for example from heightForRowAtIndexPath? If so, you don't need to create the cell to determine its height.