achiral
achiral

Reputation: 601

iOS - Custom UITableViewCell subviews within selectedBackgroundView

I have set up a custom UITableViewCell with multiple labels and an image, and I've (finally) got it to look how I want it to look.

I can't seem to figure out how to get the selection to look how I want it to look, however. I have implemented the setSelected method, which allows me to change the background color just fine, but what I would really like is to set the background color to black and display a colored rectangle on the left-hand side of the selected cell (10 pixels wide and the height of the cell).

The colored box would be a color set programmatically, so although I could easily set the selectedBackgroundView to be a UIImageView, this will not work in this case.

The following code will not display the selectedViewColor UIView at all when the cell is selected:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    UIView *selectedView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.selectedBackgroundView.bounds.size.width, self.selectedBackgroundView.bounds.size.height)];
    [selectedView setBackgroundColor:[UIColor blackColor]];

    UIView *selectedView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, self.selectedBackgroundView.bounds.size.height)];
    [selectedViewColor setBackgroundColor:[UIColor redColor]];
    [selectedView addSubview:selectedViewColor];

    self.selectedBackgroundView = selectedView;

    [super setSelected:selected animated:animated];
}

This code seems pretty basic, so I assume there is an issue with displaying any type of subview within the selectedBackgroundView.

Any alternatives or advice would be greatly appreciated.

Upvotes: 1

Views: 3085

Answers (4)

Alexander Kostiev
Alexander Kostiev

Reputation: 306

Basing on @Xono 's answer, I made this solution to the problem:

Inside of the initWithStyle:reuseIdentifier: method I added a separator view:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        CGRect selectionViewFrame = self.contentView.bounds;
        UIView *selectionView = [[UIView alloc] initWithFrame:selectionViewFrame];
        [selectionView setBackgroundColor:[UIColor colorWithWhite:1. alpha:0.65]];
        self.selectedBackgroundView = selectionView;        
        self.vwSelectedSeparator = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.contentView.frame.size.width, 1.)];
        [self.vwSelectedSeparator setBackgroundColor:[UIColor colorWithHexString:@"aaa"]];
        [self.vwSelectedSeparator setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
        [selectionView addSubview:self.vwSelectedSeparator];

        [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
    }
    return self;
}

And then in setSelected:animated: method I added these lines:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    if (selected) {
        [self.vwSelectedSeparator setBackgroundColor:[UIColor colorWithHexString:@"aaa"]];
    }
}

Works like a charm for both ios6 and ios7 for me. I hope this helps.

Upvotes: 0

andrew92b
andrew92b

Reputation: 73

You can override method "setBackgroundColor" for your subviews and add another method with the same functionality. At this case UITableCell won't be able to change backgroundColor after selection.

- (void)setBackgroundColor:(UIColor *)backgroundColor {
}

- (void)setColor:(UIColor *)color {
    [super setBackgroundColor:color];
}

Upvotes: 0

Tyson
Tyson

Reputation: 14734

In addition to Xono's answer above, following up on his comment on the answer above:

One thing I've come across a couple of times while researching this subject is a possibility that the code behind UITableViewCell may actually set the backgroundColor of all subviews to transparent when a cell is selected.

It does indeed do this if your cell's SelectionStyle is anything but None. And it does it in both the setHighlighted and setSelected calls of UITableViewCell.

My first solution was to subclass UITableViewCell and override both these methods, not calling the base class method and doing my own animations. This is not ideal as your now re-implenting (probably badly) standard iOS animations.

Looking into it further though, the standard methods animate the opacity of the whole view, not the subviews (they only set the color of the subviews). So the best approach is to still call the base class methods, and then just re-set your subview colors back to whatever they should be. Even though your setting them instantaneously, because their superview is still animating its opacity, it still fades in and out correctly:

    public override void SetHighlighted(bool highlighted, bool animated)
    {
        base.SetHighlighted(highlighted, animated);
        SelectedBackgroundView.Subviews[0].BackgroundColor = SelectionColor;
    }

    public override void SetSelected(bool selected, bool animated)
    {
        base.SetSelected(selected, animated);
        SelectedBackgroundView.Subviews[0].BackgroundColor = SelectionColor;
    } 

This is c# MonoTouch code, but it applies equally well to obj-c.
Note that in my case I always have exactly 1 subview, hence the hardcoded 0 indexer. This may differ depending on your view structure.

Upvotes: 1

Xono
Xono

Reputation: 1910

There's a few things that could be done better with this code. Reinitialising two views in the setSelected method is pretty inefficient. You're actually blanking out everything in the cell when you select it with this code (which I'm guessing is not what you want). And finally, you're treating selectedBackgroundView as if it's the only view that gets displayed when you select the cell (according to Apple's documentation, it is displayed over the backgroundView).

Try the following (Edited) - Put this code where you create the cell (presumably, cellForRowAtIndexPath:)

UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, cell.backgroundView.bounds.size.width, cell.backgroundView.bounds.size.height)]; // we need this because in cells, the background views always take up the maximum width, regardless of their frames.
container.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0]; // make it transparent - we only want the square subview to be seen.
UIView *selectedViewColor = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, self.selectedBackgroundView.bounds.size.height)];
[selectedViewColor setBackgroundColor:[UIColor redColor]];
[container addSubview:selectedViewColor]
cell.selectedBackgroundView = container;

This will make your red square appear when (and only when) the cell is selected, over the other views in the cell. From Apple's docs:

UITableViewCell adds the value of this property as a subview only when the cell is selected. It adds the selected background view as a subview directly above the background view (backgroundView) if it is not nil, or behind all other views.

Second, use the following code in your cell:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
   [super setSelected:selected animated:animated];
   if(selected == YES)
   {
      self.backgroundView.backgroundColor = [UIColor blackColor];
   }
   else
   {
      self.backgroundView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0] // replace this with whatever's appropriate - a return to the unselected state.
   }
}

This will ensure that your background turns black when the cell is selected (without otherwise interfering with what's displayed. Hopefully these changes should also resolve the issue you're having.

Upvotes: 4

Related Questions