Tried my hardest to find the answer here first, but I'm stuck. I have a UITableView set with UITableViewStyleGrouped with 4 sections, each with one or two rows. In two of the sections I needed a row to be a larger height to hold the content I'm sticking in there.
Looks nice except when I scroll up and down, textLablels, accessories and extra subviews start to shift into different rowss and I can't figure out why.
This screenshot shows the table view when it loads and then after I scroll up and down a few times. Each time I scroll different row content shuffles.
I thought I read something about this being an issue with the grouped style. Sure enough, I don't see this issue if I change the table style to default. Am I not allowed to dynamically set the height for some rows when using the grouped style?
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == kSection_Info && indexPath.row == kSectionRow_InfoPhoto)
{
return 84.0;
}
else if (indexPath.section == kSection_Level && indexPath.row == kSectionRow_LevelLevel)
{
return 70.0;
}
return 44.0;
}
I'm setting up each row manually in celForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"RecipientEntryCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
switch (indexPath.section)
{
case kSection_Info:
{
switch (indexPath.row)
{
case kSectionRow_InfoName:
{
cell.textLabel.text = #"Name";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(74, 8, 195, 25)];
self.nameLabel.textAlignment = UITextAlignmentRight;
self.nameLabel.font = [UIFont systemFontOfSize:16];
self.nameLabel.textColor = [UIColor blueColor];
self.nameLabel.text = self.currentRecipient.fullName;
[cell.contentView addSubview:self.nameLabel];
break;
}
case kSectionRow_InfoPhoto:
{
cell.textLabel.text = #"Photo";
self.imageButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.imageButton.frame = CGRectMake(10, 14, 64, 64);
[self.imageButton addTarget:self action:#selector(onImageButtonTouch:) forControlEvents:UIControlEventTouchUpInside];
NSString *imageName = #"add_image.png";
UIImage *thumb = [UIImage imageNamed:imageName];
[self.imageButton setImage:thumb forState:UIControlStateNormal];
cell.accessoryView = self.imageButton;
break;
}
default:
{
break;
}
}
break;
}
case kSection_List:
{
switch (indexPath.row)
{
case kSectionRow_ListHasList:
{
cell.textLabel.text = #"Is Listed";
self.listSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = self.listSwitch;
break;
}
case kSectionRow_ListBudget:
{
cell.textLabel.text = #"List Amount";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
self.budgetLabel = [[UILabel alloc] initWithFrame:CGRectMake(124, 8, 145, 25)];
self.budgetLabel.textAlignment = UITextAlignmentRight;
self.budgetLabel.font = [UIFont systemFontOfSize:16];
self.budgetLabel.textColor = [UIColor blueColor];
self.budgetLabel.text = [#"$" stringByAppendingFormat:#"%0.2f", [self.currentRecipient.budget floatValue]];
[cell.contentView addSubview:self.budgetLabel];
break;
}
default:
{
break;
}
}
break;
}
case kSection_Level:
{
switch (indexPath.row)
{
case kSectionRow_LevelLevel:
{
self.levelSlider = [[UISlider alloc] initWithFrame:CGRectMake(8, 2, 284, 40)];
self.levelSlider.minimumValue = 0.0;
self.levelSlider.maximumValue = 100.0;
self.levelSlider.continuous = YES;
UIImage *meterImage = [UIImage imageNamed:#"meter_labels.png"];
UIImageView *meterView = [[UIImageView alloc] initWithFrame:CGRectMake(8, 32, 284, 24)];
[meterView setImage:meterImage];
[cell.contentView addSubview:self.levelSlider];
[cell.contentView addSubview:meterView];
[meterImage release];
break;
}
case kSectionRow_LevelHasLevel:
{
cell.textLabel.text = #"Show Level";
self.levelSwitch = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
cell.accessoryView = self.levelSwitch;
break;
}
default:
{
break;
}
}
break;
}
case kSection_RecipientDelete:
{
cell.textLabel.text = #"Delete Recipient";
cell.textLabel.textAlignment = UITextAlignmentCenter;
cell.textLabel.textColor = [UIColor blueColor];
break;
}
default:
{
break;
}
}
}
return cell;
}
The "content shuffling" you see is most likely due to improper handling of cell re-use.
There is no issue specifically with the grouped style. The problem is more likely to manifest itself in that style because fewer cells fit in the screen which requires more scrolling and requires more cell re-use.
You are setting up the cell contents only when creating cells (when cell == nil). When a cell scrolls off the screen, it goes into the re-use queue. The row at the other end that is now visible re-uses the cell view that is in the re-use queue. The re-used cell contains the contents of some other row.
When all the cells are alike (at least in regard to the UI controls and not the data), this isn't a problem. When all or some of the cells are different, you get controls appearing where you don't expect them.
Because you only have a small number of rows and each one is layed out differently, the quick (and perhaps dirty) solution is to use a different re-use identifier for each cell like this:
NSString *CellIdentifier =
[NSString stringWithFormat:#"RecipientEntryCell-%d-%d",
indexPath.section, indexPath.row];
This is not at all recommended if the table view is going to have lots of different cells since every cell for every row in the table will be in memory at the same time (instead of just the few on the screen). Do not use a unique identifier as a way to solve any and all cell re-use problems.
The Table View Programming Guide shows an alternate way to design table views like this where you have a few cells with different layouts. See "The Technique for Static Row Content" on this page.
Ultimately, it's better if you understand that the table view re-uses cells for good reasons and not try to workaround it all the time. Generally, the cellForRowAtIndexPath should look like this:
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCell...
if (cell == nil) {
//Create cell...
//Set UI content that applies to all rows (if any)...
}
else {
//Cell is being re-used.
//Remove UI content that doesn't apply to this row...
}
//Add UI content that applies only to this row...
//Copy values from data source to cell UI controls...
return cell;
If you construct the cells as shown above, do not maintain class-level references to UI controls inside cells (like nameLabel, imageButton, etc). Instead, the control values should be set in cellForRowAtIndexPath from a backing data variable (model) and the value should be read or saved back to the backing data variable as soon as the UI control changes.
For example, instead of storing a reference to a UISlider in a cell, store the current slider value as a float ivar. Initialize the float where appropriate (eg. viewDidLoad). In cellForRowAtIndexPath, create the UISlider, set its value using the float ivar, and have the slider call a method when its value changes. In that method, copy the slider's value to the float ivar.
Hope this helps.
Related
I have a UIViewController that uses a UITableView to display an array. This array gets its data from fetching from Core Data. All works well except having more then 8 entries displayed at once results in rows appearing over other rows when the table view is scrolled. Periodically the text in the rows begins to become slightly fuzzy too. Very weird.
Here is the code of my 'rowForCell" method.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Creates a new cell for every row (as in, every time this method is called).
//UITableViewCell *aCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
//'alloc init' is NOT called here therefore a cell object has to be made inside the table view in the storyboard (which I've done and named cell). This is a more effecient and better way then 'alloc init'. Cells are dequeued & reused.
UITableViewCell *aCell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (aCell == nil)
{
aCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cell"];
}
//Makes sure the separator doesn't have that little gap in the beginning.
//NOTE: This MUST be here and "_tableView.separatorInset = UIEdgeInsetsZero;" must exist inside [self extraTableViewParameter]!
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
aCell.layoutMargins = UIEdgeInsetsZero;
aCell.preservesSuperviewLayoutMargins = NO;
}
//Cell details.
aCell.backgroundColor = [UIColor clearColor];
aCell.textLabel.textColor = [UIColor whiteColor];
//aCell.textLabel.font = [UIFont fontWithName:#"SanFrancisco" size:20];
aCell.detailTextLabel.textColor = [UIColor lightGrayColor];
//aCell.detailTextLabel.font = [UIFont fontWithName:#"SanFrancisco" size:20];
//Fill cell with data.
//NSDictionary *item = [_tableData objectAtIndex:indexPath.row]; //[_fetchArray objectAtIndex:indexPath.row];
//AssetsPartners *items = [_fetchArray objectAtIndex:indexPath.row]; //Using this line of code, instead of the bottom one, will result in _fetchArray NOT being sorted.
AssetsPartners *items = [_fetchedResultsController objectAtIndexPath:indexPath]; //Ask '_fetchedResultsController' what's at this particular indexPath right now. Also alphabetically sorts the array list.
//CUSTOM LABEL instead of standard '.textLabel.text' because 'items.name' is sometimes too long & covers up price: looks messy.
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(65.0, 15.0, 150.0, 30.0)];
label.textColor = [UIColor whiteColor];
label.text = items.name;
[aCell.contentView addSubview:label];
aCell.textLabel.text = #""; //Make sure this is empty!
//aCell.textLabel.text = items.name; //item[#"title"]; //Preiously, was just this with out the UILabel.
NSInteger anInteger = [items.balance integerValue];
aCell.detailTextLabel.text = [NSString stringWithFormat:#"%# P", [self addWhitespacesTo:anInteger]]; //item[#"balance"]];
//Check if it's appropriate to add image to cell or not.
if (items.photo == nil)
{
NSLog(#"No image.");
}
else
{
NSString *iconName = [self cutOutString:#"bizzer://" fromString:items.photo]; //Get rid of "bizzer://" from string value.
//Standard UITableViewCell accessors/parameters. Only used to create a clear image to shift text over.
aCell.imageView.image = [UIImage imageNamed:iconName]; //Inputs appropriate icon into cell image.
[self turnIcon:aCell.imageView toColor:#"clearColor"]; //Changes icons color accordingly.
//Entirely new UIImageView for every UITableViewCell (to tweak the frame size & position).
UIImageView *newImgView = [[UIImageView alloc] initWithFrame:CGRectMake(10.0, 15.0, 35.0, 35.0)];
newImgView.image = [UIImage imageNamed:iconName];
[aCell.contentView addSubview:newImgView];
[self turnIcon:newImgView toColor:items.color];
}
return aCell;
}
Try rearranging your code as follow
if (aCell == nil)
{
aCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cell"];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(65.0, 15.0, 150.0, 30.0)];
label.textColor = [UIColor whiteColor];
label.tag = 999;
[aCell.contentView addSubview:label];
}
UILabel *label = (UILabel *)[aCell.contentView viewWithTag:999];
AssetsPartners *items = [_fetchedResultsController objectAtIndexPath:indexPath];
label.text = items.name;
You are adding a label without considering that the cell may have been reused. You should move that code into your if (aCell == nil) block so that you only do it once per cell. Same for the image view.
I'm developing an application on iOS targeting iOS 7.0 and later.
Now, my question is that how to reload the rows of a UITableView which are visible on screen. I'm using dynamic cells.
Actually while selecting an option I'm changing some colors like title color of each cell but those cells which are already loaded on screen are not changed and if I'm loading whole table then its taking a long time.
Waiting for your kind reply.
Code
//on clicking this button color is updating
- (IBAction)btnDone:(id)sender {
appDel.selectedMood=_moodDetView.str;
appDel.barColor=_moodDetView.backgroundColor;
self.navigationController.navigationBar.barTintColor = _moodDetView.backgroundColor;
//[self.mainTblView reloadData];
[self.menuView setBackgroundColor:appDel.barColor];
[_moodDetView setHidden:YES];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//For menu contents
if (tableView.tag==2) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
cell.textLabel.textColor = [UIColor whiteColor];
cell.textLabel.font = [UIFont fontWithName:#"Futura" size:14.0];
cell.textLabel.textAlignment = NSTextAlignmentCenter;
cell.backgroundColor = [UIColor clearColor];
}
switch (indexPath.row) {
case 0:
{
cell.textLabel.text=#"Select Mood";
}
break;
case 1:
{
cell.textLabel.text=#"Search by Author";
}
break;
case 2:
{
cell.textLabel.text=#"Search by Category";
}
break;
case 3:
{
cell.textLabel.text=#"Favorites";
}
break;
case 4:
{
cell.textLabel.text=#"Feeds";
}
break;
default:
break;
}
return cell;
}
//for main table
else{
// Configure the cell...
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
FeedCustomCell* CELL=( FeedCustomCell *)cell;
CELL.cellImageView.layer.cornerRadius=CELL.cellImageView.frame.size.width/2;
CELL.cellImageView.clipsToBounds=YES;
CELL.gesRec=[[CustomTapGestureRecognizer alloc]initWithTarget:self action:#selector(authorBtnTouched:)];
[CELL.lblAuthName setTextAlignment:NSTextAlignmentJustified];
[CELL.lblAuthName setTextColor:[AppDelegate invertColor:appDel.barColor]];
NSString * str=[NSString stringWithFormat:#"%#",[[dataArray objectAtIndex:indexPath.row] objectForKey:#"authorId"]];
UIImage * img=[UIImage imageNamed:str];
CELL.cellImageView.image=img?img:[UIImage imageNamed:#"n3"];
[CELL.lblAuthName addGestureRecognizer:CELL.gesRec];
CELL.gesRec.authorImage=CELL.cellImageView.image;
NSString * authName=[[dataArray objectAtIndex:indexPath.row] objectForKey:#"authorName"];
[CELL.lblAuthName setText:authName];
[CELL.gesRec setAuthorId:(int)str.integerValue];
[CELL.gesRec setAuthorName:authName];
[CELL.txtViewQuote setTextColor:appDel.barColor];
CELL.txtViewQuote.text=[[dataArray objectAtIndex:indexPath.row] objectForKey:#"quoteTxt"];
CGSize sizeThatShouldFitTheContent = [CELL.txtViewQuote sizeThatFits:CELL.txtViewQuote.frame.size];
CELL.heightConstraintOfTxtView.constant = sizeThatShouldFitTheContent.height;
[CELL.contentView sizeToFit];
return CELL;
}
}
regards:
Syed Meesum Ali
Junior Software Developer
The simplest possible way to do it would be:
[myTable reloadRowsAtIndexPaths:myTable.indexPathsForVisibleRows withRowAnimation:UITableViewRowAnimationAutomatic];
However since you are looking for a more optimized way, The following would be better:
On button click, in the target method, iterate over visible cells, and make relevant changes in each cell.
- (IBAction)btnDone:(id)sender {
for (UITableViewCell* cell in yourTable.visibleCells) {
if ([cell isKindOfClass:[FeedCustomCell class]]) {
[((FeedCustomCell*)cell).lblAuthName setTextColor:[AppDelegate invertColor:appDel.barColor]];
}
}
}
This should be way faster that reloading (drawing) all visible cells.
ALSO, a word of advice. Use different reuseIdentifers for different types of cells, or your table might cause app crashes.
If you implement custom cell subclassing UITableViewCell, you can implement prepareForReuse method. You can reset some properties example, alpha, selection state in that method.
CoreData returns BOOL value and according to the value I draw a UILabel on UITableViewCell accessoryView. The problem is that UILabel repeats itself also on the cells it shouldn't appear at all.
CGRect lblRect = CGRectMake(230, 7, 20, 20);
UILabel *lblEnabled = [[UILabel alloc] initWithFrame:lblRect];
lblEnabled.textColor=[UIColor whiteColor];
lblEnabled.textAlignment=NSTextAlignmentCenter;
[lblEnabled setFont:[UIFont fontWithName:#"Verdana" size:10.0]];
[lblEnabled setText:#"40"];
lblEnabled.backgroundColor= [UIColor colorWithPatternImage:[UIImage imageNamed:#"greenBg"]];
lblEnabled.layer.cornerRadius = 9.0;
lblEnabled.layer.masksToBounds = YES;
lblEnabled.tag = indexPath.row;
cell.accessoryView = lblEnabled;
[cell.contentView addSubview:lblEnabled];
So it appears sometimes on the cell where the BOOL value = NO; Your help is strongly appreciated.
EDIT: I draw these labels in cellForRowForIndexPath.
EDIT: I use storyboards, so I don't check if cell is nil.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=#"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
/*
if(cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
tableView.rowHeight=57.0;
}*/
Coin *coin=[[self frcFromTV:tableView ] objectAtIndexPath:indexPath];
cell.textLabel.text=coin.coinNominal;
if(coin.comSubject.length>0)
{
cell.detailTextLabel.text=[NSString stringWithFormat:#"%#%# (%#) | %#",[self returnCatalogDefinition:coin.catalogIndex],coin.kmRef, coin.dates, coin.comSubject];
}
else
{
cell.detailTextLabel.text=[NSString stringWithFormat:#"%#%# | %#",[self returnCatalogDefinition:coin.catalogIndex],coin.kmRef,coin.dates];
}
if(coin.isCommemorative.boolValue)
{
// implement label
}
if(coin.listed.boolValue)
{
CGRect lblRect = CGRectMake(230, 7, 20, 20);
UILabel *lblEnabled = [[UILabel alloc] initWithFrame:lblRect];
lblEnabled.textColor=[UIColor whiteColor];
lblEnabled.textAlignment=NSTextAlignmentCenter;
[lblEnabled setFont:[UIFont fontWithName:#"Verdana" size:10.0]];
[lblEnabled setText:#"40"];
lblEnabled.backgroundColor= [UIColor colorWithPatternImage:[UIImage imageNamed:#"greenBg"]];
lblEnabled.layer.cornerRadius = 9.0;
lblEnabled.layer.masksToBounds = YES;
lblEnabled.tag = indexPath.row;
cell.accessoryView = lblEnabled;
[cell.contentView addSubview:lblEnabled];
}
return cell;
}
In code below you probably add your label but because those cells are reusable you should handle else statement (hiding label or whatever is appropriate)
if(coin.isCommemorative.boolValue)
{
// implement label
//remove from that part of the statement this line:
//[cell.contentView addSubview:lblEnabled];
} else {
cell.accessoryView = nil;
//hiding or modifying label for other cases
}
if you will not deal with that else statement the change you made in if will be applicable to more than one cell because of the reusing mechanism
As a "side advice" I would recommend you to subclass UITableViewCell and add the property you want (label) to encapsulate that and make only public method for showing or hiding that accessor.
EDIT:
if your flag for a change is not specifying to which cell it has to indicate (for example using indexPath) then the result is as your one.
This is quite global state if(coin.isCommemorative.boolValue) not indicating to which cell it counts try for example (for just learning purpose) add if(coin.isCommemorative.boolValue && indexPath.row%2==0) and see the result.
Are you reusing cells? If so, then you need to remove that cell from contentView inside prepareForReuse method.
I have a strange problem using the dequeueReusableCellWithIdentifier: method of UITableView. Not sure if I don't understand the method well enough or is it just plain weird. Here goes:
Im using a UITableView which presents some data to users, and inside my
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath I use the dequeue method like so:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
Afterwards I add some subviews to the contentView property of the cell. When scrolling a bit further down on my table I see those previously added subviews i.e. the cell is not empty but filled with "old" data. If I don't dequeue, and just alloc-init a new one each time, the cells are empty but I do see a bit more memory consumption which is precisely what Im trying to bring down a little. I'm using ARC if that means anything here.
What or how should I tackle the problem? I have tried running a for loop through the subviews of the content view and [view removeFromSuperview] which does remove the previous views and brings down memory consumption a little. But is that really necessary? Or is there a better way?
EDIT here is some more code how I add subviews
cell.backgroundView = [[UIView alloc] initWithFrame:cell.frame];
cell.backgroundColor = kClearColor; //defined to [UIColor clearColor]
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if (indexPath.row == 0)
{
UIImageView *shine = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
shine.image = [UIImage imageNamed:#"top_shine_1"];
[cell.backgroundView addSubview:shine]; //its a gradient thats why its added to background
UILabel *appLabel = [[UILabel alloc] initWithFrame:CGRectMake(55, winSize.height * 0.027, 250, 33)];
appLabel.backgroundColor = kClearColor; //defined to clear color
appLabel.textColor = kWhiteColor; //defined to white color
appLabel.text = [viewOrder objectAtIndex:tableView.tag]; //just an array from where I get the required text
appLabel.font = kStandardFontOfSize(30); //defined to a specific font
[cell.contentView addSubview:appLabel];
UIButton *settingsButton = [UIButton buttonWithType:UIButtonTypeCustom];
settingsButton.frame = CGRectMake(10, winSize.height * 0.0377, 31, 21);
[settingsButton setImage:[UIImage imageNamed:#"settings_button"] forState:UIControlStateNormal];
[settingsButton addTarget:self action:#selector(settings:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:settingsButton];
return cell; //here I just return it since this is all the config the first cell needs
}
NSString *app = [viewOrder objectAtIndex:tableView.tag];
NSArray *boxes = [[plist secondObjectForKey:#"order" parent:app] componentsSeparatedByString:#";"];
//Add necessary shines or create the last logotype cell - just some details and stuff, all are just images
if (indexPath.row == 1)
{
UIImageView *shine = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 282.5)];
shine.image = [UIImage imageNamed:#"top_shine_2"];
[cell.backgroundView addSubview:shine];
}
else if (indexPath.row == 2)
{
UIImageView *shine = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, winSize.width, 150)];
shine.image = [UIImage imageNamed:#"main_shine"];
[cell.backgroundView addSubview:shine];
}
else if (indexPath.row == boxes.count + 1)
{
UIImageView *logo = [[UIImageView alloc] initWithFrame:CGRectMake(111.5, 25, 97, 20)];
logo.image = [UIImage imageNamed:#"cell_logo"];
[cell.backgroundView addSubview:logo];
return cell;
}
NSString *databox = [boxes objectAtIndex:indexPath.row - 1];
UIView *view; //Main subview to be added to the cell
/*
here I have a class that creates a view with a bunch of subviews added to that view, the view is then assigned to 'view'; kinda like
view = [someAssembler assembleViewWith:options.....]. all are basically UILabels or ImageViews added to the main view
*/
[cell.contentView addSubview:view]; //and here this 'main view' is added as a subview, this view is still visible after the cell has been dequeued and the shines are as well
return cell;
Before you start criticising why im not using a single UIColor for background and text color let me remind you that this is still in testing stage, it will be taken care of later.
[cell.backgroundView addSubview:shine]; these lines of code are the problem in your case.
You should create a complete reusable cell within the if (!cell) block and repopulate them each time cellForRow is being called. For every unique cell a unique reuse identifier should be used. For example, if you have multiple cells with differently laid out subviews, you should use different identifiers for them.
In your specific example cells must be created in the if (indexPath.row == 1) blocks.
static NSString *cellIdentifier = #"cell";
UITableViewCell *cell = nil;
if (indexPath.row == 0) {
cellIdentifier = #"topCell";
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
if (!cell) {
// create the cell and add the necessary subviews for indexPath row 0
}
return cell;
}
else if (indexPath.row == 1) {
}
//etc.
}
You'll have to create the "main subview" for each cell in the !cell block with this approach though, so you should probably look into subclassing a cell.
i'am creating an iOS application which is similar to bbc application
- I have a table view which has two section
- 1st section contains cells containing scrollview wid images
- 2nd section contains expandable cells which contains scrollview did images
so the problem is that
when i use the dequereusable its showing weird behaviors like when the bottommost cell in the table is expanded the first cell in the first gets cleared etc etc
so i have just stopped using the queue and everything started working fine
but now when i added images after scrolling the cells which is not in the view gets refreshed and its
taking a lot of time to load
so could kindly guide me how to use the queue wisely in the code
described below
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier=#"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell== nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"hai"] autorelease];
///[here a different name has been used for the reuse identifier];////
if ([self tableView:tableView inSection2:indexPath.section]) {
Coffee *co =[appDelegate.coffeeArray2 objectAtIndex:indexPath.section-s1Count-1];
cell.textLabel.text=co.coffeeName;
}
if ([self tableView:tableView inSection1:indexPath.section]) {
Coffee *co =[appDelegate.coffeeArray1 objectAtIndex:indexPath.section];
cell.textLabel.text = co.coffeeName;
CGRect cellname = CGRectMake(5, 0, 290, 25);
UILabel *cellabel = [[[UILabel alloc] initWithFrame:cellname] autorelease];
cellabel.backgroundColor = [UIColor whiteColor];
cellabel.font = [UIFont italicSystemFontOfSize:20];
cellabel.textColor=[UIColor blueColor];
cellabel.highlightedTextColor = [UIColor clearColor];
cellabel.text=co.coffeeName;
[cell.contentView addSubview:cellabel];
}
// Configure the cell...
if ([self tableView:tableView canCollapseSection:indexPath.section])
{
if (!indexPath.row)
{
// first row
// only top row showing
if ([expandedSections containsIndex:indexPath.section])
{
cell.accessoryView = [myuicontroller accessoryWithColor:[UIColor grayColor] type:DTCustomColoredAccessoryTypeUp];
}
else
{
cell.accessoryView = [myuicontroller accessoryWithColor:[UIColor grayColor] type:DTCustomColoredAccessoryTypeDown];
}
}
else
{
// all other rows
cell.accessoryView = nil;
cell.accessoryType =UITableViewCellAccessoryDisclosureIndicator;
CGRect cellname = CGRectMake(5, 0, 290, 25);
UILabel *cellabel = [[[UILabel alloc] initWithFrame:cellname] autorelease];
cellabel.backgroundColor = [UIColor whiteColor];
cellabel.font = [UIFont italicSystemFontOfSize:13];
cellabel.textColor=[UIColor blueColor];
cellabel.highlightedTextColor = [UIColor clearColor];
// cellabel.text =[NSString stringWithFormat:#"category"];
[cell.contentView addSubview:cellabel];
myscrollView *svb;
svb=[[myscrollView alloc]initwitharray:appDelegate.newscat1];
}else{
myscrollView *s;
NSLog(#"inside the textlabel ext%#",cell.textLabel.text);
NSLog(#"count of array %d",[appDelegate.newscat1 count]);
NSString *cat=cell.textLabel.text;
[cell.contentView addSubview:s];
}
}
return cell;
}
You need to set the alternative reuse identifier before you dequeue the cell. At the moment you are dequeuing a cell with identifier "cell" regardless of the section you are in, so you will often be returning a section 0 cell for a section 1 part of the table.
So, branch your code so that you do different things depending on the value of indexPath.section:
if (indexPath.section == 0)
cellIdentifier = #"thisCell";
else
cellIdentifier = #"otherCell";
Then dequeue your cell, if it is nil, create with the same cell identifier variable above.
You should only be adding subviews inside your (cell = nil) code - otherwise you will end up with cells with lots of overlapping subviews and will be wasting memory. If a cell has been dequeued, you just configure the existing subviews, you don't make new ones. You can assign tags to your subviews as you add them to assist with this.