23

I'm using a custom backgroundView and selectedBackgroundView for a UITableViewCell subclass. These cells are in a grouped table, so I'm setting the background and selected background as UIImageViews based on the cell's row in cellForRowAtIndexPath:.

The problem I'm having is that when the cell is selected, its selectedBackgroundView modifies the contents of the contentView. For example, after selecting and/or highlighting a cell, the UILabel in the contentView has its backgroundColor changes and the UIView being used as a cell separator is not visible.

Before selection: Before selection/highlight After selection: After selection/highlight

I don't see this behavior documented anywhere. Is there something I need to do to prevent this? Is there a different approach to showing cell selection/highlighting that I should take to prevent this?

  • Note: Since it's a grouped table view, I set a different backgroundViews and selectedBackgroundViews with UIImageViews to account for the rounded corners on the top and bottom cells in the section in cellForRowAtIndexPath:, but I have the same problem when using the default UITableViewSelectionStyleBlue.

Edit 1:

Per an0's answer, I overrode setHighlighted:animated:. I'm not sure how reliable the implementation is, but this approach worked to maintain the highlighted and backgroundColor properties of the subviews:

NSArray *recursiveAllSubviews = [self recursiveValueForKey:@"subviews"]; // Uses MTRecursiveKVC Cocoapod
NSArray *backgroundColors = [recursiveAllSubviews valueForKey:@"backgroundColor"];
[super setHighlighted:highlighted animated:animated];
if (highlighted) {
    [recursiveAllSubviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger index, BOOL *stop){
        if ([view respondsToSelector:@selector(setHighlighted:)]) {
            [view setValue:[NSNumber numberWithBool:NO] forKey:@"highlighted"];
        }
        id possiblyNull = [backgroundColors objectAtIndex:index];
        if (possiblyNull != [NSNull null]) {
            view.backgroundColor = possiblyNull;
        }
    }];
}
MaxGabriel
  • 7,617
  • 4
  • 35
  • 82
  • Ah, this is probably related to "The content is selected automatically if the subview implements (if appropriate) the accessor methods for the highlighted property." – MaxGabriel Jan 22 '13 at 21:44

4 Answers4

45

UITableViewCell does two things automatically when highlighted/selected:

  1. Set all its subviews' backgroundColor to clear color(transparent).
  2. Highlight all subviews that can be highlighted, for example, UIImageView.

To prevent the first problem, you have to override these two methods in your UITableViewCell subclass:

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
    [super setHighlighted:highlighted animated:animated];
    if (highlighted) {
        // Recover backgroundColor of subviews.
    }
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    if (selected) {
        // Recover backgroundColor of subviews.
    }
}
an0
  • 17,191
  • 12
  • 86
  • 136
  • Awesome. My cell separator was implemented using the `backgroundColor` property so that explains why it disappeared as well. – MaxGabriel Jan 22 '13 at 23:35
  • I could guess that as well because I did similar things:) – an0 Jan 22 '13 at 23:36
  • If I want to stop the behavior of 1. and 2., am I best off overriding the `setHighlighted:animated:` and `setSelected:animated:` methods, not calling super, and implementing the methods myself? Unless there's a good way to do the 'recover backgroundColor` step that you alluded to. – MaxGabriel Jan 23 '13 at 01:50
  • Ok, I came up with a solution and added the code to my question. – MaxGabriel Jan 23 '13 at 02:19
  • Clear and concise. Good answer. – Tom Redman Oct 30 '13 at 18:44
  • I came here looking for a solution to a different but related problem, but your answer and @MaxGabriel's comment clarified that my *real* problem was the view background's being made transparent. Thanks! – sumizome Apr 24 '14 at 22:31
  • UIButton is not highlighted when cell is selected. I think subviews other than UIControl are highlighted, not all of them. – Ge Liu Apr 07 '15 at 09:22
  • It works, but it still missing during pop back to tableViewController. Any idea? – JZAU May 24 '16 at 01:27
5

set cell.selectionStyle = UITableViewCellSelectionStyleNone and override

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
  if(highlighted) {
    self.contentView.backgroundColor = colorYouWantWhenHighlighted;
  } else {
    self.contentView.backgroundColor = colorYouWantWhenUnhighlighted;
  }
}
Duck
  • 34,902
  • 47
  • 248
  • 470
nyus2006
  • 411
  • 6
  • 12
  • I did something similar to this, but instead of overriding a method (would required a new class, etc), I just changed the cell background manually at tableView:didSelectRowAtIndexPath:. – Krynble Jan 09 '14 at 16:15
  • I assume when you tap on the cell, the app takes you to the next screen right away. I doubt your method works for highlight state(for example, put your finger down on the cell and hold and then drag outside the cell, the cell should have no highlight). Can you verify it? – nyus2006 Jan 10 '14 at 16:32
  • you were almost there. The correct method is not `setHighlighted` but `setSelected`. – Duck Jun 09 '20 at 22:15
2

In my case, I had two buttons in my UITableViewCell whose background colors were being cleared. These buttons were of type GrayButton, a custom class I wrote that derives UIButton. Rather than override UITableViewCell methods, I instead overrode GrayButton's setBackgroundColor: method:

- (void)setBackgroundColor:(UIColor *)backgroundColor
{
    if (backgroundColor != [UIColor clearColor]) {
        [super setBackgroundColor:backgroundColor];
    }
}
NathanAldenSr
  • 7,841
  • 4
  • 40
  • 51
0

In my case I switched to the contentView and it works much better

UIColor *backgroundColor = selected ? [UIColor redColor] : [UIColor clearColor];
self.contentView.backgroundColor = backgroundColor;

instead of replacing the selectedBackgroundView

UIView *selectionColor = [[UIView alloc] init];
selectionColor.backgroundColor = selected ? [UIColor redColor] : [UIColor clearColor];;
self.selectedBackgroundView = selectionColor;
[selectionColor release];

now the issue is only visible if the cell you tap and start scroll from is not the selected cell.

Duck
  • 34,902
  • 47
  • 248
  • 470
Dani.Rangelov
  • 366
  • 2
  • 5