16

This seems like an easy problem, but I've hit a brick wall finding a solution and I'm hoping someone on here can help...

I am making a UITableView using UITableViewStyleGrouped, and I am seeing a small (1 pixel) white line between the first and second row of every section of my table view (see image)

example of gap

It seems that the reason for this line is that the first cell in each section is 1 pixel taller than the others. When I set the initial cell of each group to be 29 pixels high (the others are 30) the gap disappears and all is well.

While this is workaround is successful, I'd rather know the reason for this occurring so I can avoid using a messy hack.

FYI:

  • I have made sure this is not an issue with the background images I have by using - they are all 30px high, and I have replaced the top image with a middle image with the same result.
  • I have removed all borders between the cells by setting separatorStyle = UITableViewCellSeparatorStyleNone
  • This effect doesn't happen on UITableViewStylePlain

Any help is greatly appreciated.

Thanks

Code for table setup:

  myTable = [[UITableView alloc] initWithFrame:CGRectMake(TABLE_SHIFT_OFFSET,
                                                          DATE_SLIDER_HEIGHT - DATE_SLIDER_SHADOW_HEIGHT,
                                                          326,
                                                          self.view.frame.size.height - DATE_SLIDER_HEIGHT + DATE_SLIDER_SHADOW_HEIGHT) style:UITableViewStyleGrouped];
  myTable.backgroundColor = [UIColor clearColor];
  myTable.backgroundView = nil;
  myTable.separatorStyle = UITableViewCellSeparatorStyleNone;
  [self.view addSubview:myTable];
  myTable.dataSource = self;
  myTable.delegate = self;

Code for cell setup:

- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
  // This is a hack to make sure that a white line doesnt appear between the
  // first and second rows in the table, which happens for reasons I dont understand
  if (indexPath.row == 0) {
    return 29;
  }

  return 30;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{     
  MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"satCell"];
  if (!cell)
  {
    cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"satCell"];
  }

  cell.backgroundView = nil;
  cell.backgroundView = [[UIView alloc] initWithFrame:CGRectZero];

  cell.backgroundColor = [UIColor whiteColor];
  int numberOfRowsInThisSection = [self tableView:tableView numberOfRowsInSection:indexPath.section];

  if (numberOfRowsInThisSection == 1)
  {
    cell.backingImage = self.singleWhite;
  }
  else if (indexPath.row == 0)
  {
    cell.backingImage = self.topWhite;
  }
  else if (indexPath.row % 2 == 0)
  {
    // Even row
    if (indexPath.row == numberOfRowsInThisSection - 1)
    {
      // Last row (even)
      cell.backingImage = self.botWhite;
    }
    else
    {
      // Normal row (even)
      cell.backingImage = self.midWhite;
    }
  }
  else if (indexPath.row % 2 == 1)
  {
    // Odd row
    if (indexPath.row == numberOfRowsInThisSection - 1)
    {
      // Last row (even)
      cell.backingImage = self.botDark;
    }
    else
    {
      // Normal row (odd)
      cell.backingImage = self.midDark;
    }
  }

  // Setup cell text [REMOVED FOR BREVITY]

  return cell;
}

The code for setting the backing Image within MyTableViewCell:

- (void)setBackingImage:(UIImage *)backingImage
{
  if (self.backingImage != backingImage)
  {
    _backingImage = nil;
    _backingImage = backingImage;
    self.backingView.image = backingImage;
  }
}

self.backingView is set up using the following code:

