Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS: Using UIAppearance to define custom UITableViewCell color

If I set the backgroundColor attribute on a grouped UITableViewCell, the background color successfully changes. Great.

But I'd like to use UIAppearance to change the background color on all my UITableViewCells, so I can do it in one place and affect a change everywhere. Here's my code:

[[UITableViewCell appearance] setBackgroundColor:[UIColor colorWithRed:30.0/255.0 green:30.0/255.0 blue:30.0/255.0 alpha:1.0]];

UITableViewCell implements UIAppearance and UIAppearanceContainer, so I would have thought this would work. But it doesn't. I've also tried using -[UITableViewCell appearanceWhenContainedIn:(Class)], and that doesn't work either.

Any ideas?

like image 239
Greg Maletic Avatar asked Nov 29 '11 19:11

Greg Maletic


3 Answers

Update (2013/7/8) - This has been fixed in newer versions of iOS. However, it's worth knowing about if you're targeting iOS 6 or below.

You can blame Apple for this one, and it's actually pretty mean of them. Technically, backgroundColor is not customizable through appearance proxies.

From Apple's documentation:

To support appearance customization, a class must conform to the UIAppearanceContainer protocol and relevant accessor methods must be marked with UI_APPEARANCE_SELECTOR.

If we go into a class like UIBarButtonItem and look at the tintColor property we see this:

@property(nonatomic,retain) UIColor *tintColor UI_APPEARANCE_SELECTOR;

So because it's marked with the UI_APPEARANCE_SELECTOR tag we know it works with UIAppearance.

Here's where Apple are particularly mean: in a UIView, backgroundColor has no appearance selector tag, but still works with UIAppearance. According to all the documentation Apple provide it should not, but yet it does!

This gives the misleading impression that it will work for all sub-classes of UIView, including UITableView. This has come up before, in this previous SO answer

So the bottom line is that backgroundColor shouldn't work at all with UIAppearance, but for some reason it does on a UIView. It is not guaranteed to work on UIView subclasses, and it doesn't work at all on UITableView. Sorry I couldn't give you a more positive answer!

like image 81
lxt Avatar answered Nov 14 '22 12:11

lxt


You can create your own subclass of UITableViewCell that conforms to UIAppearance and mark a custom setter with UI_APPEARANCE_SELECTOR. Then set the cell backgroundColor on the superlass from your custom setter .

In your appDelegate

[[CustomCell appearance] setBackgroundCellColor:[UIColor redColor]];

In your UItableView subclass

@interface CustomCell : UITableViewCell <UIAppearance>

@property (nonatomic, weak) UIColor *backgroundCellColor UI_APPEARANCE_SELECTOR;

@implementation CustomCell

@synthesize backgroundCellColor;

-(void)setBackgroundCellColor:(UIColor *)backgroundColor
{
    [super setBackgroundColor:backgroundColor];
}

I'm using ARC in this example.

like image 23
Nate Potter Avatar answered Nov 14 '22 11:11

Nate Potter


Without Subclassing! Doing this on a subclass is probably NOT the best practice, especially if you want to hit all tableView backgrounds. That's a lot of subclassing. A lot of potential bugs. A mess really. The best way to do this is to use a category. You will have to set one up for both the tableViewCell and the tableView. I will just demonstrate the one for the cell. The property on the tableView you must do is the backgroundColor property. NB. I'm prepending my methods with "sat".

// .h

    #import <UIKit/UIKit.h>

@interface UITableViewCell (Appearance)<UIAppearance>
@property (strong, nonatomic) UIColor *satBackgroundColor UI_APPEARANCE_SELECTOR;
@end

// .m

#import "UITableViewCell+Appearance.h"

@implementation UITableViewCell (Appearance)

- (UIColor *)satBackgroundColor
{
    return self.backgroundColor;
}

- (void)setSatBackgroundColor:(UIColor *)satBackgroundColor
{
    self.backgroundColor = satBackgroundColor;
}


@end

Now in your appDelegate or some manager class you'll import the category and just call it as if it had an appearance proxy built in.

UITableViewCell *cell = [UITableViewCell appearance];
cell.satBackgroundColor = [UIColor orangeColor];

Ok, so now just do the one for the tableView's background property. Simple.

like image 3
smileBot Avatar answered Nov 14 '22 13:11

smileBot