The table view I have changes Cell background colors quite often. The colors set in -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath are fine but some colors are set after the table view loads like:
- (void) ChangeCellColorAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentCell inSection:0]];
cell.backgroundView.backgroundColor = [UIColor orangeColor];
}
If a cell is changed with that method it will revert back to what it was originally set to when it scrolls out of view. How can I insure it will stay the same color when I scroll out of view.
You should take into account how the cell loading process works: when you set the color of a visible cell and the user scrolls to a point where that cell is not visible, and then scrolls back to the original point the table view will call again tableView:cellForRowAtIndexPath:, a new cell will be created(better said an already created cell will be reused if you are doing things correctly as I assume) and the background color you previously set will be ignored.
The solution is to know which color each cell should have in tableView:cellForRowAtIndexPath: and set the color there.
Related
I changed the color of text for the cell clicked in the table. But after the cell is clicked, when i come back to table the text of cell has the original color. Could you give me an advice?
This is the code in "didSelectRowAtIndexPath"
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.textLabel.highlightedTextColor = [UIColor blueColor];
Thank you
after the cell is clicked, when i come back to table the text of cell has the original color. Could you give me an advice?
You need to have the color for each cell stored somewhere other than in the table, so that you can reproduce the colors you want anytime the table redraws itself. Typically, you'll have some sort of data structure that stores the table's data, and that's usually the right place to save any changes the user makes. The table view's data source should have a -tableView:cellForRowAtIndexPath: method that sets the color according to what you've saved, along with any other cell attributes.
This is happen because the cells are reused, so lets say when you change text colour property of some cell it will be affected as you expect but when you scroll and that cell disappear off the screen it will be put to reuse pool and if it appears again on the screen table view takes some cell from the reuse pool but it's properties will be different so the colour won't persist.
You should keep somewhere, for example in NSMutableArray, info about which table was clicked.
You can add an index path to the array when you click the cell and in cellForRowAtIndexPath: check is this indexPath in the array and if it is change appropriate property.
The problem is that iOS throws away your cell if you scroll away and recreates it when it's needed (you scroll back to the cell).
If I were you, I would subclass UITableViewCell and overwrite
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
In there you would have
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected: selected animated: animated];
self.textLabel.textColor = selected ? [UIColor blueColor] : [UIColor blackColor];
}
Since iOS UITableView remembers which cell is selected, this should work fine, even when it's recreated.
The reason it's happening is what others are saying: cells are reused.
Storing selection state or color will work, however if you just need to make sure that selected cells have a different color for a label than non-selected cells, there's a way that does not require to use a supporting data structure.
You just need to check if the cell being setup at - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is currently selected or not, and that can be achieved with [tableView indexPathForSelectedRow] if your table uses single selection, or [tableView indexPathsForSelectedRows] if it uses multiple selection.
The last case requires you to find the current indexPath in the returned array, and might be slower than using the supporting array.
But if the selection is simple, then this solution is probably faster, uses less memory and is easier to read (IMO).
So I've made some custom table view cells and they draw correctly and look great, however once I scroll past the edge of the visible cells they start being reused, which is fine, except that when I scroll back the reused cells are still shown and don't redraw. Specifically all the cells look the same except for the top-most cell.
Pictures detailing the occurrence:
How I have this coded up, is when the cells get made if the indexPath.row is greater than 0 add an "overlap effect" which is just a gradient on a uiview placed underneath the custom drawing on the UITableViewCell's contentView.
This is how I add the overlap effect in the UITableViewController's tableView:cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"APostCell";
PostCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
CustomPost *aPost = [self.posts objectAtIndex:indexPath.row];
if (indexPath.row > 0) {
[cell addOverlap];
}
cell.postDateLabel.text = [aPost datePostedAsString];
return cell;
}
How would I implement this [cell removeOverlap]?
Try this
if (indexPath.row == 0) {
//remove overlap here
} else {
[cell addOverlap];
}
beacuse, except 1st cell all have overlap.On scrolling the reused cell have the overlap. So for first cell remove the overlap if present.
So after I posted the question I figured it out and, since I had the question and had previously not found any information on the subject figured I would share.
So whenever
PostCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]
is called, the table view either creates a new cell or reuses an old one. When a new cell is created and it is not the top cell (indexPath.row == 0) it adds the overlap to the UITableViewCell. And if it reuses the cell, that method still gets called, regardless what cell is being reused. So naturally once the cell created at the top is reused, the gradient view still gets added to cell.contentView and it stays there even when I'm reusing for the topmost cell again.
In fact adding the overlap view in this way will stack multiple overlap views into the same cell.
So what has to be done (if you intend to customize the cell appearance this way) is to remove the added views before each reuse of the cell. So you have to overwrite the custom tableviewcell's prepareForReuse method and do just that like so.
- (void) prepareForReuse {
[super prepareForReuse];
[self removeOverlap];
}
Be SURE the cell has the overlap view otherwise your app will break by trying to remove views not there. so have something like
- (void) removeOverlap {
if ([self.contentView.subviews count] > 1) {
//This method works based on the assumption [cell addOverlap] adds new view
//underneath existing views - like [self.contentView insertSubview:overlappedView atIndex:0];
[[self.contentView.subviews objectAtIndex:0] removeFromSuperview];
}
}
I have read many posts about similar problems but nothing seems to work, I am obviously doing something wrong. I have a TableViewController that is in a StoryBoard (XCode 5). For the PrototypeCell I set the type to custom and set the Identifier to "pbvcell". I added some labels, changed the background etc.. Here is my tableview delegate method for setting the cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"pbvcell"];
// Configure the cell...
PBVlead *lead = [self.leads objectAtIndex:indexPath.row];
NSLog(#"Cell class %#", [cell class]);
UILabel *leadNameLabel = (UILabel *)[cell viewWithTag:1];
leadNameLabel.text = lead.leadName;
return cell;
}
Now the app launches but even after I add an object to the tableview datasource array and do a reload data, the cells are blank, like the custom cell is not being used. It looks like this should be easy and thats all I need to do. What on earth am I missing?
I left this in a comment above but just to keep things tidy I will post it as an answer here. It is silly but it is good to note that you have to manipulate the contextView of a prototype cell and not the tableview cell itself in order for your visual changes to have an effect...
"Because I embrace my own stupidity I will tell everyone what was going on here. I had set the TableCell background to blue and added some UILabels and set there color to white to show up against the blue background. Run the app, no labels.... What I finally realized is, I had not set the Content View background to blue. So..... What was happening was the labels actually are shown in the content view in the view hierarchy. White labels on a white background equals, invisible... :-) I set the content view background to blue and wola, there is everything! :-) Brother..?
I have a UITableView with Dynamic Prototypes.
I implemented the code below so that when I select a row, it will be marked and the previously selected row will be unmarked.
However, for example, the row I selected is displayed in the middle of the screen, then when I scroll up or down, another cell (which is in the same middle position of the screen) is marked. In short, every view, there is a selected cell at the middle.
Please advise.
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *oldIndexPath = [self.tableView indexPathForSelectedRow];
[self.tableView cellForRowAtIndexPath:oldIndexPath].accessoryType = UITableViewCellAccessoryNone;
[self.tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
return indexPath;
}
Probably you are overwriting accessoryType in your cellForRowAtIndexPath method - this is called each time table is about to draw new rows which were invisible before (as you described when you scroll up / down).
You need to handle it also in that function and update accessoryType there - otherwise it will randomly reuse a cells with different accessoryTypes.
You are modifying just the visuals of cell, you're not updating the data model. Store the selected index path in a #property somewhere, and adjust accessoryType inside cellForRowAtIndexPath.
I have a full list that populates a UITableView. This I want to background the one of the cells with a different color and it works initially, but for some reason when I start scrolling the table up and down, it starts drawing more cells with the green background.
Please note that there is always one detailCell.detailTimeLabel.text that's equal to currentTime.
The code I have is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
... SOME MORE CODE HERE ...
if ([detailCell.detailTimeLabel.text isEqualToString:currentTime]) {
UIView* backgroundView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
backgroundView.backgroundColor = [UIColor greenColor];
detailCell.backgroundView = backgroundView;
for (UIView* view in detailCell.contentView.subviews)
{
view.backgroundColor = [UIColor clearColor];
}
}
}
Which can be the problem?
Your trying to store data in a tableviewcell. You can't do this because the cells are constantly reused. The background you see repeated occurs because its the same cell being displayed over and over again with different text.
When you dequeue the cell, you need to reset it to blank and wipe out all the previous data. Then you should set the background color only if the data you are putting into the cell has the current time.
As a general rule, you should never refer to data in the cells. If you need to know what is in a particular cell, look at the data at the indexpath within the datamodel itself.