UIEdgeInsetsMake creating a weird band on the cell, and I don't know how to fix it
Asked Answered
R

3

9

I'm trying to use UIEdgeInsetsMake to make set the background of my cell to a gradient. I've tried multiple things to get it to work, but no matter what I use, there's always an issue.

I simply have two static cells, where I'm trying to set their backgroundView in willDisplayCell:. I have separate images for the top, bottom and middle cells, but since I have two cells I only need the top and the bottom. These are those images:

Top

enter image description here

Bottom

enter image description here

There's a missing line on the top of the bottom one so that there's not a 2pt line in between them. I adjusted the height of the bottom one slightly to compensate (1pt higher). These images are 44x44pt.

I set them as follows:

- (void)tableView:(UITableView *)tableView 
  willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row == 0) {
        UIImageView *topCellBackgroundImageView = 
        [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"grouped-cell-bg-top"]
               resizableImageWithCapInsets:UIEdgeInsetsMake(5.0, 5.0, 5.0, 5.0)]];
        cell.backgroundView = topCellBackgroundImageView;
    }
    // If it's the last row
    else if (indexPath.row == ([tableView numberOfRowsInSection:0] - 1)) {
        UIImageView *bottomCellBackgroundImageView = 
        [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"grouped-cell-bg-bottom"] 
               resizableImageWithCapInsets:UIEdgeInsetsMake(5.0, 5.0, 5.0, 5.0)]];
        cell.backgroundView = bottomCellBackgroundImageView;
    }

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}

This does not work well. As you can see in the image below, in the top cell, there's a 1pt white "band" across the cell that looks quite ugly. I don't know why it's there.

enter image description here

So I changed the topCellBackgroundView to have edge insets of (5.0, 5.0, 0.0, 5.0), as since it's only rounded on the top, the bottom property needn't be factored in (it's flat). That works perfectly! Except when you select the cell, the bottom cell no longer takes the entirety of its cell up.

enter image description here

What am I supposed to do? It seems no matter what I do, it doesn't work. I also tried 1.0 instead of 0.0, as well as -1.0 to no avail.

Roundsman answered 7/7, 2013 at 16:32 Comment(3)
have u set the row height to be the height of the backgroundimageviews?Winterfeed
Pause your app while this table view is on the screen and type: po [[[UIApplication sharedApplication] keyWindow] recursiveDescription] in your debugger. Paste the output here.Coulee
Yes, both are 44 in height, materik. robert, it's available here: gist.github.com/anonymous/2ad5556bceb6cd4666edRoundsman
B
9

Great news: you're not going crazy. Your stretchable image code is likely perfect. The problem is that UITableView with a grouped style adds extra points to the height of your cells in addition to the value you return in heightForRowAtIndexPath:

So the solution to your problem is to return an adjusted cell height in heightForRowAtIndexPath: based on the total number of rows in the section:

- (CGFloat)heightForRow:(NSIndexPath *)indexPath {
    CGFloat standardHeight = 44.0f;
    NSInteger numberOfRowsInSection = [self numberOfRowsInSection:indexPath.section];

    if (numberOfRowsInSection == 1) {
        standardHeight -= 2;
    }
    else if (indexPath.row == 0 || indexPath.row == numberOfRowsInSection-1) {
        standardHeight -= 1;
    }

    return standardHeight;
}

Here's a sample image showing how UITableView does this:

uitable-view-grouped-style-borked

As far as I know, only the grouped style table views are affected, and even then the effect changes based on the total number of rows in a given section:

  1. One Row Adds two points to the cell height (defaults height is 92 pixels on retina).
  2. Two Rows Adds one point to the height of the first and last rows (90 pixels high each by default on retina).
  3. Three Rows Adds one point to the height of the first and last rows (90 pixels high each on retina by default). The middle rows are unaffected.

This is so frustrating and has never been documented as far as I know. :-)

Update

The calculations above are for a grouped style table view using the default separator style of UITableViewCellSeparatorStyleSingleLineEtched. Here's what to do in the other cases (i.e. UITableViewCellSeparatorStyleSingleLine and UITableViewCellSeparatorStyleNone):

- (CGFloat)tableView:(UITableView *)tableView 
   heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGFloat rowHeight = 44.0f;
    if (indexPath.row == 0) {
        rowHeight -=1;
    }
    return rowHeight;
}
Blackberry answered 14/7, 2013 at 19:0 Comment(1)
bro.. if i could give you a +100 on this one i wouldn't even think twice!Refuel
P
3

Update

As it turns out, I was wrong about the background, see the approved response.

On the other hand, adding a separator doesn't "solve" the issue, but it hides it pretty darn well, so my answer might still be valid in that sense.

Original post

My guess is that since you are trying to draw your own separator, the background that the tableView uses as "selected" background is already scaled considering the default separator style, I replicated the issue by putting UITableViewCellSeparatorStyleNone, and painted to cells with different colors:

Little red line between the two

Notice the tiny red line in between of the cells. Try setting the color of the separator (tableView.separatorColor) to match your style, and remove the bottom line from the asset.

Also, since we're talking about an stretchable asset, you should consider that the stretchable-image takes the insets as "this sides should be fixed" and the center region, gets copy-pasted all around, so, to have a truly stretchable graphic, you should do something like:

// For the top one
UIEdgeInsetsMake(38.0, 5.0, 5.0, 5.0)

// For the bottom one
UIEdgeInsetsMake(5.0, 5.0, 38.0, 5.0)

The key here, is that this leaves just one pixel tall of repeating pattern, which would be a solid color, and then the borders have the proper fade.

Here's the graphical explanation of the later, (I reused one file I made for the company designers and applied your dimensions):

Stretchable images

(Notice the tiny red line in between of the cells)

Perplex answered 11/7, 2013 at 6:55 Comment(2)
man thanks a lot for opening my eyes to this world of 'stretchiness'.. will definitely come in handy at some point +1Refuel
Note that as of iOS 6, you can choose to stretch the internal region instead of “copy-pasting” it. See resizableImageWithCapInsets:resizingMode:.Militiaman
N
0

In your debugger output, the height of the second SettingsCell is 45 points, not 44 as expected. Note that this is the second one in the output, but it's actually shown in reverse order and this is actually the first table cell (you can see that from the frame values of the cells, this one being at y=46 while the other one is at y=91).

I believe that this causes your first cell's background misalignment in the second screenshot (and the stretching responsible for the white band you describe on the first screenshot, although I personally can't notice it) and that you can fix this by ensuring that your UITableViewCell is set to the correct frame height (44 points). This isn't in the code snippet you included in your question, but this is most likely in your – tableView:heightForRowAtIndexPath: method.

Once you have fixed this, edge insets of (5.0, 5.0, 1.0, 5.0) are probably what you intended since you don't wish to stretch the one-point bottom line. But (5.0, 5.0, 5.0, 5.0) and (5.0, 5.0, 0.0, 5.0) should look the same since there is no vertical stretching at all when the height of the bitmap and the height of the frame are exact matches.

Niehaus answered 10/7, 2013 at 21:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.