47

I have a menu like so:

Menu

The normal (unselected) state for each cell is an image, the selected state is also an image (that looks like the default blue one). However, I'd like to add an additional third image, so that when the user selects a cell, it briefly changes to that third color before going blue (selected).

This is my code:

(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView setBackgroundColor:[UIColor clearColor]];

    NSString *cellIdentifier = @"MenuItemCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle  reuseIdentifier:cellIdentifier];
    }

    UIImage *cellBackgroundNormal = [UIImage imageNamed:@"cell_menu_normal"];
    UIImage *cellBackgroundSelected = [UIImage imageNamed:@"cell_menu_selected"];
    UIImageView *cellBackgroundView = [[UIImageView alloc] initWithImage:cellBackgroundNormal];
    UIImageView *cellBackgroundSelectedView = [[UIImageView alloc] initWithImage:cellBackgroundSelected];
    cell.backgroundView = cellBackgroundView;
    cell.selectedBackgroundView = cellBackgroundSelectedView;

    [cell.textLabel setBackgroundColor:[UIColor clearColor]];
    [cell.textLabel setTextColor:[UIColor whiteColor]];
    [cell.textLabel setFont:[UIFont boldSystemFontOfSize:17.0]];
    cell.textLabel.text = [self.menuItems objectAtIndex:indexPath.row];

    return cell;
} 

As you can see, I only have two states as of now. I don't see how I can introduce some sort of cell.hoveredBackgroundView for the third image. If anyone can help me implement this, I'd really appreciate it.

swiftcode
  • 3,039
  • 9
  • 39
  • 64

15 Answers15

93

iOS 6.0 and later

- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}

- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
  // Add your Colour.
    CustomCell *cell = (CustomCell *)[tableView cellForRowAtIndexPath:indexPath];
    [self setCellColor:[UIColor whiteColor] ForCell:cell];  //highlight colour
}

- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath {
  // Reset Colour.
    CustomCell *cell = (CustomCell *)[tableView cellForRowAtIndexPath:indexPath];
    [self setCellColor:[UIColor colorWithWhite:0.961 alpha:1.000] ForCell:cell]; //normal color

}

- (void)setCellColor:(UIColor *)color ForCell:(UITableViewCell *)cell {
    cell.contentView.backgroundColor = color;
    cell.backgroundColor = color;
}

Custom UITableViewCell

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    UIView * selectedBackgroundView = [[UIView alloc] init];
    [selectedBackgroundView setBackgroundColor:[UIColor colorFromHexString:@"5E6073"]]; // set color here
    [self setSelectedBackgroundView:selectedBackgroundView];
}
Ayush
  • 3,989
  • 1
  • 26
  • 34
  • 1
    Thanks, that's it! I also had to implement `didUnhighlightRowAtIndexPath` to handle the image changes when a row gets deselected. – swiftcode Apr 28 '13 at 19:29
  • Could you improve the code to show how to change the color of the cell please ? :) – Lucien May 21 '13 at 14:34
  • @Lucien I have Updated the Code. Check it out – Ayush May 21 '13 at 15:18
  • 1
    I tried a bunch of other solutions, this is the only one that worked. Nice! – CWitty Mar 12 '14 at 18:13
  • 4
    I have to disagree with this solution. You don't create the selectedBackgroundView as part of the setSelected method. You do that when creating the custom UITableViewCell itself. – Rickster Feb 18 '15 at 00:50
  • @Rickster the reason he did it this way, is probably because what you propose doesn't work just like that - the cells will be highlighted only the first time. For some reason, when you set the selectedBackgroundView to the same UIView as before nothing happens. This can be fixed by setting the selectedBackgroundView to nil first. – Radu Simionescu Jun 11 '15 at 20:49
  • This might work but it seems this creates at least two UIView's for one selection. Rather this should be done just once in awake from nib or initWithFrame. – BangOperator Jun 29 '16 at 10:11
32

iOS 8.0 (and later) using Swift

Swift 2

override func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    return true
}

override func tableView(tableView: UITableView, didHighlightRowAtIndexPath indexPath: NSIndexPath) {
    var cell = tableView.cellForRowAtIndexPath(indexPath)
    cell?.contentView.backgroundColor = UIColor.orangeColor()
    cell?.backgroundColor = UIColor.orangeColor()
}

