I have an app where the UITableView
\'s separator inset is set to custom values - Right 0
, Left 0
. This works perfectly in iOS 7.
As to what cdstamper suggested instead of the table view, adding below lines in the cell's layoutSubview method works for me.
- (void)layoutSubviews
{
[super layoutSubviews];
if ([self respondsToSelector:@selector(setSeparatorInset:)])
[self setSeparatorInset:UIEdgeInsetsZero];
if ([self respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)])
{
[self setPreservesSuperviewLayoutMargins:NO];;
}
if ([self respondsToSelector:@selector(setLayoutMargins:)])
{
[self setLayoutMargins:UIEdgeInsetsZero];
}
}
iOS 8.0 introduces the layoutMargins property on cells AND table views.
This property isn't available on iOS 7.0 so you need to make sure you check before assigning it!
The easy fix is to subclass your cell and override the layout margins property as suggested by @user3570727. However you will lose any system behavior like inheriting margins from the Safe Area so I do not recommend the below solution:
(ObjectiveC)
-(UIEdgeInsets)layoutMargins {
return UIEdgeInsetsZero // override any margins inc. safe area
}
(swift 4.2):
override var layoutMargins: UIEdgeInsets { get { return .zero } set { } }
If you don't want to override the property, or need to set it conditionally, keep reading.
In addition to the layoutMargins
property, Apple has added a property to your cell that will prevent it from inheriting your Table View's margin settings. When this property is set, your cells are allowed to configure their own margins independently of the table view. Think of it as an override.
This property is called preservesSuperviewLayoutMargins
, and setting it to NO
will allow the cell's layoutMargin
setting to override whatever layoutMargin
is set on your TableView. It both saves time (you don't have to modify the Table View's settings), and is more concise. Please refer to Mike Abdullah's answer for a detailed explanation.
NOTE: what follows is a clean implementation for a cell-level margin setting, as expressed in Mike Abdullah's answer. Setting your cell's preservesSuperviewLayoutMargins=NO
will ensure that your Table View does not override the cell settings. If you actually want your entire table view to have consistent margins, please adjust your code accordingly.
Setup your cell margins:
-(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];
}
}
Swift 4:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// Remove seperator inset
if cell.responds(to: #selector(setter: UITableViewCell.separatorInset)) {
cell.separatorInset = .zero
}
// Prevent the cell from inheriting the Table View's margin settings
if cell.responds(to: #selector(setter: UITableViewCell.preservesSuperviewLayoutMargins)) {
cell.preservesSuperviewLayoutMargins = false
}
// Explictly set your cell's layout margins
if cell.responds(to: #selector(setter: UITableViewCell.layoutMargins)) {
cell.layoutMargins = .zero
}
}
Setting the preservesSuperviewLayoutMargins
property on your cell to NO should prevent your table view from overriding your cell margins. In some cases, it seems to not function properly.
If all fails, you may brute-force your Table View margins:
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
// Force your tableview margins (this may be a bad idea)
if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
[self.tableView setSeparatorInset:UIEdgeInsetsZero];
}
if ([self.tableView respondsToSelector:@selector(setLayoutMargins:)]) {
[self.tableView setLayoutMargins:UIEdgeInsetsZero];
}
}
Swift 4:
func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Force your tableview margins (this may be a bad idea)
if tableView.responds(to: #selector(setter: UITableView.separatorInset)) {
tableView.separatorInset = .zero
}
if tableView.responds(to: #selector(setter: UITableView.layoutMargins)) {
tableView.layoutMargins = .zero
}
}
...and there you go! This should work on iOS 7 and 8.
EDIT: Mohamed Saleh brought to my attention a possible change in iOS 9. You may need to set the Table View's cellLayoutMarginsFollowReadableWidth
to NO
if you want to customize insets or margins. Your mileage may vary, this is not documented very well.
This property only exists in iOS 9 so be sure to check before setting.
if([myTableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)])
{
myTableView.cellLayoutMarginsFollowReadableWidth = NO;
}
Swift 4:
if myTableView.responds(to: #selector(setter: self.cellLayoutMarginsFollowReadableWidth)) {
myTableView.cellLayoutMarginsFollowReadableWidth = false
}
(above code from iOS 8 UITableView separator inset 0 not working)
EDIT: Here's a pure Interface Builder approach:
NOTE: iOS 11 changes & simplifies much of this behavior, an update will be forthcoming...
Here's the only way to fully control this stuff (that I could find)
To fully control both separator insets and layout margins on each cell. Do this in the willDisplayCell
method on your UITableviewDelegate
.
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
cell.layoutMargins = UIEdgeInsetsZero
cell.contentView.layoutMargins = UIEdgeInsetsMake(0, 10, 0, 10)
cell.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
The cell object controls the separator, and the contentView
controls everything else. If your separator inset spaces are showing up in an unexpected color this should solve it:
cell.backgroundColor = cell.contentView.backgroundColor
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ... Get the cell
cell.separatorInset = UIEdgeInsetsMake(0.f, 20.f, 0.f, [UIScreen mainScreen].bounds.size.width - 20);
// others
return cell;
}
For any specific cell you want to hide the separator.
After having seen the answers at floor 3, I tried to figure out what the relationship of setting up the separator between TableView & TableViewCell and did some test. Here are my conclusions:
we can consider that setting the cell's separator to zero has to move the separator in two steps: first step is to set cell's separatorinset to zero. second step is to set cell's marginlayout to zero.
set the TableView's separatorinset and marginlayout can affect the Cell's separatorinset. However, from the test, I find that the TableView's separatorinset seem to be useless, TableView's marginlayout can actually affect cell's marginlayout.
set Cell's PreservesSuperviewLayoutMargins = false, can cut off TableView's marginlayout effect on Cells.
one of the solutions:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
cell.preservesSuperviewLayoutMargins = false
cell.separatorInset = UIEdgeInsetsZero
cell.layoutMargins = UIEdgeInsetsZero
return cell
}
Swift:
override func viewDidLoad() {
super.viewDidLoad()
if self.tableView.respondsToSelector("setSeparatorInset:") {
self.tableView.separatorInset = UIEdgeInsetsZero
}
if self.tableView.respondsToSelector("setLayoutMargins:") {
self.tableView.layoutMargins = UIEdgeInsetsZero
}
self.tableView.layoutIfNeeded() // <--- this do the magic
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
if cell.respondsToSelector("setSeparatorInset:") {
cell.separatorInset = UIEdgeInsetsZero
}
if cell.respondsToSelector("setLayoutMargins:") {
cell.layoutMargins = UIEdgeInsetsZero
}
return cell
}