My UITableView is pulling data from an array. Items in this array have a property called IsSelected. I am trying to put a UIImageView in the cell contentView for each item that is selected.
The UITableView however when reusing the cell is causing my image to be reused on cells that it shouldn't. I cannot figure out for the life of me how I should be doing it differently. I have attached a screen shot showing the issue. If I keep scrolling up and down the image goes all over the place:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SchoolCellIdentifer = #"SchoolCellIdentifier";
SchoolInfoItem *item = [self.schoolsArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SchoolCellIdentifer];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SchoolCellIdentifer];
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
cell.textLabel.text = item.Name;
if ([item.selected isEqualToString:#"1"])
{
cell.contentView.backgroundColor = [BVColors WebBlue];
UIImageView *selectedItemCheckMarkIcon = [[UIImageView alloc] initWithFrame:CGRectMake(300, 13, 17, 17.5)];
[selectedItemCheckMarkIcon setImage:[UIImage imageNamed:#"check-mark.png"]];
[cell.contentView addSubview:selectedItemCheckMarkIcon];
}
else
{
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
return cell;
}
You need to make sure the UIImageView is getting removed from the cell content view. It looks like in your code when a cell gets dequeued that imageview is still in the cells view hierarchy.
Best solution is to have your cell hold onto a reference to the image view and remove it when necessary.
Take the following:
if ([item.selected isEqualToString:#"1"])
{
cell.contentView.backgroundColor = [BVColors WebBlue];
cell.myImageView = [[UIImageView alloc] initWithFrame:CGRectMake(300, 13, 17, 17.5)];
[selectedItemCheckMarkIcon setImage:[UIImage imageNamed:#"check-mark.png"]];
[cell.contentView addSubview:cell.myImageView];
}
else
{
[cell.myImageView removeFromSuperview];
cell.myImageView = nil;
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
Note in the else case the removal of the imageview.
You keep adding a the UIImageView as a subview on the cell's contentView. This isn't removed when the table view is reusing the cell. You'll need to remove the subview if it shouldn't appear.
You should make selectedItemCheckMarkIcon a property on your UITableViewCell subclass. And then have a method in your subclass where you set the image or visibility of the image accordingly.
You could also use the accessoryView property on the UITableView and set the imageView as that:
if ([item.selected isEqualToString:#"1"]) {
cell.contentView.backgroundColor = [BVColors WebBlue];
UIImageView *selectedItemCheckMarkIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"check-mark.png"]];
cell.accessoryView = selectedItemCheckMarkIcon;
} else {
cell.accessoryView = nil;
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
Note that you don't need to set a frame in this case because the system will automatically set the frame correctly for the accessoryView.
Related
Hi i am a newbie to iOS.
I have implemented a tableview in ViewController1.In the tableview i am displaying tittle,detailText and disclosure indicator for the cell.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:MyIdentifier];
}
cell.textLabel.text =[listTableArray objectAtIndex:indexPath.row];
cell.textLabel.textColor=[UIColor grayColor];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.detailTextLabel.text=#"4mile";
return cell;
}
Everything works fine,Now i need a image before the disclosure indicator when i do it with
UIImageView *accessoryView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
accessoryView.image=[UIImage imageNamed:#"list.png"];
cell.accessoryView = accessoryView;
Disclosure indicator is replaced by the image.But i need both image along with disclosure indicator without using Custom cell.
How can i do it...? Help me out.
Thanks
similiar to the due date but at the place of time i need image
UITableViewCell have a fixed layout, depending on the style you use when you initialize it. You cannot change that layout, since the cell lays out its subviews as it prefers to have them.
add the - (void)layoutSubviews to your cell subclass like this:
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = CGRectMake(0.0f , 0.0f, 20.0f, 20.0f);
}
else try this
UIImageView *imageView = [[UIImageView alloc] init];
imageView.image = [UIImage imageNamed:#"list.png"];;
[imageView setFrame:CGRectMake(50, 5, 20, 20)]; // customize the frame
[cell.contentView addSubview:imageView];
If you wanna add UIImageView to standard UITableViewCell you need add your imageView to contentView of cell and add all subviews like a textLabel, detailTextLabel and accessoryView
UIImageView *accessoryImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
accessoryImageView.image=[UIImage imageNamed:#"list.png"];
[cell.contentView addSubview:accessoryImageView];
or subclass of UITableViewCell and implement all views inside it
I have a strange problem with the UITableView. I add a checkmark image to all the cells that does not have the date property set to the current day. When the tableview is populated it seems to be working fine since the first elements, that has no date, does not show the checkmark. When I scroll down to the first cell with a correct date, the checkmark is showed. When I scroll back up, the checkmark is showed in all the cells. Why does this happen?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"InventoryingSearchResultCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
cell.backgroundColor = [UIColor clearColor];
UIView *selectionColor = [[UIView alloc] init];
selectionColor.backgroundColor = myAppDelegate.config.appTheme;
cell.selectedBackgroundView = selectionColor;
// create accessory view
UIImage *image = [UIImage imageNamed:#"u12_normal.png"];
cell.accessoryView = [[UIImageView alloc] initWithImage:image];
}
// fill in data
NSDictionary *item = [searchResult objectAtIndex:indexPath.row];
NSString *inventoryDate;
inventoryDate = [item valueForKey:#"InventoryDate"];
if (![inventoryDate isEqual:[NSNull null]]){
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
NSDate *date = [dateFormat dateFromString:inventoryDate];
if ([self hasBeenInventoried:date]) {
UIImage *image1 = [UIImage imageNamed:#"tick.png"];
UIImageView *view1 = [[UIImageView alloc] initWithFrame:CGRectMake(16, 40, 16, 16)];
view1.image = image1;
[cell.accessoryView addSubview:view1];
}
} else {
inventoryDate = #"";
}
return cell;
}
Yes it is for reusing cell property of UITableView.
so remove this line from your code:
// create accessory view
UIImage *image = [UIImage imageNamed:#"u12_normal.png"];
cell.accessoryView = [[UIImageView alloc] initWithImage:image];
place it in the else part where you change this image
you can also see the link
Check and uncheck the same UITableViewCell
It's happening because you're reusing cells. In the event that the checkmark needs to be shown, you're adding it to your cell. However, when it is NOT required, you're not setting the image to blank/nil.
When a cell is reused that already has this image on it, you never tell it to remove the checkmark.
I would recommend adding your view1 accessory subview when your cell is nil so that the subview always exists. Then, if your inventory requires the checkmark, set the image. If it does not require the checkmark, set the image to nil.
Side note: in general, only add subviews to a reusable cell when the cell is nil. If you add subviews to existing cells, there's a good chance that you'll end up having multiples of that subview on your cell.
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 have a TableView with custom table cells. I add programmatically borders at the bottom of each cell to keep the screendesign layout. Everything is fine, when the App loads the first time. But after scrolling (and scrolling back to the top) multiple border lines are displayed all over the screen.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *CellIdentifier = #"CellProgramm";
ProgrammTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
...
if([object.cellMessageArray[1] isEqualToString:#"wrapper"] || [object.cellMessageArray[1] isEqualToString:#"keynote"] || [object.cellMessageArray[1] isEqualToString:#"break"]) {
UIImageView *lineSeparator = [[UIImageView alloc] initWithFrame:CGRectMake(0, cell.bounds.size.height, 1024, 5)];
lineSeparator.image = [UIImage imageNamed:[NSString stringWithFormat:#"blind.png" ]];
lineSeparator.backgroundColor = [UIColor whiteColor];
[cell.contentView addSubview:lineSeparator];
}
else if([object.cellMessageArray[1] isEqualToString:#"standard"]) {
UIImageView *lineSeparator = [[UIImageView alloc] initWithFrame:CGRectMake(60, cell.bounds.size.height+4, 1024, 1)];
lineSeparator.image = [UIImage imageNamed:[NSString stringWithFormat:#"blind.png" ]];
lineSeparator.backgroundColor = [UIColor pxColorWithHexValue:#"eeeeee"];
[cell.contentView addSubview:lineSeparator];
}
}
Has anyone an idea?
When you scroll a tableview, the cells are reused (dequeueReusableCellWithIdentifier) to optimize performance. In the code above, a lineSeparator image view is added to the cell each time the cellForRowAtIndexPath method is invoked. If the cell is used 5 times, it will have 5 image views added.
One way address this is to remove the lineSeparator image view from the cell before it is reused. This is typically done in the cell's prepareForReuse method.
In the cellForRowAtIndexPath, add a tag to the lineSeparator image view (e.g., lineSeparator.tag = 100;
In your cell's class, implement the prepareForReuse method. E.g.:
-(void)prepareForReuse{
UIView *lineSeparatorView = [self.contentView viewWithTag:100];
[lineSeparatorView removeFromSuperview];
}
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.