Reputation: 25144
I'm implementing a color-chooser table view where the user can select amongst, say, 10 colors (depends on the product). The user can also select other options (like hard drive capacity, ...).
All color options are inside their own tableview section.
I want to display a little square on the left of the textLabel showing the actual color.
Right now I'm adding a simple square UIView, give it the correct background color, like this :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RMProductAttributesCellID];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:RMProductAttributesCellID] autorelease];
cell.indentationWidth = 44 - 8;
UIView *colorThumb = [[[UIView alloc] initWithFrame:CGRectMake(8, 8, 28, 28)] autorelease];
colorThumb.tag = RMProductAttributesCellColorThumbTag;
colorThumb.hidden = YES;
[cell.contentView addSubview:colorThumb];
}
RMProductAttribute *attr = (RMProductAttribute *)[_product.attributes objectAtIndex:indexPath.section];
RMProductAttributeValue *value = (RMProductAttributeValue *)[attr.values objectAtIndex:indexPath.row];
cell.textLabel.text = value.name;
cell.textLabel.backgroundColor = [UIColor clearColor];
UIView *colorThumb = [cell viewWithTag:RMProductAttributesCellColorThumbTag];
colorThumb.hidden = !attr.isColor;
cell.indentationLevel = (attr.isColor ? 1 : 0);
if (attr.isColor) {
colorThumb.layer.cornerRadius = 6.0;
colorThumb.backgroundColor = value.color;
}
[self updateCell:cell atIndexPath:indexPath];
return cell;
}
This displays fine without problems.
My only problem is that when I select a "color" row, during the fade-to-blue selection animation, my custom UIView (colorThumb) is hidden. It gets visible again just after the selection/deselection animation ended, but this produces an ugly artifact.
What should I do to correct this? Don't I insert the subview at the right place?
(There's nothing special in the didSelectRowAtIndexPath, I just change the cell's accessory to a checkbox or nothing, and deselect the current indexPath).
Upvotes: 177
Views: 57966
Reputation: 2796
It's because the table view cell automatically changes the background color of all views inside the content view for the highlighted state. You may consider subclassing UIView
to draw your color or using UIImageView
with a custom 1x1 px stretched image.
Upvotes: 121
Reputation: 3727
SIMPLEST solution without bugs with animation (as in the top rated answer) and without subclassing and drawing - set layer's border color instead of backgroundColor and set very big border width.
colorThumb.layer.cornerRadius = 6
colorThumb.layer.borderWidth = colorThumb.frame.width
colorThumb.layer.borderColor = value.color
Upvotes: 1
Reputation: 2902
For may case, This is the Septs to avoid getting the gray color for all items in the cell (in case you are using custom table view cell):
Set the selectionStyle to .none 👉 selectionStyle = .none
Override this method.
func setHighlighted(_ highlighted: Bool, animated: Bool)
Call the super, to get the benefit of super setup.
super.setHighlighted(highlighted, animated: animated)
Do what ever highlighting logic you want.
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
// Your Highlighting Logic goes here...
}
Upvotes: 4
Reputation: 9487
I wanted to keep the default selection behavior except for one cell subview that I wanted to ignore the automatic background color change. But I also needed to be able to change the background color at other times.
The solution I came up with was to subclass UIView
so it ignores setting the background color normally and add a separate function to bypass the protection.
Swift 4
class MyLockableColorView: UIView {
func backgroundColorOverride(_ color: UIColor?) {
super.backgroundColor = color
}
override var backgroundColor: UIColor? {
set {
return
}
get {
return super.backgroundColor
}
}
}
Upvotes: 0
Reputation: 10432
UITableViewCell
changes the background color of all sub views when cell is selected or highlighted ,You can Solve this problem by overriding Tableview cell's setSelected:animated
and setHighlighted:animated
and resetting view background color.
In Objective C :
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
UIColor *color = self.yourView.backgroundColor;
[super setSelected:selected animated:animated];
if (selected){
self.yourView.backgroundColor = color;
}
}
-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated{
UIColor *color = self.yourView.backgroundColor;
[super setHighlighted:highlighted animated:animated];
if (highlighted){
self.yourView.backgroundColor = color;
}
}
In Swift 3.1 :
override func setSelected(_ selected: Bool, animated: Bool) {
let color = yourView.backgroundColor
super.setSelected(selected, animated: animated)
if selected {
yourView.backgroundColor = color
}
}
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
let color = yourView.backgroundColor
super.setHighlighted(highlighted, animated: animated)
if highlighted {
yourView.backgroundColor = color
}
}
Upvotes: 225
Reputation: 5605
Found a pretty elegant solution instead of messing with the tableViewCell selection/highlighting methods. You can create a subclass of UIView that ignores setting its background color to clear color.
Swift 3/4:
class NeverClearView: UIView {
override var backgroundColor: UIColor? {
didSet {
if backgroundColor != nil && backgroundColor!.cgColor.alpha == 0 {
backgroundColor = oldValue
}
}
}
}
Swift 2:
class NeverClearView: UIView {
override var backgroundColor: UIColor? {
didSet {
if CGColorGetAlpha(backgroundColor!.CGColor) != 0 {
backgroundColor = oldValue
}
}
}
}
Obj-C version:
@interface NeverClearView : UIView
@end
@implementation NeverClearView
- (void)setBackgroundColor:(UIColor *)backgroundColor {
if (CGColorGetAlpha(backgroundColor.CGColor) != 0) {
[super setBackgroundColor:backgroundColor];
}
}
@end
Upvotes: 44
Reputation: 2298
Inspired by Yatheesha B L's answer.
If you call super.setSelected(selected, animated:animated), it will clear all background color you set. So, we will not call super method.
In Swift :
override func setSelected(selected: Bool, animated: Bool) {
if(selected) {
contentView.backgroundColor = UIColor.red
} else {
contentView.backgroundColor = UIColor.white
}
}
override func setHighlighted(highlighted: Bool, animated: Bool) {
if(highlighted) {
contentView.backgroundColor = UIColor.red
} else {
contentView.backgroundColor = UIColor.white
}
}
Upvotes: 4
Reputation: 4962
If the background solution mentioned above isn't fixing your problem, your issue may lie in your datasource
for your tableView.
For me, I was creating an instance of a DataSource object (called BoxDataSource
) to handle the delegate and dataSource tableView methods, as so:
//In cellForRowAtIndexPath, when setting up cell
let dataSource = BoxDataSource(delegate: self)
cell.tableView.dataSource = dataSource
return cell
This was causing the dataSource to be deallocated whenever the cell was tapped, and thus all the contents disappeared. The reason being, is ARC deallocating/garbage collecting nature.
To fix this, I had to go into the custom cell, add a datasource variable:
//CustomCell.swift
var dataSource: BoxDataSource?
Then, you need to set the dataSource to the cell's dataSource var
you just created in cellForRow, so this isnt deallocated with ARC.
cell.statusDataSource = BoxAssigneeStatusDataSource(delegate: self)
cell.detailsTableView.dataSource = cell.statusDataSource
return cell
Hope that helps.
Upvotes: -1
Reputation: 1393
Adding another solution if you're using storyboards. Create a subclass of UIView
that does not allow the backgroundColor
to be set after it is initially set.
@interface ConstBackgroundColorView : UIView
@end
@implementation ConstBackgroundColorView
- (void)setBackgroundColor:(UIColor *)backgroundColor {
if (nil == self.backgroundColor) {
[super setBackgroundColor:backgroundColor];
}
}
@end
Upvotes: -1
Reputation: 391
This is similar to Pavel Gurov's answer, but more flexible in that it allows any color to be permanent.
class PermanentBackgroundColorView: UIView {
var permanentBackgroundColor: UIColor? {
didSet {
backgroundColor = permanentBackgroundColor
}
}
override var backgroundColor: UIColor? {
didSet {
if backgroundColor != permanentBackgroundColor {
backgroundColor = permanentBackgroundColor
}
}
}
}
Upvotes: 0
Reputation: 17257
Place this code in your subclass of UITableViewCell
Swift 3 syntax
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if(selected) {
lockerSmall.backgroundColor = UIColor.init(red: 233/255, green: 106/255, blue: 49/255, alpha: 1.0)
}
}
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
if(highlighted) {
lockerSmall.backgroundColor = UIColor.init(red: 233/255, green: 106/255, blue: 49/255, alpha: 1.0)
}
}
Upvotes: -1
Reputation: 2636
Don't forget to override setSelected
as well as setHighlighted
override func setHighlighted(highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
someView.backgroundColor = .myColour()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
someView.backgroundColor = .myColour()
}
Upvotes: 0
Reputation: 12592
Try Following code:
-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
//Set your View's Color here.
}
Upvotes: 0
Reputation: 35783
For Swift 2.2 this works
cell.selectionStyle = UITableViewCellSelectionStyle.None
and reason is explained by @Andriy
It's because table view cell automatically changes background color of all views inside content view for highlighted state.
Upvotes: 10
Reputation: 413
Draw the view instead of setting background colour
import UIKit
class CustomView: UIView {
var fillColor:UIColor!
convenience init(fillColor:UIColor!) {
self.init()
self.fillColor = fillColor
}
override func drawRect(rect: CGRect) {
if let fillColor = fillColor {
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, fillColor.CGColor);
CGContextFillRect (context, self.bounds);
}
}
}
Upvotes: 1
Reputation: 241
here is my solution,use contentView to show selectionColor,it's work perfectly
#import "BaseCell.h"
@interface BaseCell ()
@property (nonatomic, strong) UIColor *color_normal;
@property (nonatomic, assign) BOOL needShowSelection;
@end
@implementation BaseCell
@synthesize color_customSelection;
@synthesize color_normal;
@synthesize needShowSelection;
- (void)awakeFromNib
{
[super awakeFromNib];
[self setup];
}
- (void)setup
{
//save normal contentView.backgroundColor
self.color_normal = self.backgroundColor;
if (self.color_normal == nil) {
self.color_normal = [UIColor colorWithRGBHex:0xfafafa];
}
self.color_customSelection = [UIColor colorWithRGBHex:0xF1F1F1];
self.accessoryView.backgroundColor = [UIColor clearColor];
if (self.selectionStyle == UITableViewCellSelectionStyleNone) {
needShowSelection = NO;
}
else {
//cancel the default selection
needShowSelection = YES;
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
}
/*
solution is here
*/
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
if (needShowSelection) {
self.contentView.backgroundColor = self.backgroundColor = color_customSelection;
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
if (needShowSelection) {
self.contentView.backgroundColor = self.backgroundColor = color_normal;
}
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if (needShowSelection) {
UIColor *color = selected ? color_customSelection:color_normal;
self.contentView.backgroundColor = self.backgroundColor = color;
}
}
Upvotes: -1
Reputation: 109
You can cell.selectionStyle = UITableViewCellSelectionStyleNone;
,
then set the backgroundColor at - (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
Upvotes: 7
Reputation: 9703
Inspired by Yatheesha B L's answer I created a UITableViewCell category/extension that allows you to turn on and off this transparency "feature".
Swift
let cell = <Initialize Cell>
cell.keepSubviewBackground = true // Turn transparency "feature" off
cell.keepSubviewBackground = false // Leave transparency "feature" on
Objective-C
UITableViewCell* cell = <Initialize Cell>
cell.keepSubviewBackground = YES; // Turn transparency "feature" off
cell.keepSubviewBackground = NO; // Leave transparency "feature" on
KeepBackgroundCell is CocoaPods compatible. You can find it on GitHub
Upvotes: 8
Reputation: 493
UITableViewCell changes the backgroundColor of all subviews on selection for some reason.
This might help:
Use something like that to stop UITableView from changing your view color during selection.
Upvotes: 3
Reputation: 4779
Another way to manage the problem is to fill the view with core-graphics gradient, like:
CAGradientLayer* gr = [CAGradientLayer layer];
gr.frame = mySubview.frame;
gr.colors = [NSArray arrayWithObjects:
(id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:.5] CGColor]
,(id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:.5] CGColor]
, nil];
gr.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0],[NSNumber numberWithFloat:1],nil];
[mySubview.layer insertSublayer:gr atIndex:0];
Upvotes: 9