15

I'm trying to add a subview to a UITableViewCell and the design that I'm working from demands that this particular subview (an image) needs to be larger than the actual UITableViewCell and thus partly overlap its siblings.

So I've set up my table cell, generated my image and added it to the cell's contentView:

// rowHeight for the UITableView is 45.0f

UIImage *image = [self createCellThumbnail: someImage];
UIImageView *thumbView = [[UIImageView alloc] initWithFrame: CGRectMake(150, -5, 55,55)];
thumbView.transform = CGAffineTransformMakeRotation(0.1f);
thumbView.image = image;

cell.clipsToBounds = NO;
cell.contentView.clipsToBounds = NO;

[cell.contentView addSubview: thumbView];

While the image will 'overflow' into the cell below it, the top of the image is always clipped, as demonstrated here:

img

Does anyone know if what I'm trying to do is possible with the current approach?

Or should I just figure out a way to draw these images onto the UITableView after all the cells are drawn (it's a non-scrollable tableview, so that would work and be fairly easy).

Update:

Have also tried adding the following, to no avail:

cell.opaque = NO;
cell.contentView.opaque = NO;

cell.clearsContextBeforeDrawing = NO;
cell.contentView.clearsContextBeforeDrawing = NO;

cell.clipsToBounds = NO;    
cell.contentView.clipsToBounds = NO;
gre_gor
  • 6,669
  • 9
  • 47
  • 52
NSSec
  • 4,431
  • 1
  • 27
  • 29

4 Answers4

15

I seems that the tableView renders its cell from bottom to top, so the cells above one cell overlap that one cell. To avoid this, you'd have to set the backgroundColor of all cells to +[UIColor clearColor] so that you won't see those overlap problems.

But setting the backgroundColor to clear in -tableView:cellForRowAtIndexPath: does not make any sense. UIKit does a lot of stuff with the cell before it's drawn, so does it reset the backgroundColor property of the cell.

What we need to do is setting the backgroundColor in a later state. Luckily there is this -[UITableViewDelegate tableView:willDisplayCell:forRowAtIndexPath:] which we can implement like this:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    cell.backgroundColor = [UIColor clearColor];
}

Now we're setting the backgroundColor just before the cell is drawn an this turns out to be working.

Joost
  • 10,333
  • 4
  • 55
  • 61
  • This works for me. It is kinda odd that when you set a backgroundColor explicitly at UITableViewCell creation that UIKit subsequently says "Sure, but not today, I'll make it this opaque color.". – NSSec May 16 '10 at 09:41
  • 1
    added an answer with what worked for me that may help someone - probably requires a combination of the two answers – Sam Nov 12 '13 at 11:53
  • updated my answer - by changing the z-order of the cells views we don't need to use transparent cells... – Sam Nov 12 '13 at 12:41
  • willDisplayCell did the trick. looks like it's not enough to set the cell background at the interface builder. It looks like cell background color gets sets again when it is a reused cell, which will cover the cell on top/behind of it. – hasan May 21 '15 at 14:01
  • ios9, successfully animate uilabel outside of cell with this approach. – neobie Sep 03 '16 at 07:37
4

UPDATE:

So I've done some more experimentation and the following solution still works without having to set the background of the cell to transparent, this involved moving the z order of the covered cell. This works with highlighting and selecting of the other cell (via the relevant callbacks), and if the two cell's backgrounds are different colors. Solution is as follows (you can ignore the didHighlight and didSelect methods if they don't matter to you):

(note that "covered row" is the one whose content we are trying to keep visible and In my case its content goes slightly into the row above, which was clipping it)

-(void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == 0 && indexPath.row == ROW_ABOVE_COVERED_ROW)
    {
        NSIndexPath * rowbelow = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
        UITableViewCell* cell = [tableView cellForRowAtIndexPath:rowbelow];
        [cell.superview bringSubviewToFront:cell];
    }
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == 0 && indexPath.row == ROW_ABOVE_COVERED_ROW)
    {
        NSIndexPath * rowbelow = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
        UITableViewCell* cell = [tableView cellForRowAtIndexPath:rowbelow];
        [cell.superview bringSubviewToFront:cell];
    }
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0 && indexPath.row == COVERED_ROW)
    {
        [cell.superview bringSubviewToFront:cell];
        cell.contentView.superview.clipsToBounds = NO;
    }
}

NOTE: you should also set the background color of your content to clear, or it will adopt the bgcolor of the rest of your cell, and so when you manage to bring your content to the front of the covering cell, it will take the background color with it and leave a nasty looking block in the other cell (in my case my only content was the detailTextLabel and the textLabel):

// in cellForRowAtIndexPath:
[cell setBackgroundColor:[UIColor redColor]]; //using red for debug
cell.detailTextLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.backgroundColor = [UIColor clearColor];

I hope that's helpful to anyone else trying this....

ORIGINAL:

For me the solution was to use:

self.contentView.superview.clipsToBounds = NO;

My cells were already transparent, but my content was still getting clipped. In my case I was using a custom cell which moves it's content up in layoutSubviews. So layoutSubviews for my custom cell wound up as follows:

-(void)layoutSubviews
{
    [super layoutSubviews];
    self.contentView.frame = CGRectOffset(self.contentView.frame, 0, -11);
    self.contentView.superview.clipsToBounds = NO;
}

I don't know if this would work if the cell above was opaque, or if the cells were to highlight when pressed, whether this would cover up my content.

However, I didn't need to make the cell transparent again in the viewWillDisplayCell callback method - doing it in the normal cellForRowAtIndexPath was sufficient

Sam
  • 3,453
  • 1
  • 33
  • 57
2

I had this problem and I made sure my custom tableviewcell's main background had clip subviews checked and it solved the problem. This was with a custom tableview cell loaded from a xib though. Not exactly the same but similar situation.

mike
  • 21
  • 1
1

I actually had the opposite just yesterday, I had created a custom table cell and for some reason I got an overflow which I didn't want to have. My solution was to add the following code to my view controller class:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{  
    return 175;
}

When it matched the height of the table cell there was no overlap; when it was too small there was overlap. Mind you though that I got very quicky behavious so I'm not sure it's a very good idea to do this.

Ivo Jansch
  • 1,438
  • 1
  • 15
  • 18
  • That modifies the actual height of the table cell, which in my case is already set tot 45.0f (the preferred height). Also, modifying that will only extend the cell 'to the bottom' while my problem exists at the top of the cell. – NSSec May 16 '10 at 08:37