32

The grouped UITableView places a margin between the edge of the view and the table cells. Annoyingly (for me) this margin is some function of the width of the view.

In my application I have two UITableViews of different widths that I am looking to align the cell edges of.

Is it possible to get this margin? Or better is it possible to set this margin?

cheers, --Ben

user577814
  • 387
  • 1
  • 3
  • 6

9 Answers9

54

Grouped TableView Width in Relation to Cell Margin

TBWidth (0 to 20) Left Margin = TBWidth - 10

TBWidth (20 to 400) Left Margin = 10

TBWidth (401 to 546) Left Margin = 31

TBWidth (547 to 716) Left Margin = apx 6% of tableView

TBWidth (717 to 1024) Left Margin = 45

- (float) groupedCellMarginWithTableWidth:(float)tableViewWidth
{
    float marginWidth;
    if(tableViewWidth > 20)
    {
        if(tableViewWidth < 400)
        {
            marginWidth = 10;
        }
        else
        {
            marginWidth = MAX(31, MIN(45, tableViewWidth*0.06));
        }
    }
    else
    {
        marginWidth = tableViewWidth - 10;
    }
    return marginWidth;
}
Matthew Thomas
  • 549
  • 1
  • 3
  • 3
  • 1
    Confirmed that this works. How did you find this out? You are amazing!! – bentford Mar 08 '11 at 02:03
  • Yeah this definitely worked.... but its sad that this is the best way I've found to do it haha. – tomwilson Aug 23 '11 at 03:40
  • 4
    For iPhone, when the device orientation is landscape, the margin is still 10. So the 6th line of the above code should check it the device is iPhone also. In Monotouch this is how it works: if(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone || tableViewWidth < 400) – Yiannis Mpourkelis Jun 20 '12 at 20:01
  • thanks anyway, I was really baffled on why my custom UITableViewCell was being misaligned (the content started in the margin, instead of the white area of the grouped table view) , since i was designing it in a blank xib designer view instead of using the prototype view in the storyboard, so i didn't realize you needed to basically shift everything over by x pixels (which you list). Thanks! – mgrandi Oct 31 '12 at 20:33
  • Does anyone know the analogous calculation for top and bottom margins? – Joshua Frank Apr 27 '13 at 17:55
  • @Matthew Thomas I am totally blown away by this answer. And I thought I had exhausted every piece of research on UITableView, UITableViewCell. I have been going through all the official Apple Docs for days trying to find the holy grail, like UITableViewCell.marginLeft, but to no avail. Although god only knows why Apple cannot just give us this property. But thank you so much for this brilliant answer, and nice piece of Math at marginWidth = MAX(31, MIN(45, tableViewWidth*0.06)). I am up voting this answer:) – Charles Robertson Nov 17 '16 at 15:19
  • 1
    @Yiannis Mpourkelis Thanks for this addition, and for people using objective c: if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone || tableViewWidth < 400) – Charles Robertson Nov 17 '16 at 15:23
18

By creating (and using!) a subclass of UITableViewCell, you can possibly achieve what you're looking for. Just move the contentview and backgroundview around in layoutsubviews, as in the code sample below which shifts the visible parts of the cell 20pt to the right.

- (void) layoutSubviews
{
    NSLog (@"Cell/contentview/backgroundview before:\n%@\n%@\n%@", self, self.contentView, self.backgroundView);
    [super layoutSubviews];
    CGRect frame = self.backgroundView.frame;
    frame.origin.x += 20;
    frame.size.width -= 20;
    self.backgroundView.frame = frame;
    frame = self.contentView.frame;
    frame.origin.x += 20;
    frame.size.width -= 20;
    self.contentView.frame = frame;
    
    NSLog (@"Cell/contentview/backgroundview after:\n%@\n%@\n%@", self, self.contentView, self.backgroundView);
}
Community
  • 1
  • 1
Steven Kramer
  • 8,473
  • 2
  • 37
  • 43
12

I was using Matthew Thomas implementation but it's unreliable (it's broken if you use autorotation in your controllers) and has a lot of hardcoded code... I realized that there is a very simple solution, my implementation is a single line of code:

- (float)cellMargins
{
    return self.backgroundView.frame.origin.x * 2;
}

This is far better IMO :)

EDIT: my implementation was unreliable too (it does not work properly when switching to/from editing mode), this is my final implementation (I handled right and left margins separately):

- (float)leftMargin
{
    return self.contentView.frame.origin.x;
}

- (float)rightMargin
{
    CGRect frame = self.contentView.frame;
    float containerWidth = frame.size.width;
    float margin = self.frame.size.width - (containerWidth + frame.origin.x);
    return margin;
}

- (float)cellMargins
{
    return ([self leftMargin] + [self rightMargin]);
}
Community
  • 1
  • 1
daveoncode
  • 18,900
  • 15
  • 104
  • 159
  • 5
    Your solution looks great but can't be used in cases when you need to known margins before the cells are created – flagg19 Sep 06 '12 at 08:46
6

Updated Matthew Thomas's code for cell margins calculation. Created via UITableView's category. This can be useful, when you need to determine text height in cell, and you dont' know width of that cell.

So, actual cell can be calculated like

cell.width = tableView.width - tableView.cellsMargin * 2;

An here's the code

@implementation UITableView (CellsMargins)

// This is black magic
// from
// http://stackoverflow.com/questions/4708085/how-to-determine-margin-of-a-grouped-uitableview-or-better-how-to-set-it

