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];
}
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 got a table view with two sections, no crazy code, just my delegate methods.
It works pretty fine, like i want it to work. It should just look like on this screenshot:
Now the problem is: Sometimes while scrolling or flicking the scoll view to the bounds, this happens (if you can't see it: There is 1 or 1/2 pixel in gray on the top of the second section header, what is not intended to be so):
So, is this a iOS 7.1 or 7.x bug? I'm not using a custom view for the header. Does anyone know how to fix this?
Feedback really is appreciated.
I had this same problem that I battled for a few weeks, and the way I solved it was to set the tableView's separatorStyle to UITableViewCellSeparatorStyleNone, and add a custom subview that is a line to the cell's contentView.
Then in your cellForRowAtIndexPath method, hide the line subview of the last cell in the section:
- (UIView *)lineView
{
// Your frame will vary.
UIView *colorLineView = [[UIView alloc]initWithFrame:CGRectMake(82, 67.5, 238, 0.5)];
colorLineView.backgroundColor = [UIColor blackColor];
return colorLineView;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
static NSString *identifier = #"cellIdentifier";
UIView *lineView = [self lineView];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
[cell.contentView addSubview:lineView];
}
if (indexPath.section == 0)
{
if (indexPath.row == keys.count -1)
{
lineView.hidden = YES;
}
}
return cell;
}
It may be recycling one of the cell views with the separator from the scroll. This is a long shot, but what if you were to try tweaking the footer view for the section by returning an empty view?
-(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
return [[UIView alloc] init];
}
It's also a good trick for removing empty cells from the table when you have only a couple rows.
I tried it with multiple different things and the cleanest approach i found is this.
I created a custom view for the header, but wanted it to look the same as the original not modified header:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 34)];
[headerView setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, tableView.frame.size.width, 34)];
[label setFont:[UIFont boldSystemFontOfSize:14]];
if (section == 0) {
NSMutableArray *difficultyArray = [dictionary objectForKey:#"Difficulty"];
NSString *difficulty = [difficultyArray objectAtIndex:0];
[label setText:[NSString stringWithFormat:#"Time Challenge (%#)", difficulty]];
} else {
[label setText:#"Freeplay (5x5 board)"];
}
[headerView addSubview:label];
return headerView;
}
Now we got the sections as they would appear without custom header views, but the bug still exists. I made it simple and clean:
UIView *lineFix = [[UIView alloc] initWithFrame:CGRectMake(0, 77.5, self.tableView.frame.size.width, 0.5)];
lineFix.backgroundColor = [UIColor groupTableViewBackgroundColor];
[self.tableView addSubview:lineFix];
Now we set a view over the buggy seperator with a height of 0.5 pixel, the seperator isn't visible anymore. Between the two section headers now is a 0.5 height view what shouldn't be there, but since i set it the same color as the section background color it isn't noticeable. The view moves, because it is a subview of the tableview, the same direction like the tableview.
If you have questions, just add a comment.
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.
-Hi, I have a problem with a tableview reloading data and what I want to do is that you put the selected cell in blue and change an image that is, the problem is that if I make the [self.mytableview reloadData] in the didselectedRow blue background disappears and if I do the image of the cell does not change, I'm a bit lost with this piece of code I give thanks
if (indexPath.row == _selectedRow) {
UIImageView *favView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"favIconSelected.png"]];
CGRect frame = favView.frame;
frame.origin.x = 294;
frame.origin.y = 7;
favView.frame = frame;
[cell.contentView addSubview:favView];
[favView release];
}
cell.selectedBackgroundView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:SELCETED_BGIMGCELL]]autorelease];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Product *p = [_productList objectAtIndex:indexPath.row];
_selectedRow=indexPath.row;
[_delegate productWasSelected:p];
[self.myTableView reloadData];
}
You don't need to call reloadData at all. All you need to do is to update the cell that was selected.
In the didSelectRowAtIndexPath method you can get the cell directly by calling cellForRowAtIndexPath on the tableView. This will return the actual cell that's on display and you can directly set the image it's displaying.
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.imageView.image = ...;
Alternatively you could control the image display from your cell subclass if you provide it with both the normal and selected images when you configure each instance.
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.