Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

1 pixel gap between first and second cell in grouped UITableView

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)];
like image 753
head in the codes Avatar asked Jan 31 '13 23:01

head in the codes


4 Answers

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.

like image 52
Can Avatar answered Nov 16 '22 14:11

Can


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)]);

like image 1
Geoff Hackworth Avatar answered Nov 16 '22 13:11

Geoff Hackworth


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!

like image 1
Marco Tolman Avatar answered Nov 16 '22 12:11

Marco Tolman


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;
}
like image 1
Josh Bernfeld Avatar answered Nov 16 '22 13:11

Josh Bernfeld