- (CGFloat)cellsMargin {

   // No margins for plain table views
   if (self.style == UITableViewStylePlain) {
      return 0;
   }

   // iPhone always have 10 pixels margin
   if (! isIPad()) {
      return 10;
   }

   CGFloat tableWidth = self.frame.size.width;

   // Really small table
   if (tableWidth <= 20) {
      return tableWidth - 10;
   }

   // Average table size
   if (tableWidth < 400) {
      return 10;
   }

   // Big tables have complex margin's logic
   // Around 6% of table width,
   // 31 <= tableWidth * 0.06 <= 45

   CGFloat marginWidth  = tableWidth * 0.06;
   marginWidth = MAX(31, MIN(45, marginWidth));
   return marginWidth;
}

@end
Community
  • 1
  • 1
tt.Kilew
  • 5,954
  • 2
  • 33
  • 51
2

This is now accessible through a property of UITableViewCell:

@property(nonatomic) UIEdgeInsets separatorInset

Apple Docs - UITableViewCell - seperatorInset

Community
  • 1
  • 1
DylanVann
  • 483
  • 5
  • 9
  • And by creating a subclass of UILabel ala [this method](http://stackoverflow.com/questions/3476646/uilabel-text-margin) using instead a property of `insets` you define, you can easily set up your own inset to a custom label in `tableView:viewForHeaderInSection:` or similar with code like this: `label.insets = tableview.separatorInset;` – henryaz Oct 13 '14 at 15:25
1

More accurate left margin for the 'stretchy' zone instead of the 'apx. 6% calc.'

Side note: the iPad implements additional section top and bottom padding in addition to the left margin padding, it's 10.0f below 400.0f table width and 31.0f otherwise.

-(CGFloat)leftMarginForTableView:(UITableView*)tableView
{
    if (tableView.style != UITableViewStyleGrouped) return 0;
    CGFloat widthTable = tableView.bounds.size.width;
    if (isPhone)              return (10.0f);
    if (widthTable <= 400.0f) return (10.0f);
    if (widthTable <= 546.0f) return (31.0f);
    if (widthTable >= 720.0f) return (45.0f);
    return (31.0f + ceilf((widthTable - 547.0f)/13.0f));
}
Community
  • 1
  • 1
The Rat
  • 955
  • 7
  • 10
1

I would like to supplement Matthew's answer with a code to set-up fixed margin.

  1. Subclass UITableViewCell.
  2. Implement setFrame:
- (void)setFrame:(CGRect)frame
{
    CGFloat inset = 15.0;
    CGFloat tableWidth = 400.0;
    frame.origin.x -= [self groupedCellMarginWithTableWidth:tableWidth] - inset;
    frame.size.width = frame.size.width + 2 * ([self groupedCellMarginWithTableWidth:tableWidth] - inset);
    [super setFrame:frame];
}

This will set left and right margin for 15.0 while table width may vary.

Community
  • 1
  • 1
Vladimir Obrizan
  • 2,538
  • 2
  • 18
  • 36
0

I have tried a different approach. But not sure whether it always work. I had a grouped UITableView and needed to make customized Header View. But as you know, when implementing viewForHeader inSection method we can not determine the margins for our created view. So, what I have done is firstly add a CGRect property to ViewController .m file:

@property(nonatomic)CGRect groupedCellRectangle;

and in UITableViewDelegate:

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
    static BOOL groupFrameInitialized = NO;
    if(!groupFrameInitialized){
        groupFrameInitialized = YES;
        self.groupedCellRectangle = cell.contentView.frame;
    }
}

after then, in my viewForHeader inSection method:

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
  UIView *v = [[UIView alloc] initWithFrame:CGRectMake(self.groupedCellRectangle.origin.x, 0, self.groupedCellRectangle.size.width, 28)];
  [v setBackgroundColor:[UIColor clearColor]];
  UILabel *label = [[UILabel alloc] initWithFrame:v.frame];
  label.text = @"KAF";
  [v addSubview:label];
  return v;
}

the result is as I assumed. groupedCellRectangle had successfully stored CGRect value with margin.

The idea behind this approach was UITableView always call viewForHeader method after willDisplay call.

Hope that helps...

madiden
  • 11
  • 2
0

Just in case anyone needs this, the function for the section title left margin when obtained from tableView:titleForHeaderInSection: seems to be:

- (float)marginForSectionTitleOnGroupedTableView {
   float width = self.width;

   if (width <= 400) return 19;
   else if (width >= 401 && width < 547) return 40;
   else if (width >= 547 && width < 560) return 41;
   else if (width >= 560 && width < 573) return 42;
   else if (width >= 573 && width < 586) return 43;
   else if (width >= 586 && width < 599) return 44;
   else if (width >= 599 && width < 612) return 45;
   else if (width >= 612 && width < 625) return 46;
   else if (width >= 625 && width < 639) return 47;
   else if (width >= 639 && width < 652) return 48;
   else if (width >= 652 && width < 665) return 49;
   else if (width >= 665 && width < 678) return 50;
   else if (width >= 678 && width < 691) return 51;
   else if (width >= 691 && width < 704) return 52;
   else if (width >= 704 && width < 717) return 53;

   // if (width >= 717) 
   return 54;
}

For widths greater than 401, the margin seems to be around 7.5% of the table view width; but when trying to align some other view with the section title, the alignment didn't work as expected; so the verbose approach seems to work better.

  • I used this which works well enough for the iPad but for iPhone the margins when landscape (and therefore over 401px) are still 19px. Just adding the extra device check works great though. – Daniel Wood Jun 06 '12 at 10:40