override func tableView(tableView: UITableView, didUnhighlightRowAtIndexPath indexPath: NSIndexPath) {
    var cell = tableView.cellForRowAtIndexPath(indexPath)
    cell?.contentView.backgroundColor = UIColor.blackColor()
    cell?.backgroundColor = UIColor.blackColor()
}

Swift 3

override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
    return true
}

override func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)
    cell?.contentView.backgroundColor = UIColor.orange
    cell?.backgroundColor = UIColor.orange
}

override func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)
    cell?.contentView.backgroundColor = UIColor.black
    cell?.backgroundColor = UIColor.black
}

enter image description here

petesalt
  • 356
  • 3
  • 20
AG1
  • 6,648
  • 8
  • 40
  • 57
32

You can also use UIAppearance like this:

UIView *selectionView = [UIView new];
selectionView.backgroundColor = [UIColor redColor];
[[UITableViewCell appearance] setSelectedBackgroundView:selectionView];

This will apply to all instances of UITableViewCell or any of its subclasses you might have. Just make sure the selectionStyle property of your cell is not set to UITableViewCellSelectionStyleNone.

Milan Cermak
  • 7,476
  • 3
  • 44
  • 59
  • 10
    Thank you! This is, **hands down**, the most elegant answer. The accepted answer looks like code from yesteryear. **Here is the same code in Swift:** http://d.pr/n/14HUv – Clifton Labrum Jun 23 '15 at 20:17
  • 2
    However with this solution, multiple selection acts very strangely. (only one row can be highlighted) – Gannicus Sep 08 '15 at 19:50
  • 2
    This breaks the deselect animation of the cells when popping back to a previous table view controller in the navigation: Deselection becomes immediate, and it's impossible to tell which row was selected. – Nicolas Miari Feb 03 '17 at 03:15
14

Easier than accepted answer:

In your UITableViewCell subclass:

In awakeFromNib or init:

self.selectionStyle = UITableViewCellSelectionStyleNone;

Then:

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
    [super setHighlighted:highlighted animated:animated];

    if (highlighted) {
        self.backgroundColor = [UIColor yourHighlightColor];
    }
    else {
        self.backgroundColor = [UIColor yourNormalColor];
    }
}
marcshilling
  • 1,410
  • 1
  • 12
  • 19
9

Swift:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
}

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    let selectionColor = UIView() as UIView
    selectionColor.layer.borderWidth = 1
    selectionColor.layer.borderColor = UIColor.blueColor().CGColor
    selectionColor.backgroundColor = UIColor.blueColor()
    cell.selectedBackgroundView = selectionColor
}

Swift 4:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
    tableView.deselectRow(at: indexPath, animated: true)
}

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
{
    let selectionColor = UIView() as UIView
    selectionColor.layer.borderWidth = 1
    selectionColor.layer.borderColor = UIColor.blue.cgColor
    selectionColor.backgroundColor = UIColor.blue
    cell.selectedBackgroundView = selectionColor
}
talves
  • 13,993
  • 5
  • 40
  • 63
LondonGuy
  • 10,778
  • 11
  • 79
  • 151
8

Try this in custom cell-

- (void)awakeFromNib
{
    UIView *selectedBackgroundView = [[UIView alloc] initWithFrame:self.bounds];
    selectedBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    selectedBackgroundView.backgroundColor = [UIColor colorWithRed:246.0/255.0 green:95.0/255.0 blue:22.0/255.0 alpha:1.0];
    self.selectedBackgroundView = selectedBackgroundView;
}
Zoeb S
  • 695
  • 1
  • 7
  • 21
3

Based on Milan Cermak's answer, you can use UIAppearance.

In Swift 1.1 / 2.0:

let selectionView = UIView()
selectionView.backgroundColor = UIColor.redColor()
UITableViewCell.appearance().selectedBackgroundView = selectionView
Amr Hossam
  • 2,313
  • 1
  • 22
  • 23
2

Using Objective C, Change Selected Cell Background color other than defaults. No need to create custom cells.

If you only want to change the selected color of the cell, you can do this, Be aware that for this to work, in the Storyboard (or XIB file) you must select a Selected Background colour other than None. Just add following code in UITableView Delegate method : tableView cellForRowAtIndexPath:

UIView *bgColor = [[UIView alloc] init];

bgColor.backgroundColor = [UIColor yellowColor];

[cell setSelectedBackgroundView:bgColor];
ViJay Avhad
  • 2,684
  • 22
  • 26
1

Custom UITableViewCell Swift 3.0

