Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

selectedBackgroundView on UITableViewCell gets applied to all views in cell

I have a table cell being displayed that shows a users image, name and some text. The user's image is 50x50, but I want a border around it so I set the view to center the image and set the frame to 52x52 and then set the background color of that view to my border color. That shows a 1 pixel border around the image.

I also want to show a 30 pixel wide border on the right of the cell when the cell is selected. I've tried to do that by creating a UIView the size of the cell's frame, then adding a subview to that view with a UIView the width and background color I would like. I then set that view to the selectedBackgroundView of the cell.

The problem here is that the cell's selectedBackgroundView gets applied to the background of all views inside the cell. So when I select a cell, the images "border" gets set to the cell's selected background color, the other 30px "border" I'm adding gets changed to that background color also.

Code inside my cellForRowAtIndexPath:

cell = (UserCellView *) currentObject;

UIView *c = [[UIView alloc ] initWithFrame:CGRectMake(0, 0, 30, cell.frame.size.height)];
c.backgroundColor = [[UIColor alloc] initWithRed:64/255.0 green:64/255.0 blue:64/255.0 alpha:1.0];

UIView *v = [[UIView alloc ] initWithFrame:cell.frame];             
v.backgroundColor = [[UIColor alloc] initWithRed:35/255.0 green:35/255.0 blue:35/255.0 alpha:1.0];              

[v addSubview:c];       

cell.selectedBackgroundView = v;

[c release];
[v release];
like image 674
JasonB Avatar asked Oct 29 '10 14:10

JasonB


3 Answers

I'll assume that you haven't actually tested what's going on to form your analysis that it "gets applied to the background of all views inside the cell".

I did something like this:

@interface TestView : UIView {
}
@end
@implementation TestView
-(void)setBackgroundColor:(UIColor*)c {
  // Breakpoint here.
  NSLog("setBackgroundColor: %@",c);
  [super setBackgroundColor:c];
}
@end

...

UIView * v = [[[UIView alloc] initWithFrame:(CGRect){{0,0},{20,20}}] autorelease];
v.backgroundColor = [UIColor magentaColor];
UIView * v2 = [[[TestView alloc] initWithFrame:(CGRect){{5,5},{10,10}}] autorelease];
v2.backgroundColor = [UIColor yellowColor];
[v addSubview:v2];
cell.selectedBackgroundView = v;

The end result is that -setBackgroundColor: is called from -[UITableViewCell _setOpaque:forSubview:] when the view is selected, with something like UIDeviceWhiteColorSpace 0 0 (i.e. [UIColor clearColor]).

Or, in other words, the background colour of some of the subviews are set to [UIColor clearColor] while the cell is selected, allowing selectedBackgroundView to show through. I think this happens because a common optimization is to give textLabel/detailTextLabel the table's background colour (e.g. white) so it draws faster, but this means the background colour has to be reset when the cell is selected.

The easiest fix is to use an image instead: a 1-by-1-pixel image of the correct colour in a UIImageView will work, if a bit messy. (I had this problem when drawing custom separator lines with 1-pixel-high UIViews, so I just included the separator into the background image.)

An alternative fix is to use a CALayer instead: Add a 52x52 sublayer to the UIImageView's layer, and set the sublayer's background colour. I'm pretty sure UITableViewCell simply walks the view hierarchy, so it should ignore custom layers. (The big disadvantage with layers is that they don't auto-size, which made them unsuitable for my purposes, and means the 30px right border won't auto-size.)

A workaround is to subclass the relevant views and ignore -setBackgroundColor: if it's equal to [UIColor clearColor].

like image 139
tc. Avatar answered Oct 31 '22 23:10

tc.


A simple but obnoxious-to-maintain solution is to override setSelected:animated: and setHighlighted:animated: with implementations re-setting the various backgrounds you want. Something along the lines of:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];
    self.childView.backgroundColor = [UIColor blueColor]; // whichever you want
}
like image 38
Roee Shenberg Avatar answered Oct 31 '22 22:10

Roee Shenberg


First add this to your file

#import <QuartzCore/QuartzCore.h>

Then turn your view into an image with...

    UIView *rowView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 1.0, 60.0)];
    rowView.backgroundColor = [UIColor colorWithRed:35/255.0 green:35/255.0 blue:35/255.0 alpha:1.0];        

    UIGraphicsBeginImageContext(rowView.bounds.size);
    [rowView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *yourImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

Then instead of adding a UIView to your cell, just add a UIImageView with "yourImage".

like image 1
Andrew Gene Avatar answered Oct 31 '22 21:10

Andrew Gene