[[UIImageView alloc] initWithFrame:CGRectMake(7, 0, 307, 30)];
head in the codes
  • 1,159
  • 12
  • 24
  • Are we able to see some code? Do you have anything going on in heightForRowAtIndexPath:? – Rowan Freeman Feb 01 '13 at 01:19
  • Cell view related code would be nice. To which view in the cell are you adding views, the cell itself, the contentView or the backgroundView? Your adding your own images that has a border? I did some tests and I think think separator style does not work with grouped tables, they always draw the separator. – Mattias Wadman Feb 05 '13 at 16:46
  • I have added all the code I can think of that might affect the table structure. I'd be happy to add anything else if required... PS. The hack in heightForRowAtIndexPath: is my current workaround. If the hack is removed, the gap re-appears – head in the codes Feb 06 '13 at 21:54
  • does the while line appear only if you add the backing image? what if you dont have any of the backing image to the cell? Since there is no irregular pattern in the image, why dont you set the color of the cell to [UIColor colorWithPattern:backingImage]; ? (I am also curious to know why the white line appears though) – Nitin Alabur Feb 06 '13 at 22:55
  • In this case, the border is included in the image, so a patterned image will result in unwanted effects. The hack to reduce the height of the cell seems to be a reasonable workaround, but it seems odd (and I want to know why) that in grouped table views, the first cell is 1 pixel higher. This happens irrespective of the image used - the image simply highlights the effect – head in the codes Feb 06 '13 at 23:48
  • How are you setting the x and y position of the whole table itself that is holding all of the rows? Since x and y positions have to be integers if you set a float like a y position of 17.5 instead of just 17 it may be rounding up to 18 but then when it creates the rows rounding down to 17 which lifts the first row up 1 pixel and after that the rows are set based on the previous row position (which is this third time rounded to 18 when it's calculated) so the error only happens once instead of alternat-ing-ly occurring. – Albert Renshaw Feb 12 '13 at 04:23
  • you said your background images are 30px height.. but check if all the cell background images has the same width... AND try this cell.backingImage.contentMode = UIViewContentModeScaleAspectFill; – chuthan20 Feb 13 '13 at 02:57
  • Did you ever get to the bottom of this? I am seeing a similar issue. – Imran Apr 18 '13 at 12:34
  • I'm afraid not. I have used the workaround mentioned above for now since I havev't got enough time to devote to the issue. I'll revisit it when I get some free time! – head in the codes Apr 24 '13 at 21:09

5 Answers5

3

This is kind of a known issue, only happens when you hide the separators of a grouped tableView, the exact same thing happened in this other stack overflow question.

Simply put, the top and lower cells use one extra point, but the separator hides it, so the solution is:

  1. Substract the additional spacing (as you already did)
  2. Make the background a repeating pattern or an stretchable image (Not trying to promote myself, but the answer I gave on the previous linked thread also explains how to do stretchable images).
  3. Don't hide the separator, but match is color with the background of the cells so it looks like its hidden.

Option 2 seems to be the "cleanest" and adds the benefit of supporting variable heights of tableView cells.

And why does it have that extra point? Only Apple knows.

Community
  • 1
  • 1
Can
  • 8,502
  • 48
  • 57
  • Three good solutions, of which #1 matches my requirements best. I'd rather have all the rows the same height, rather than stretching my backgrounds to fit that extra point. It is also worth noting here that I've not seen the bottom row exhibit the same behaviour - that row always seems to be 30 points, just like the rest. It's a shame to have to resort to such a dirty method, but as you say - Apple only knows why it's made like that... – head in the codes Jul 23 '13 at 00:32
1

In my experience, grouped table views add 1 point (not pixel!) to the height of the first and last row in each section. You should be able to see this by adding debug code to override layoutSubviews on your table cell class, have it call super and then print the bounds.size.height.

I presume this is to provide space for the border drawing and, like you, I have just adjusted the value I return for the height of the first and last cells to cancel out the effect. This may well change in iOS 7 since the visual appearance has changed.

[Edit] This is a nice bit of debug code to inspect view hierarchies. (You can't call recursiveDescription directly because it is private and your code won't compile under default Xcode settings.)

NSLog(@"%@", [self.view performSelector:@selector(recursiveDescription)]);

Geoff Hackworth
  • 2,673
  • 1
  • 16
  • 16
  • Please could you clarify the difference you mention between a point and a pixel? Do you mean that a point is the universal representation and shows as 2 pixels on a retina display? – head in the codes Jul 22 '13 at 18:24
  • Yes, that's exactly what I mean. You were measuring pixels by looking at your retina display. To adjust for the extra spacing you need to use tableView:heightForRowAtIndexPath: but provide the adjustment as points, not pixels. – Geoff Hackworth Jul 24 '13 at 13:37
1

If you do the following it will fix your problem: Get rid of the 'backingImage' (backingView). In stead use the cell's backgroundView property, just assign the UIImageView to that. Now when you create your UIImageView for the background make sure you make the UIImage stretchable like this:

UIImage *bg = [UIImage imageName:@"filename.png"];
bg = [bg resizableImageWithCapInsets:UIEdgeInsetsMake(15, 0, 15, 0)];

NOTE: I'm assuming your image has a height of 30 points. Looking at the image you're using it can be stretched vertically, what I'm doing in this example. To save more memory you could also stretch it horizontally, but the asset you have may not lend itself for that.

Hope this helps!

Marco Tolman
  • 321
  • 2
  • 11
  • This is a good answer, but unfortunately not what I'm after. While this will cover the gap, I am still left with the top row being 1 point bigger than the other rows... – head in the codes Jul 23 '13 at 00:27
1

On iOS 8 with no seperators on a grouped tableview I had the problem as well. There was a 0.5f point gap between my sections. Since the background behind my tableview was not the same color, it was very noticeable. The solution is to fill with a blank headerview 1 px high, then spill out of it by 1 px with another view. Works like a charm.

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 1)];
    view.backgroundColor = [UIColor whiteColor];
    view.opaque = YES;
    view.clipsToBounds = NO;

    //Spill a view out by one pixel.
    UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 2)];
    subView.backgroundColor = [UIColor whiteColor];
    subView.opaque = YES;
    [view addSubview:subView];
    return view;
}

- (CGFloat)tableView:(UITableView *)tableView
heightForHeaderInSection:(NSInteger)section {
    return 1;
}
Josh Bernfeld
  • 4,246
  • 2
  • 32
  • 35
0

This is screaming section headers to me. Are you overriding the viewForHeaderInSection method? My best guess is that the bounds for your first cell in each section is somehow eliding with the bounds for the header of that section.

Try overriding these methods:

– tableView:viewForHeaderInSection:
– tableView:viewForFooterInSection:
– tableView:heightForHeaderInSection:
– tableView:heightForFooterInSection:
– tableView:willDisplayHeaderView:forSection:
– tableView:willDisplayFooterView:forSection:

and play with what they return. Especially heightForHeaderInSection / viewForHeaderInSection / willDisplayHeaderView:ForSection:

Mark Pauley
  • 1,445
  • 12
  • 11
  • I have tried overriding those methods, but that doesn't seem to have any effect. From Can and Geoff's answers above, it seems this is a known issue and not due to these delegate method's returns... – head in the codes Jul 23 '13 at 00:41