override func awakeFromNib() {
        super.awakeFromNib()

        let selectedBackgroundView = UIView();
        selectedBackgroundView.backgroundColor = UIColor.lightGray;
        self.selectedBackgroundView = selectedBackgroundView;
    }
Kiran Patel
  • 944
  • 10
  • 16
0

Add following code to cellForRowAtIndexPath method

var cell=tableView.dequeueReusableCellWithIdentifier("cell")!
var viewBG=UIView(frame: CGRectMake(0,0,self.view.frame.size.width,50))
viewBG.backgroundColor=UIColor(colorLiteralRed: 71.0/255.0, green: 121.0/255.0, blue: 172.0/255.0, alpha: 1)
cell.selectedBackgroundView=viewBG
Amit Jagesha シ
  • 1,092
  • 12
  • 21
0

For swift 3

self.tableView.reloadData()
let selectedCell = tableView.cellForRow(at: indexPath)
selectedCell?.contentView.backgroundColor = UIColor.red
0

I end up with following code.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

  //  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[cellIdArray objectAtIndex:indexPath.row] forIndexPath:indexPath];

    // Configure the cell...
    cell.backgroundView =
    [[UIImageView alloc] init] ;
    cell.selectedBackgroundView =[[UIImageView alloc] init];

    UIImage *rowBackground;
    UIImage *selectionBackground;


    rowBackground = [UIImage imageNamed:@"cellBackgroundDarkGrey.png"];
    selectionBackground = [UIImage imageNamed:@"selectedMenu.png"];

    ((UIImageView *)cell.backgroundView).image = rowBackground;
    ((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;



    return cell;
}
Avijit Nagare
  • 8,482
  • 7
  • 39
  • 68
0

In Swift 5:

Under your custom cell, implement this method. highlighted means you press it but not lift your finger yet. You can compare with Apple music library page list, which is the same behavior.

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        
        if selected {
            cellLabel.textColor = .white
            cellImageView.tintColor = .white
        } else {
            cellLabel.textColor = .black
            cellImageView.tintColor = .systemPink
        }
    }

    override func setHighlighted(_ highlighted: Bool, animated: Bool) {
        super.setHighlighted(highlighted, animated: animated)
        
        if highlighted {
            cellLabel.textColor = .white
            cellImageView.tintColor = .white
        } else {
            cellLabel.textColor = .black
            cellImageView.tintColor = .systemPink
        }
    }
Zhou Haibo
  • 1,681
  • 1
  • 12
  • 32
-1

You could create a custom UITableViewCell, in which you add a UIButton with the size of the cell. Then you can easily make custom methods for the UIButton's TouchDown (hover) and TouchUpInside events, and set the background.

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        cellButton = [[UIButton alloc] initWithFrame:self.contentView.frame];
        UIImage *cellBackgroundNormal = [UIImage imageNamed:@"cell_menu_normal"];
        UIImageView *cellBackgroundView = [[UIImageView alloc] initWithImage:cellBackgroundNormal];
        self.backgroundView = cellBackgroundView;

        [cellButton addTarget:self action:@selector(hoverCell) forControlEvents:UIControlEventTouchDown];
        [cellButton addTarget:self action:@selector(tapCell) forControlEvents:UIControlEventTouchUpInside];
    }
    return self;
}

- (void)hoverCell
{
    UIImage *cellBackgroundHover = [UIImage imageNamed:@"cell_menu_third_image"];
    UIImageView *cellBackgroundHoverView = [[UIImageView alloc] initWithImage:cellBackgroundHover];
    self.backgroundView = cellBackgroundHoverView;
}

- (void)tapCell
{
    UIImage *cellBackgroundSelected = [UIImage imageNamed:@"cell_menu_selected"];
    UIImageView *cellBackgroundSelectedView = [[UIImageView alloc] initWithImage:cellBackgroundSelected];
    self.backgroundView = cellBackgroundSelectedView;
}
-4

You could introduce a timer to set the time you want (like 1/2 s if I understood) between the 2 different background colors ? But you may already thought about that :/

Lucien
  • 451
  • 4
  • 16
  • I don't think that would work, because I want it to state in that intermediate state for as long as the user holds his finger down on the selected menu item and only change it to blue on release. It's just like a button, for example, that have their own highlighted state. – swiftcode Apr 28 '13 at 17:55
  • 1
    These seems pretty hacky and not entirely reliable – Giles Van Gruisen Jul 31 '14 at 16:42