51

I have a problem with separators between UITableViewCells in UITableView on iOS 9. They have the significant left margin. I already have code for removing spacing introduced by iOS 8 but it doesn't work with iOS 9. It looks like they added something else. I suppose it might be connected with layoutMarginsGuide but I haven't figured it out yet. Does anyone had a similar problem and found out the solution?

Julian
  • 9,299
  • 5
  • 48
  • 65

11 Answers11

70

Okay, I have found out the solution. The only thing required for that is to set on the presenting instance of UITableView that flag cellLayoutMarginsFollowReadableWidth

myTableView.cellLayoutMarginsFollowReadableWidth = NO;

I wanted to find some reference in the documentation but it looks like it is not ready yet, only mentioned on diff page.

As the flag was introduced in iOS 9 for the backward compatibility you should add a check before trying to set it:

if([myTableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)])
{
    myTableView.cellLayoutMarginsFollowReadableWidth = NO;
}

For Swift 2.0 you can use #available to check iOS version.

if #available(iOS 9, *) {
    myTableView.cellLayoutMarginsFollowReadableWidth = false
}

Moreover you need to compile it with Xcode 7 or above.

EDIT

Please keep in mind that this is the only required fix if your separators looked "fine" up to iOS 8, otherwise you need to change a bit more. You can find info how to do this already on SO.

Community
  • 1
  • 1
Julian
  • 9,299
  • 5
  • 48
  • 65
  • Thanks for that! is there any option to turn this feature off for the whole app or do I have to manually Update all TableViewController properties? – JonEasy Oct 19 '15 at 13:39
  • Have you considered creating a base class for tableview (set it there) and use it wherever possible? Moreover I would look if it can be set by appearance. – Julian Oct 19 '15 at 13:46
  • 1
    yes this is what I'm doing right now (base class approach) I was just wondering if there would be some kind of "global switch" to shut this off completely. I tried the appearance stuff but this doesn't work with `[[UITableView appearance] setCellLayoutMarginsFollowReadableWidth:NO];` – JonEasy Oct 20 '15 at 07:41
  • Maybe that could be a next SO question how (if possible) to set it globally :) – Julian Oct 20 '15 at 07:42
  • 2
    Doesn't seem to have any effect on iOS 9.1. –  Dec 14 '15 at 09:08
  • @ElgsQianChen has it worked for you for previous versions? – Julian Dec 14 '15 at 11:31
  • @JulianKról I haven't tried with any earlier version yet. –  Dec 14 '15 at 12:27
  • So this is only the addition you need if your separators where fine on iOS 8. This is what changed between iOS 8 and 9. Find how to fix it on iOS 8 (it is already on SO) and add this and you are done. – Julian Dec 14 '15 at 12:31
  • Thanks for this! Any idea why, on iPad, this code makes a full width separator on empty cells, but when data is added to a cell there are a few pixels on the left side where the separator is missing? I found a patch/fix for this but just wondering if you may know more. – RanLearns May 13 '17 at 02:30
32

If you want to do it in interface builder. The default separator inset is Automatic. Change it to custom by selecting the dropdown.

enter image description here

ArunGJ
  • 2,685
  • 21
  • 27
18

Swift 2.2 iOS 9.3

In viewDidLoad

tableView.cellLayoutMarginsFollowReadableWidth = false

In UITableViewDelegates

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    if cell.respondsToSelector(Selector("setSeparatorInset:")){
        cell.separatorInset = UIEdgeInsetsZero
    }
    if cell.respondsToSelector(Selector("setPreservesSuperviewLayoutMargins:")) {
        cell.preservesSuperviewLayoutMargins = false
    }
    if cell.respondsToSelector(Selector("setLayoutMargins:")){
        cell.layoutMargins = UIEdgeInsetsZero
    }
}
ahajib
  • 12,838
  • 29
  • 79
  • 120
Huy Vu
  • 404
  • 3
  • 12
12

Swift 3.0 / 4.0

tableView.cellLayoutMarginsFollowReadableWidth = false

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if cell.responds(to: #selector(setter: UITableViewCell.separatorInset)) {
        cell.separatorInset = UIEdgeInsets.zero
    }
    if cell.responds(to: #selector(setter: UIView.preservesSuperviewLayoutMargins)) {
        cell.preservesSuperviewLayoutMargins = false
    }
    if cell.responds(to: #selector(setter: UIView.layoutMargins)) {
        cell.layoutMargins = UIEdgeInsets.zero
    }
}
derdida
  • 14,784
  • 16
  • 90
  • 139
11

Perfect Solution upto iOS 9

In viewDidLoad

- (void)viewDidLoad {
    [super viewDidLoad];
    //Required for iOS 9
    if ([[[UIDevice currentDevice]systemVersion]floatValue] >= 9.0) {
        self.testTableView.cellLayoutMarginsFollowReadableWidth = NO;
    }
}

