20

I am trying to change the appearance of a custom selected TableViewCell using Swift.

Do I need to do it via the designer or programmatically?

I tried the following:

enter image description here

And here is my code:

@IBOutlet var tableView: UITableView!


    var tableData: [String] = ["One", "Two", "Three", "Four"]

    override func viewDidLoad() {
        super.viewDidLoad()


        // Register custom cell
        var nib = UINib(nibName: "vwTblCell", bundle: nil)
        tableView.registerNib(nib, forCellReuseIdentifier: "cell")

    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.tableData.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell     {

        var cell:TblCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as TblCell

        cell.lblCarName.text = tableData[indexPath.row]
        cell.imgCarName.image = UIImage(named: tableData[indexPath.row])

        return cell

    }

    func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
        println("Row \(indexPath.row) selected")
    }

    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 70
    }
Hamed Ghadirian
  • 6,159
  • 7
  • 48
  • 67
Peasant coder
  • 213
  • 1
  • 2
  • 4

12 Answers12

26

I have a likeness problem. In your cellForRowAtIndexPath method set:

cell.selectionStyle = .None

and then set didHighlightRowAtIndexPath...

func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
    let cell  = tableView.cellForRow(at: indexPath)
    cell!.contentView.backgroundColor = .green
}

func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) {
    let cell  = tableView.cellForRow(at: indexPath)
    cell!.contentView.backgroundColor = .clear
}
Eray Alparslan
  • 806
  • 7
  • 12
Cao Yong
  • 598
  • 6
  • 7
  • 1
    it works, but only the first time you click the cell :( – joe Jun 04 '16 at 04:57
  • @joe You want selection not highlighting. This is probably what you're after: http://stackoverflow.com/questions/26895370/swift-uitableviewcell-selected-background-color-on-multiple-selection – Youssef Moawad Jul 14 '16 at 19:11
  • 1
    Or just set the cell's `selectedBackgroundView`… – Ashley Mills Sep 27 '18 at 09:36
  • This is not a good solution because it doesn't look natural as normal selection would do. RG128's answer I think is better. – Tung Fam May 19 '19 at 18:48
20

My two cents: the proper way of doing it (also visually) is to use the designated view in a (tableView)cell, that is the selectedBackgroundView property. However, you need to initialize it first with UIView()

SWIFT 3.0

override func awakeFromNib() {
    super.awakeFromNib()
    self.selectedBackgroundView = UIView()
    self.selectionStyle = .default // you can also take this line out
}

Then you can use it in your customized cell as follows:

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
    self.selectedBackgroundView!.backgroundColor = selected ? .red : nil
}

That's it. Of course you can also integrate the above in your UITableView functions referred to above. Check it out.

RG128
  • 211
  • 2
  • 6
  • 4
    While this might work, it's not the right way to do it - `selectedBackgroundView` is only shown when the cell is selected, so there's no need to override the `setSelected` method.Just set `selectedBackgroundView?.backgroundColor = .red` in `awakeFromNib()` – Ashley Mills Sep 27 '18 at 09:33
15

You've the right method already in there: didSelectRowAtIndexPath. In that method you can call tableView.cellForRowAtIndexPath(indexPath) and get your cell. Than you can set the cell-background to your color:

 func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
        println("Row \(indexPath.row) selected")
        let cell:YourCustomCell = tableView.cellForRowAtIndexPath(indexPath) as YourCell
        cell.backgroundColor = UIColor.redColor()
    }

Or, a better way would be to check in your cellForRowAtIndexPath method, if a cell is selected:

if(cell.selected){
  cell.backgroundColor = UIColor.redColor()
}else{
  cell.backgroundColor = UIColor.clearColor()
}
Christian
  • 22,585
  • 9
  • 80
  • 106
  • 2
    Thanks :) this is in the right direction. I have a problem though: When I click on a cell it becomes it's original grey selection color, when I click the next cell the previously selected cell does become red. But the currently selected cell is always grey. – Peasant coder Mar 03 '15 at 14:29
  • Did you try the first idea? – Christian Mar 03 '15 at 14:32
  • 1
    I tried both, behaviour was the same for either method. – Peasant coder Mar 03 '15 at 14:33
  • I can't Test it at the moment. But that's the way to go for you. – Christian Mar 03 '15 at 14:34
  • 2
    Added: "var myBackView = UIView(frame: cell.frame); cell.selectedBackgroundView = myBackView;" to your code. now it works. – Peasant coder Mar 03 '15 at 15:12
  • 1
    This is probably what everyone is looking for: http://stackoverflow.com/questions/26895370/swift-uitableviewcell-selected-background-color-on-multiple-selection – Youssef Moawad Jul 14 '16 at 19:11
13

Update for Swift 3

This answer is based on Cao Yong answer, and it is intended as an update for Swift 3

For Swift 3, use the following code in your cellForRowAt indexPath method set:

cell.selectionStyle = .none

Then, set it in didHighlightRowAtIndexPath

func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
    let cell  = tableView.cellForRow(at: indexPath)
    cell!.contentView.backgroundColor = .red
}

