13

I've added a text shadow to cells in my UITableView to give them an etched look:

cell.textLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.000];
cell.textLabel.shadowColor = [UIColor whiteColor];
cell.textLabel.shadowOffset = CGSizeMake(0, 1);

Since the shadow color is actually white, when a row gets selected and becomes blue, the white shadow becomes really visible and makes the text look ugly.

Does anyone know how I can remove the shadow before the default cell selection style gets applied?

I have tried:

  1. Using -tableView:willSelectRowAtIndexPath: to unset the shadow with cell.textLabel.shadowColor = nil but this doesn't work in time - the shadow gets unset only after the blue select style is applied.
  2. Checking cell.selected in tableView:cellForRowAtIndexPath: before setting the shadow but this obviously doesn't work since the cell is not redrawn after a selection.

I also tried overriding the -tableView:willDisplayCell:forRowAtIndexPath: delegate method as Kevin suggested below. From logging statements I put in, this delegate method is only called just before a cell is drawn - by the time a cell is touched, it is already too late. This is the code I used

(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
  NSLog(@"in willDisplayCell");
  if (cell.highlighted || cell.selected) {
    NSLog(@"drawing highlighed or selected cell");
    cell.textLabel.shadowColor = nil;
  } else {
    cell.textLabel.shadowColor = [UIColor whiteColor];
  }
}
Chu Yeow
  • 977
  • 1
  • 9
  • 16

4 Answers4

34

One way which should work is to extend UITableViewCell and override the setSelected AND setHighlighted methods, setting the drop shadow state accordingly. This will make sure it's painted at the same time as the background highlight update.

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
    [super setHighlighted:highlighted animated:animated];
    [self applyLabelDropShadow:!highlighted];
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];
    [self applyLabelDropShadow:!selected];
}

- (void)applyLabelDropShadow:(BOOL)applyDropShadow
{
    self.textLabel.shadowColor = applyDropShadow ? [UIColor whiteColor] : nil;
}
ragamufin
  • 4,113
  • 30
  • 32
Mike
  • 1,648
  • 1
  • 16
  • 15
  • Actually, you may just need to override setHighlighted:animated and not touch setSelected:animated at all. I'd try that first. – Mike Jul 31 '09 at 17:06
  • 3
    I can confirm that you need to override both. The cell is Highlighted when you touch it, then Selected when you lift your finger up. If you only override setHighlighted:, the shadow will reappear when the touch ends. Even if you transition to another view after selection, it's noticeable. – benzado Dec 01 '09 at 20:24
3

Use -tableView:willDisplayCell:forRowAtIndexPath:. That's the last thing that's called right before the cell is actually displayed, so you can query its selected property and set the text shadow accordingly.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 2
    Thanks Kevin. I read the documentation on this once you pointed `-tableView:willDisplayCell:forRowAtIndexPath:` out, and it sounds like it should do the trick, but it doesn't. I think `-tableView:willDisplayCell:forRowAtIndexPath:` is called when the cell is 1st drawn, and at the time when a cell is touched, it has already been drawn and so that delegate method isn't called. I'll update my question with the code I tried. Thanks again! – Chu Yeow Jul 27 '09 at 11:08
  • 1
    Ok, I think the correct solution then is to create your own UITableViewCell subclass and override `-setSelected:animated:` there to adjust the shadow as needed. – Lily Ballard Jul 30 '09 at 07:45
3

You should override tableView:willDisplayCell:forRowAtIndexPath: and you need to set the backgroundColor to [UIColor clearColor], also, you should only act on the highlighted state, the selected state has a slightly different meaning

bshirley
  • 8,217
  • 1
  • 37
  • 43
2

I think this is better:

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{ 
    [super setHighlighted:highlighted animated:animated];
    [self applyLabelDropShadow:!self.highlighted];
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{   
    [super setSelected:selected animated:animated];
    [self applyLabelDropShadow:!self.selected];
}

There will be no shadow during changes between states.

Michal
  • 43
  • 4