In TableViewDelegate methods add following code:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

    // Remove seperator inset
    if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
        [cell setSeparatorInset:UIEdgeInsetsZero];
    }

    // Prevent the cell from inheriting the Table View's margin settings
    if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) {
        [cell setPreservesSuperviewLayoutMargins:NO];
    }

    // Explictly set your cell's layout margins
    if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
        [cell setLayoutMargins:UIEdgeInsetsZero];
    }
}
Bhuvan Bhatt
  • 3,276
  • 2
  • 18
  • 23
  • First of all, if you copy code from the other answer on SO it would be nice if you mention the original author with a link to its answer (that would be fair). Secondly instead of checking iOS version you can check whether object responds to selector. – Julian Dec 02 '15 at 16:23
  • @JulianKról You are right about respond to selector its an alternative. And this is my solution I have created a demo project for this too and working properly on all iOS versions. It would really feel great if you appreciate. – Bhuvan Bhatt Dec 02 '15 at 16:28
  • yeah but the second part of the code I already seen on the SO with same comments etc. Moreover making every thing bold and italic also do not improves its readability :) Further, the question is explicit about iOS 9 assuming that up to iOS 8 everything was already done (that counts to the second part of your answer). I appreciate yours involvement but I'm giving you my feedback – Julian Dec 02 '15 at 16:29
  • Yeah that was actually I was using till iOS 8. But for iOS 9 besides this code one must set self.testTableView.cellLayoutMarginsFollowReadableWidth = NO . So for the proper answer I have mentioned the complete code. – Bhuvan Bhatt Dec 02 '15 at 16:31
  • 1
    what about link to the original answer on SO ? you didn't say anything about that :) – Julian Dec 02 '15 at 16:38
  • @JulianKról Ok noted. Will keep this in mind for future. Thnks. – Bhuvan Bhatt Dec 02 '15 at 16:38
5

This worked perfectly for me in iOS 9.

For OBJ-C

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath  { 
        if ([tableView respondsToSelector:@selector(setSeparatorInset:)])
        {
            [tableView setSeparatorInset:UIEdgeInsetsZero];
        }

        if ([tableView respondsToSelector:@selector(setLayoutMargins:)])
        {
            [tableView setLayoutMargins:UIEdgeInsetsZero];
        }

        if ([cell respondsToSelector:@selector(setLayoutMargins:)])
        {
            [cell setLayoutMargins:UIEdgeInsetsZero];
        }
         return cell;
    }
jithin
  • 459
  • 8
  • 21
5

Based on different answers here, I am able to remove the gap from separator with these lines of codes in Swift:

tableView.separatorInset = UIEdgeInsetsZero
tableView.layoutMargins = UIEdgeInsetsZero
cell.separatorInset = UIEdgeInsetsZero
cell.layoutMargins = UIEdgeInsetsZero

But still I am having this small gap before the text:

enter image description here

codelearner
  • 1,354
  • 1
  • 16
  • 32
  • As a workaround, I have just put constraint of `-14` for `UITableView` instead of using above codes to align the table manually. – codelearner Mar 29 '16 at 09:43
3

The accepted answer did not work for me. Until I moved setCellLayoutMarginsFollowReadableWidth BEFORE setLayoutMargins (still needed for iOS 8):

if([_tableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) {
  _tableView.cellLayoutMarginsFollowReadableWidth = NO;
}

if ([_tableView respondsToSelector:@selector(setLayoutMargins:)]) {
  _tableView.layoutMargins = UIEdgeInsetsZero;
}
Amal T S
  • 3,327
  • 2
  • 24
  • 57
Donnit
  • 1,217
  • 12
  • 19
  • this is what I needed! The order matters. For me, I noticed that the order mattered for iPad table views in landscape. Once rotated it was fixed, but the first sight of the table was wrong. Thank you! – manroe Nov 11 '16 at 19:49
1
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

    // Remove seperator inset

    if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
        [cell setSeparatorInset:UIEdgeInsetsZero];
    }

    // Prevent the cell from inheriting the Table View's margin settings

    if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) {
        [cell setPreservesSuperviewLayoutMargins:NO];
    }

    // Explictly set your cell's layout margins

    if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
        [cell setLayoutMargins:UIEdgeInsetsZero];
    }

}
Mihawk
  • 581
  • 4
  • 12
  • Not in my case. This was working fine up to the version 8 of iOS. Something else seems to be required for iOS 9. – Julian Jul 21 '15 at 11:13
1

For iOS 8 and 9

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if ([UITableView instancesRespondToSelector:@selector(setLayoutMargins:)]) [[UITableViewCell appearance] setLayoutMargins:UIEdgeInsetsZero];
    if ([UITableView instancesRespondToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) [[UITableViewCell appearance] setPreservesSuperviewLayoutMargins:NO];
}

and

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([cell respondsToSelector:@selector(setLayoutMargins:)]) [cell setLayoutMargins:UIEdgeInsetsZero];
}
0

This is my solution for Swift 3.0/iOS 10 in XCode 8.2.1.

I have created a subclass for UITableview which works for IB and programmatically create tableviews.

import UIKit

class EXCSuperTV: UITableView
{
    required init?(coder aDecoder: NSCoder)
    {
        super.init(coder: aDecoder)
        setupView()
    }

    override init(frame: CGRect, style: UITableViewStyle)
    {
        super.init(frame: frame, style: style)
        setupView()
    }

    func setupView() {}
}

class EXCNoFooter: EXCSuperTV
{
    override func setupView()
    {
        super.setupView()
        //tableFooterView = UIView.Zero()
    }
}


class EXCMainTV: EXCNoFooter
{
    override func setupView()
    {
        super.setupView()
        separatorInset = UIEdgeInsets.zero
    }
}
Darkwonder
  • 1,149
  • 1
  • 13
  • 26