Clear cells in UITableView to put other views on cells - ios

I'm going to have cells with different subviews depending on which category is chosen. I choose a new category, reload data etc, but instead of displaying new cell with other subviews, the views are being laid on each other when I switch between them categories, how to correct that?
Here is my code:
//cell for row at indexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier];
}
if ([currentCategory isEqualToString:#"Projects"])
{
Project *pr=[projectsArray objectAtIndex:indexPath.row];
NSLog(#"Project ID %i, ProjectName %#", pr.ident, pr.projectName);
UILabel *nameLabel=[[UILabel alloc] initWithFrame:CGRectMake(0, 20, 200, 100)];
nameLabel.text=pr.projectName;
UIImageView *iv=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 192)];
iv.image=pr.baseImage;
[cell addSubview:iv];
[cell addSubview:nameLabel];
}
else if ([currentCategory isEqualToString:#"Glossaire"])
{
Glossaire *gl=[glossaireArray objectAtIndex:indexPath.row];
UILabel *nameLabel=[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 45)];
nameLabel.font=[UIFont fontWithName:#"Arial" size:25.0f];
nameLabel.text=gl.glossaireName;
nameLabel.backgroundColor=[UIColor redColor];
UILabel *introLabel=[[UILabel alloc] initWithFrame:CGRectMake(0, 50, 200, 50)];
introLabel.font=[UIFont fontWithName:#"Arial" size:16.0f];
introLabel.text=gl.intro;
introLabel.backgroundColor=[UIColor redColor];
UILabel *descriptionLabel=[[UILabel alloc] initWithFrame:CGRectMake(0, 100, 350, 100)];
descriptionLabel.font=[UIFont fontWithName:#"Arial" size:16.0f];
descriptionLabel.text=gl.description;
descriptionLabel.backgroundColor=[UIColor redColor];
NSLog(#"Glossaire ID: %i, NAME: %# INTRO: %# Description %#", gl.ident, gl.glossaireName, gl.intro, gl.description);
[cell addSubview:nameLabel];
[cell addSubview:introLabel];
[cell addSubview:descriptionLabel];
}
return cell;
}
//And switching between categories
- (IBAction)viewProjects:(id)sender
{
currentCategory=#"Projects";
projectsArray=[dbm fetchProjectsSummary];
[mainTable reloadData];
}
- (IBAction)viewGlossaire:(id)sender
{
currentCategory=#"Glossaire";
glossaireArray=[dbm fetchGlossaireSummary];
[mainTable reloadData];
}
Also they say the reuse identifier is deprecated, what's the new version for it? thanks!

I just answered a similar question in cellForRowAtIndexPath memory management.
Basically, cells are re-used, so you are adding your subviews to the cell each time it is displayed and they are building up over time. You could either use a different CellIdentifier for each cell layout so a cell with one layout isn't reused for a cell that needs a different layout, or you could just get rid of this logic:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier];
}
And just have this:
UITableViewCell *cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:nil];
That way your cells won't be re-used each time and you don't need to worry about cleaning up the contents from the last time they were used.
To actually answer your question, you could just loop through the cell subviews and say [view removeFromSuperview] to clean them out each time, but I don't think that's a good solution.

Related

Table view cell reload first cell content

This is how my table is populated :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CellNewsInfo *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (!cell) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
// Set up the cell
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
NSString *titleArticle=[[stories objectAtIndex: storyIndex] objectForKey: #"title"];
titleArticle = [titleArticle stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (indexPath.row==0) {
scr=[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 200)];
scr.tag = 1;
scr.autoresizingMask=UIViewAutoresizingNone;
[cell addSubview:scr];
[self setupScrollView:scr];
UIPageControl *pgCtr = [[UIPageControl alloc] initWithFrame:CGRectMake(120, 170, 80, 36)];
[pgCtr setTag:12];
pgCtr.backgroundColor = [UIColor clearColor];
pgCtr.numberOfPages=5;
pgCtr.tintColor=[UIColor redColor];
pgCtr.pageIndicatorTintColor=[UIColor blueColor];
self.pageControl.hidden=YES;
pgCtr.currentPageIndicatorTintColor = [UIColor redColor];
pgCtr.autoresizingMask=UIViewAutoresizingNone;
[cell addSubview:pgCtr];
}
else{
cell.title.text=titleArticle;
cell.title.numberOfLines=2;
why when i scroll it , the first cell is reloading ? i just want to have that scroll view only once at the beginig .
The reason your scrollview is being added again is that the cells are being reused once they are deallocated.
You should look into creating your own custom cells if you are going to display multiple cells types in one tableView, or even using two different cell identifiers depending on if the row is 0.
CellNewsInfo *cell;
if (indexPath.row == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:#"scrollCell" forIndexPath:indexPath];
if ([cell viewWithTag:1]) {
scr = [cell viewWithTag:1];
}
else {
scr=[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 200)];
scr.tag = 1;
}
// continue customization here with scrollview
}
else {
cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// continue customization here without scroll view
}
return cell;

Adding UILabel in custom cell issue

I have custom cell in my UITableView and according to the string's value I want to add a UILabel in the cell.
Here is my code for cell,
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary * tmpDictn = [tableAry objectAtIndex:indexPath.section];
NSString * typeStr = [tmpDictn objectForKey:#“DocumentType”];
NSString * cellIdentifier = #"TestCell";
TestCell *cell = (TestCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell){
cell = [[TestCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
if ([typeStr isEqualToString:#“Text”]) {
UILabel * textLbl = [[UILabel alloc] init];
textLbl.backgroundColor=[UIColor clearColor];
textLbl.textColor=[UIColor lightGrayColor];
textLbl.userInteractionEnabled=NO;
textLbl.numberOfLines = 0;
textLbl.font = [UIFont fontWithName:#“Helvetica" size:16];
[textLbl setFrame:CGRectMake(30, 20, 250, 25)];
textLbl.text= [NSString stringWithFormat:#"%# %i",[splitAry objectAtIndex:i],indexPath.section];
[cell addSubview:textLbl];
}
}
return cell;
}
My UITableView contain 5 cell(dynamic). And only first cell should have this label(this also change according to Text). This code is adding UILabel in first cell but also add UILabel at 3 and 5th cell.
I have checked that its same UILabel created 1 time and added in cell 1st, 3rd and 5th. And "Text" is only at first position in Tableary.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary * tmpDictn = [tableAry objectAtIndex:indexPath.section];
NSString * typeStr = [tmpDictn objectForKey:#“DocumentType”];
NSString * cellIdentifier = #"TestCell";
TestCell *cell = (TestCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell){
cell = [[TestCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
UILabel *lbl = [cell viewWithTag:1];
if(lbl)
{
[lbl removeFromSuperView];
}
if ([typeStr isEqualToString:#“Text”]) {
UILabel * textLbl = [[UILabel alloc] init];
textLabel.tag = 1;
textLbl.backgroundColor=[UIColor clearColor];
textLbl.textColor=[UIColor lightGrayColor];
textLbl.userInteractionEnabled=NO;
textLbl.numberOfLines = 0;
textLbl.font = [UIFont fontWithName:#“Helvetica" size:16];
[textLbl setFrame:CGRectMake(30, 20, 250, 25)];
textLbl.text= [NSString stringWithFormat:#"%# %i",[splitAry objectAtIndex:i],indexPath.section];
[cell addSubview:textLbl];
}
}
return cell;
}
Try using a different cell identifier for the cells that need a label added to them, e.g. #"TextCell". Otherwise, you are reusing cells that already have a label added even if it is not supposed to be there. Alternatively, you could remove the label (if it is there) in an 'else' condition of your if ([typeStr isEqualToString:#“Text”]) block but I think that the former is cleaner.
You are using
[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
The problem for your code is that table view is reusing your cell. So it will appear in many as your number of cells increases.
I am a bit old programmer but I think the best way is to
if (!cell)
{
cell = [[TestCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
//Add your label here.
}
if(!label)
{ //your code;
//set label text to nil if your condition not met;
}
A quick solution for you is to put an else and set text to nil;
Cheers.

How to disable reloading tableview when scrolling in iOS

I want to disable reloading table view when scrolling. Now, my app when user scroll the uitableview, cellForRowAtIndexPath has been recalled. How can I disable it when srcolling? Please give me some advice. Thanks in advance.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"%d,%d",indexPath.section,indexPath.row];
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *FileNameLabel;
UILabel *UploadTimeLabel;
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
CFileNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(40, 0, 130, 30)];
UploadTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(40, 20, 130, 25)];
FileNameLabel.tag = 1000;
FileNameLabel.backgroundColor = [UIColor clearColor];
FileNameLabel.font = [UIFont fontWithName:#"Helvetica" size:14];
FileNameLabel.font = [UIFont boldSystemFontOfSize:14];
FileNameLabel.textColor = [UIColor blackColor];
// FileNameLabel.text =[temp objectAtIndex:indexPath.row];
[cell.contentView addSubview: FileNameLabel];
[FileNameLabel release];
UploadTimeLabel.tag = 2000;
UploadTimeLabel.backgroundColor = [UIColor clearColor];
UploadTimeLabel.font = [UIFont fontWithName:#"Helvetica" size:12];
UploadTimeLabel.textColor = [UIColor grayColor];
// UploadTimeLabel.text = [UploadTimeArray objectAtIndex:indexPath.row];
[cell.contentView addSubview: UploadTimeLabel];
[UploadTimeLabel release];
}
if( [OriginalArray count] > 0)
{
UILabel *fileNameLbl = (UILabel*)[cell.contentView viewWithTag:1000];
//fileNameLbl.text =[temp objectAtIndex:indexPath.row];
fileNameLbl.text =[[OriginalArray valueForKey:#"FILENAME"] objectAtIndex:indexPath.row];
UILabel *uploadlbl = (UILabel*)[cell.contentView viewWithTag:2000];
uploadlbl.text =[[OriginalArray valueForKey:#"UPLOADTIME"] objectAtIndex:indexPath.row];
}
_tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
return cell;
}
You cannot block the cellForRowAtIndexPath: from calling when scrolling the tableview. If something need not happen every time, You may keep it in if condition.
if (cell == nil)
{
//Functionality goes here when it not needed to happen every time.
}
Do not implement the method UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"yourID"]; or UITableViewCell *cell = [tableView dequeueCellWithReuseIdentifier:nil];
It will prevent the table view from reusing the cell. But its not a good idea if your tableview is going to contain large number of cell. Hope this helps. :)
Instead of trying to avoid being reloaded, maybe you could play with your data source, so that it appears as the data doesn't change. I hope you understand what I mean.
use dequecellforrowatindex method to avoid cell reloading.

Table View troubles with dequeue cell

So I am having trouble showing my data in a UITableView. I do believe it has something to do with reusing the cells. I have checked online and here at SO but have not found a solution that works for me. Any help would be appreciated.
I have an Array that is populated by text and pictures. I am then showing the information in a tableView. If I were to use static sized cells everything works out fine, but the amount of text changes, so I have also implemented the heightForRowAtIndexPath method. This works as well, until I scroll all the way down to the bottom.
After that, when I scroll back up, all the cell heights change and the display gets all jumbled. Some text gets cut off, pictures get chopped and some of the cells only have the last portion of text. I really think it has something to do with reusing the cells, but I don’t know how to attack this problem. Below is my code for cellForRowAtIndexPath and heightForRowAtIndexPath.
- (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:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[NSString class]])
{
NSString *label = [_theBigArray objectAtIndex:indexPath.row];
CGSize stringSize = [label sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:NSLineBreakByWordWrapping];
UITextView *textV = [[UITextView alloc] initWithFrame:CGRectMake(5, 5, 290, stringSize.height +50)];
textV.font = [UIFont systemFontOfSize:15];
textV.text = [_theBigArray objectAtIndex:indexPath.row];
textV.textColor = [UIColor blackColor];
textV.editable = NO;
[cell.contentView addSubview:textV];
}
else if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[UIImage class]])
{
UIImageView *imageV = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 290, 100)];
imageV.contentMode = UIViewContentModeScaleAspectFit;
imageV.image = [_theBigArray objectAtIndex:indexPath.row];
[cell.contentView addSubview:imageV];
}
return cell;
[tableView reloadData];
}
For heightForRowAtIndexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
int rowHeight = 0.0f;
if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[NSString class]])
{
NSString *temp = [_theBigArray objectAtIndex:indexPath.row];
CGSize size = [temp sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:NSLineBreakByWordWrapping];
rowHeight = size.height+50;
}
else if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[UIImage class]])
{
rowHeight = 115.0f;
}
//NSLog(#"rowHeight is %i", rowHeight);
return rowHeight;
[tableView reloadData];
}
I even tried to make two different cells and call them separately, but the same thing happens. I did still use the same heightForRowAtIndexPath method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *newCell = [[UITableViewCell alloc] init];
if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[NSString class]])
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
NSString *label = [_theBigArray objectAtIndex:indexPath.row];
CGSize stringSize = [label sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:NSLineBreakByWordWrapping];
UITextView *textV = [[UITextView alloc] initWithFrame:CGRectMake(5, 5, 290, stringSize.height +50)];
textV.font = [UIFont systemFontOfSize:15];
textV.text = [_theBigArray objectAtIndex:indexPath.row];
textV.textColor = [UIColor blackColor];
textV.editable = NO;
[cell.contentView addSubview:textV];
newCell = cell;
}
else if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[UIImage class]])
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"PictureCell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"PictureCell"];
}
UIImageView *imageV = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 290, 100)];
imageV.contentMode = UIViewContentModeScaleAspectFit;
imageV.image = [_theBigArray objectAtIndex:indexPath.row];
[cell.contentView addSubview:imageV];
newCell = cell;
}
return newCell;
[tableView reloadData];
}
Any ideas?
The main problem is that you're adding subviews to cells every time they scroll in, but when a cell is reused, it will already have those subviews added. (That is, when a cell is reused, it will already have a UITextView or UIImageView depending on the reuse identifier.)
You need to check if these subviews exist first; this is commonly done by using the -[UIView viewWithTag] method, or by subclassing UITableViewCell and assigning each view as a property.
(You can take a look at the SO question How to get other control value from UITableViewCell? to see how to use viewWithTag. I would avoid subclassing UITableViewCell until you're more comfortable with the out-of-the-box implementation.)
Also, this line of code:
UITableViewCell *newCell = [[UITableViewCell alloc] init];
is a terrible idea, because you are creating a new UITableViewCell without checking to see if you can reuse one first. This defeats the entire purpose of reusing cells, which is fast scrolling performance. Instead, just declare it without initializing it:
UITableViewCell *newCell;
Also, in heightForRowAtIndexPath, you are
declaring rowHeight as an int (it should be a CGFloat)
trying to call reloadData after the method returns (which will never happen, but you should never try to call reloadData from this method)

Add a label to the first UITableViewCell row while reusing cells

Is it possible to add a label to a UITableViewCell while reusing cells? This is the code I have been trying but when I scroll down and the cells get reused my label moves around. I just want the label in the very first row at all times, but I cannot figure this out for the life of me. I know I can not reuse the cells and it will work but I would like to reuse the cells. Please help me figure this out. Thank you very much...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SimpleTableIdentifier] autorelease];
}
if ([indexPath row] == 0) {
UILabel* Label = [[UILabel alloc] initWithFrame:CGRectMake(2,2, 62, 23)];
[Label setText:#"Text"];
[cell addSubview:Label];
[Label release];
}
return cell;
}
I think I figured it out. I made the 1st row not reusable.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
static NSString *CellsToBeReused = #"CellsToBeReused";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellsToBeReused];
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellsToBeReused] autorelease];
}
if ([indexPath row] == 0) {
UITableViewCell *first_Cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
UILabel* Label = [[UILabel alloc] initWithFrame:CGRectMake(2,2, 62, 23)];
[Label setText:#"Text"];
Label.backgroundColor = [UIColor whiteColor];
[first_Cell.contentView addSubview:Label];
[Label release];
return first_Cell;
} else {
return cell;
}
}

Resources