I have a simple tableViewCell build in interface builder. It contains a UIView which contains an image. Now, when I select the cell, the default blue selection background is
You can change the behavior of the tableViewCell by overriding the function setHighlighted in UITableViewCell class (you will need to inherit from it). Example of my code where I change the background image of my cell :
// animate between regular and highlighted state
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated; {
[super setHighlighted:highlighted animated:animated];
//Set the correct background Image
UIImageView* backgroundPicture = (UIImageView*)[self viewWithTag:HACK_BACKGROUND_VIEW_TAG];
if (highlighted) {
backgroundPicture.image = [UIImage imageNamed:@"FondSelected.png"];
}
else {
backgroundPicture.image = [UIImage imageNamed:@"Fond.png"];
}
}
You can also change the selection mode to gray, blue or none in the interface builder.
Add this to your UITableViewCell
override func setHighlighted(highlighted: Bool, animated: Bool) {
super.setHighlighted(false, animated: animated)
if highlighted {
self.backgroundColor = UIColor.blueColor()
}else{
UIView.animateWithDuration(0.2, animations: {
self.backgroundColor = UIColor.clearColor()
})
}
}
When your UITableViewCell
is selected, there are two states you should pay attention to: Highlighted
and Selected
.
So, for scenarios that you have a custom cell class which is subclass of UITableViewCell
, you can easily override these two methods to avoid this situation(Swift):
class MyCell: UITableViewCell {
@IBOutlet var myView: UIView!
override func setHighlighted(highlighted: Bool, animated: Bool) {
let myViewBackgroundColor = myView.backgroundColor
super.setHighlighted(highlighted, animated: animated)
myView.backgroundColor = myViewBackgroundColor
}
override func setSelected(selected: Bool, animated: Bool) {
let myViewBackgroundColor = myView.backgroundColor
super.setSelected(selected, animated: animated)
myView.backgroundColor = myViewBackgroundColor
}
}
Summary
This solution let's you lock some of a cell's background colors, while the remainder are controlled by system behaviour.
Based on mientus' answer, I have created a solution which allows you to specify which views should keep their background color.
This still allows other cell subviews to have their background removed on highlighting/selection, and is the only solution which works in our case (two views needing a permanent background).
I used a protocol-oriented approach, with a BackgroundLockable
protocol containing the list of views to lock, and running a closure while keeping the colors:
protocol BackgroundLockable {
var lockedBackgroundViews: [UIView] { get }
func performActionWithLockedViews(_ action: @escaping () -> Void)
}
extension BackgroundLockable {
func performActionWithLockedViews(_ action: @escaping () -> Void) {
let lockedViewToColorMap = lockedBackgroundViews.reduce([:]) { (partialResult, view) -> [UIView: UIColor?] in
var mutableResult = partialResult
mutableResult[view] = view.backgroundColor
return mutableResult
}
action()
lockedViewToColorMap.forEach { (view: UIView, color: UIColor?) in
view.backgroundColor = color
}
}
}
Then I have a subclass of UITableViewCell
, which overrides highlighting and selection to run the protocol's closure around calling the default (super) behaviour:
class LockableBackgroundTableViewCell: UITableViewCell, BackgroundLockable {
var lockedBackgroundViews: [UIView] {
return []
}
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
performActionWithLockedViews {
super.setHighlighted(highlighted, animated: animated)
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
performActionWithLockedViews {
super.setSelected(selected, animated: animated)
}
}
}
Now I just have to subclass LockableBackgroundTableViewCell
or use the BackgroundLockable
protocol in a cell class to easily add locking behaviour to some cells!
class SomeCell: LockableBackgroundTableViewCell {
@IBOutlet weak var label: UILabel!
@IBOutlet weak var icon: UIImageView!
@IBOutlet weak var button: UIButton!
override var lockedBackgroundViews: [UIView] {
return [label, icon]
}
}
You need to override the next two methods in your custom cell:
- (void) setSelected:(BOOL)selected animated:(BOOL)animated;
- (void) setHighlighted:(BOOL)highlighted animated:(BOOL)animated;
Note that:
[super setSelected:animated:]
and [super setHighlighted:animated:]
in the beginning of your custom implementation or correspond methods;UITableViewCellSelectionStyleNone
selectionStyle for your custom cell, to disable any default UITableViewCell
styling;Here the example of the implementation:
- (void) setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
[self setHighlightedSelected:highlighted animated:animated];
}
- (void) setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
[self setHighlightedSelected:selected animated:animated];
}
- (void) setHighlightedSelected:(BOOL)selected animated:(BOOL)animated
{
void(^selection_block)(void) =
^
{
self.contentView.backgroundColor = selected ? SELECTED_BACKGROUND_COLOR : NORMAL_BACKGROUND_COLOR;
};
if(animated)
{
[UIView animateWithDuration:SELECTION_ANIMATION_DURATION
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:selection_block
completion:NULL];
}
else
selection_block();
}
The contentView
is the property of UITableViewCell
that is appeared in iOS 7. Note that you can use your own cell's child view or views instead of it.
Ok, loosing the background color of a UIView class is normal behavior when its in a selected tableviewcell. I couldn't figure out how to prevent that. Now I've just replaced the UIView with an UIImageView containing a stretched 1x1 white pixel. Ugly imo, but it works.