func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) {
    let cell  = tableView.cellForRow(at: indexPath)
    cell!.contentView.backgroundColor = .clear
}
Ibrahim
  • 6,006
  • 3
  • 39
  • 50
13

The correct (more native & natural) way to do it:

override func awakeFromNib() {
    super.awakeFromNib()
    selectedBackgroundView = UIView()
    selectedBackgroundView?.backgroundColor = .blue
}

Why other approaches are wrong:

  1. Highlight solution: highlight is not select. Highlight doesn't animate as Selection does. It doesn't feel natural as a normal Selection would do
  2. No need to change the color of selectedBackgroundView in setSelected method, because iOS handles the animation for us.
  3. Setting backgroundColor also doesn't behave the same as iOS does it
Tung Fam
  • 7,899
  • 4
  • 56
  • 63
8

When you tap a cell, a subviews background color is actually being changed. That subview is 'selectedBackgroundView'. You can override the view of each cell in the cellForRowAtIndexPath TableView delegate method.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

   let cell = tableView.dequeueReusableCellWithIdentifier("identifier", forIndexPath: indexPath) 

   let selectedView = UIView()
   selectedView.backgroundColor = UIColor(red: 250/255, green: 250/255, blue:     250/255, alpha: 1.0)
   cell.selectedBackgroundView = selectedView

   return cell
}

Change the color to whatever you like.

Eli Whittle
  • 1,084
  • 1
  • 15
  • 19
  • 1
    Amazing - all these upvoted answers and only one got it correct! Even better would be to do this in `awakeFromNib` in a custom cell, or set drag a view in and set in the storyboard. – Ashley Mills Sep 27 '18 at 09:33
6

To keep your code clean you should think about moving screen design related code for your cells from UITableViewController into a UITableViewCell class.

Your UITableViewController` needs only to set the selected state of the cell as follows:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
    guard let cell = tableView.cellForRow(at: indexPath) else { return }
    cell.setSelected(true, animated: true)
}

Your desired customisation can be implemented in a derrived UITableViewCell class by overriding var isSelected. With this solution you could have even different select colors for each cell.

class MyTableViewCell: UITableViewCell
{
    @IBOutlet weak var label:UILabel!

    override var isSelected: Bool
    {
        didSet{
            if (isSelected)
            {
                self.backgroundColor = UIColor.red
                if let label = label
                {
                    label.textColor = UIColor.white
                }
            }
            else
            {
                self.backgroundColor = UIColor.white
                if let label = label
                {
                    label.textColor = UIColor.black
                }
            }
        }
    }
}
hhamm
  • 1,511
  • 15
  • 22
2

SWIFT 5 Update

Set the selection style to .none in the cellForRowAT method:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Cell
    cell.selectionStyle = .none

    return cell
}

Then implement the didHighlightRowAt and the didUnhighlightRowAt methods:

func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
    let cell  = tableView.cellForRow(at: indexPath)
    cell!.contentView.backgroundColor = .red
}

func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) {
    let cell  = tableView.cellForRow(at: indexPath)
    // Add timer to be able see the effect
    Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { (_) in
       cell!.contentView.backgroundColor = .white
    }
}
Wissa
  • 1,444
  • 20
  • 24
0

For tableView==


First Call This method-

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
        cell.textLabel?.text = "Show Label"
        cell.backgroundColor = UIColor.redColor()

    }

And than call this method

func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
        cell.backgroundColor = UIColor.clearColor()
    }

For CollectionView==

1-

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        let cell = dateCollectionView.cellForItemAtIndexPath(indexPath) as! DateCollectionViewCell
            cell!.dateLabel.backgroundColor = UIColor.redColor()

 }  

2-

func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
        let cell = dateCollectionView.cellForItemAtIndexPath(indexPath) as? DateCollectionViewCell
        cell!.dateLabel.backgroundColor = UIColor.clearColor()
    }
iDeveloper
  • 2,339
  • 2
  • 24
  • 38
0
override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
    updateUI(isSelected: selected)
}

private func updateUI(isSelected: Bool){
   label.textColor = isSelected ? .red : .green
   //etc..
}
Ahmed Allam
  • 104
  • 8
-1

None of above answers worked, so I try mine and it worked: Here it is:-

  1. In your cell create function like this.
func reloadcell() {
    if isSelected {
        conView.backgroundColor = .yellow
    } else if isHighlighted {
        conView.backgroundColor = .yellow
    } else {
        conView.backgroundColor = .clear
    }
}

and call that function in layoutsubviews and in your didselect method

Aleksey Potapov
  • 3,683
  • 5
  • 42
  • 65
-1

In your cellForRowAtIndexPath method set:

cell.selectionStyle = .none

and then set didHighlightRowAtIndexPath like this...

func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
    let cell  = tableView.cellForRow(at: indexPath)
    cell!.contentView.backgroundColor = .green
}
Syscall
  • 19,327
  • 10
  • 37
  • 52
IsaSam
  • 1
  • Please describe in more detail why the specific code is needed and provide more sample code for the first code block as it needs to be in a specific method. – Fabian Mar 27 '21 at 15:09