82

In my app, I use a UITableView My problem is that I want to remove the last border of the last cell in UITableView.

Please check the following image:

enter image description here

Arnaud
  • 7,259
  • 10
  • 50
  • 71

21 Answers21

141

The best solution is add a footer view. The following code hide the last cell's line perfectly:

Objective-C

self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 1)];

Swift 4.0

tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 1))
grg
  • 5,023
  • 3
  • 34
  • 50
Tonny Xu
  • 2,162
  • 2
  • 16
  • 20
  • 14
    Works only when you have one section – GoodSp33d Dec 04 '14 at 15:22
  • @GoodSp33d I have multiple sections and it works by doing it this way `if(indexPath.row == [self.tableView numberOfRowsInSection:indexPath.section] - 1) { self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 1)]; }` In the `CellForRowAtIndexPath` datasource – Josh Valdivieso Dec 04 '14 at 15:25
  • My bad commented early. Adding only that line, sets the default height to 44 px I guess, so along with that line of code i ahd to pass `1.0f` as footer height in `-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section` delegate. Now it works like a charm. Upvoted – GoodSp33d Dec 04 '14 at 15:28
  • It is incredible that nowadays this hack is still needed... – javi_swift Feb 18 '22 at 14:10
  • @javi_swift 2022 – hasan Aug 02 '22 at 08:41
44

In iOS 7 there is an easier solution. Supposing cell is your last cell:

cell.separatorInset = UIEdgeInsetsMake(0, cell.bounds.size.width, 0, 0);
davidisdk
  • 3,358
  • 23
  • 12
25

Updated on 9/14/15. My original answer become obsolete, but it is still a universal solution for all iOS versions:

You can hide tableView's standard separator line, and add your custom line at the top of each cell. The easiest way to add custom separator is to add simple UIView of 1px height:

UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width, 1)];
separatorLineView.backgroundColor = [UIColor grayColor];
[cell.contentView addSubview:separatorLineView];

To date, I subscribe to another way for hiding extra separators below cells (works for iOS 6.1+):

self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
Community
  • 1
  • 1
onegray
  • 5,105
  • 1
  • 23
  • 29
  • 6
    The CGRectZero method did not work for me. However the other answer below which sets the frame to CGRectMake(0, 0, self.tableView.frame.size.width, 1) did work. – Ecuador Jan 13 '16 at 20:54
  • Genius answer! saved my day. – Rohit Mandiwal Dec 12 '16 at 01:26
  • 1
    Ecuador's comment is right. Answer is not working on iOS 12 and should not be marked as correct as it just removes the separators from empty cells. – heyfrank Jan 21 '19 at 13:17
13

This works for me in iOS 7 and 8:

cell.separatorInset = UIEdgeInsetsMake(0, 0, 0, CGRectGetWidth(tableView.bounds));

NOTE: be careful about using tableView.bounds as it sometimes reports the wrong value depending on when you call it. See Reporting incorrect bounds in landscape Mode

Community
  • 1
  • 1
Jamie Forrest
  • 10,895
  • 6
  • 51
  • 68
  • Works perfect in iOS8 too. Thanks – Tim Dec 02 '14 at 02:53
  • 1
    I used this to avoid strange behavior on 5.5 inch screen: `cell!.separatorInset = UIEdgeInsetsMake(0, 0, 0, CGRectGetWidth(self.tableView.bounds)-self.view.layoutMargins.left)` – Camillo Mar 03 '15 at 13:16
  • When the orientation is first in portrait, then in landscape, it appears the line – pableiros Sep 08 '16 at 16:47
12

Add this line of code in viewDidLoad().

tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0.001))

This make sure the last separator will be replaced and replaced (invisible) footer view will not occupy any extra height. Since the footer view's width will be managed by UITableView, so you can set it to 0.

Jonny
  • 1,969
  • 18
  • 25
  • 1
    The only solution that worked for me, the `.zero` left one separator at the bottom of the table. – Ido Nov 14 '18 at 17:35
  • @Ido hello, just to let you know that I I rolled back your edit version because I don't think the `CGRect` extension you added is a great practice. If you don't like those cumbersome `0` in the sample code, you can write something like this: `var frame = CGRect.zero \n frame.size.height = 0.001` – Jonny Nov 15 '18 at 03:15
  • May I ask why isn't it a good practice? It's let you use the same way you can use with `.zero`, but use actual zero (0.001) which do what you need. – Ido Nov 15 '18 at 05:59
  • Because it's not flexible enough (what if one day you want a frame with 0.001 width?), and the property name `CGRect.myZero` doesn't provide much of sense of what it can do. – Jonny Nov 15 '18 at 07:43
  • You can call it `almostZero` if thats what making it not flexible. You can add some comment above the `static let ...` to describe it (with 3 / so you'll be able to see it via the xCode's autocomplete interface). – Ido Nov 15 '18 at 10:16
7

Extension based solution for Swift 4, tested on iOS 12

I noticed that setting a empty view of height = 1 also removes the separator of the last visible cell however setting height = 0 just removes the separator of the empty cells

extension UITableView {
    func removeSeparatorsOfEmptyCells() {
        tableFooterView = UIView(frame: .zero)
    }

    func removeSeparatorsOfEmptyCellsAndLastCell() {
        tableFooterView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 0, height: 1)))
    }
}
heyfrank
  • 5,291
  • 3
  • 32
  • 46
3

My shorter version:

self.tblView.tableFooterView = [UIView new];
3

In Swift simply:

tableView.tableFooterView = UIView()
DungeonDev
  • 1,176
  • 1
  • 12
  • 16
2

Here is the remedy I currently use. it might not be the best approach, but it works.

Remove SeparatorColor and setSeparatorStyle to make custom separator

- (void)viewDidLoad
{
    [super viewDidLoad];

    ...

    [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
    [self.tableView setSeparatorColor:[UIColor clearColor]];
}

Add custom separator to each cell with tableview background

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    ...

    UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(0, cell.frame.size.height-1, 320, 1)];
    separatorLineView.backgroundColor = self.tableView.backgroundColor;
    [cell.contentView addSubview:separatorLineView];

    return cell;
}
Hiro
  • 353
  • 5
  • 12
  • 1
    This won't work with cells with custom height - at the point where you're fetching the cell's height, the cell hasn't been completely laid out. Also, you shouldn't be using hard coded values like 320 - how does this look on an iPhone 6? – Zorayr Apr 26 '15 at 02:36
1
_tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
Şafak Gezer
  • 3,928
  • 3
  • 47
  • 49
ohana
  • 37
  • 10
  • 3
    Hi and welcome to stackoverflow, and thank you for answering. While this code might answer the question, can you consider adding some explanation for what the problem was you solved, and how you solved it? This will help future readers to understand your answer better and learn from it. – Plutian Feb 18 '20 at 09:52
1

For iOS 7 and above

    -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
    {
        BOOL bIsLastRow = NO;

        //Add logic to check last row for your data source
        NSDictionary *dict = [_arrDataSource objectAtIndex:indexPath.section];
        if([_arrDataSource lastObject] == dict)
        {
           bIsLastRow = YES;
        }

        //Set last row separate inset left value large, so it will go outside of view
        if ([cell respondsToSelector:@selector(setSeparatorInset:)]) 
        {
            [cell setSeparatorInset:UIEdgeInsetsMake(0, bIsLastRow ? 1000 :0, 0, 0)];
        }
    }
Aditya Deshmane
  • 4,676
  • 2
  • 29
  • 35
1

Below code helps me to remove the separator from last row in a UITableView.

Swift 3.0

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if indexPath.row == tableView.numberOfRows(inSection: indexPath.section) {
        cell.separatorInset.right = cell.bounds.size.width
    }
}
juanjo
  • 3,737
  • 3
  • 39
  • 44
user2991638
  • 111
  • 1
  • 6
1

Swift 4.2

To get a separator that goes from left to right in your last cell, add this in your UITableViewDataSource's tableView(_:cellForRowAt:) implementation:

if tableView.numberOfRows(inSection: indexPath.section) - 1 == indexPath.row {
        cell.separatorInset = .zero
}

If you don't want a separator for one particular cell, consider drawing your own separator and set tableView.separatorStyle = .none.

JanApotheker
  • 1,746
  • 1
  • 17
  • 23
1

Xcode 12, Swift 5

for removing separator after last cell, select grouped UITableView style You can do that in Interface Builder or setup it in viewDidLoad

tableView.style = UITableView.Style.grouped

remove empty space before first cell, write this code in viewDidLoad:

tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 0.1))

for removing empty space before your cells, and you are going to scroll it under the navigation bar or have other difficulties with avoiding empty space, try to constraint your table to controller's super view Here double click the top constraint and then choose 'SuperView.Top' at the constraint settings

If you want full width separator or your own width of it, just select Separator Inset as Custom, and configure to your values, or write it in viewDidLoad:

tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
kefir
  • 21
  • 4
1

This works great for me:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    guard let cell = cell as? yourCell else {
            return
    }
    // Here we're checking if your cell is the last one
    if indexPath.row == numberOfCells.count - 1 {
        cell.separatorInset.left = UIScreen.main.bounds.width
    }
}

What we're doing here is setting the size of the left inset to be a big enough value so that the separator line won't be visible anymore. So when we increase the size value of our inset it will get closer and closer to the right of our screen since left inset goes towards the right direction.

Simon McNeil
  • 293
  • 1
  • 13
0

Find the last cell index in the cellForRow delegate and put the line of code from below:

cell.separatorInset = UIEdgeInsetsMake(
    0.f,
    40.0f,
    0.f,
    self.view.frame.size.width-40.0f
);
Mohammad
  • 21,175
  • 15
  • 55
  • 84
0

Another option is by subclassing UITableView:

@synthesize hideLastSeparator = _hideLastSeparator;
@synthesize hideLastSeparatorView = _hideLastSeparatorView;
-(void)setHideLastSeparator:(BOOL)hideLastSeparator {
    if (_hideLastSeparator == hideLastSeparator) {
        return;
    }
    _hideLastSeparator = hideLastSeparator;
    if (_hideLastSeparator) {
        _hideLastSeparatorView = [[UIView alloc] initWithFrame:CGRectMake(0, self.contentSize.height, self.bounds.size.width, 0.5f)];
        _hideLastSeparatorView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
        _hideLastSeparatorView.backgroundColor = self.backgroundColor;
        [self addSubview:_hideLastSeparatorView];
        [self hideSeparator];
    }
    else {
        [_hideLastSeparatorView removeFromSuperview];
        _hideLastSeparatorView = nil;
    }
}
-(void)setContentSize:(CGSize)contentSize {
    [super setContentSize:contentSize];
    if (_hideLastSeparator) {
        [self hideSeparator];
    }
}
-(void)hideSeparator {
    CGRect frame = _hideLastSeparatorView.frame;
    frame.origin.y = self.contentSize.height - frame.size.height;
    _hideLastSeparatorView.frame = frame;
}

The .h should only contain property declarations for hideLastSeparator and hideLastSeparatorView.
When wanting to hide the separator, use the new class and set myTableView.hideLastSeparator = YES.
The way this works is by obstructing the last separator by adding a new view over it.

This is somewhat easier to use in my opinion (than using a custom separator, or setting the last separatorInset of the last cell), and avoids some weird animations that the method of setting a tableFooterView sometimes causes (e.g. during row insertion/deletion or other table animations).

alex-i
  • 5,406
  • 2
  • 36
  • 56
0

If you're using custom separators for your UITableViewCell the best solution is:

  1. Implement in your custom cell function, that hides separatorView
class YourCell: UITableViewCell {
    // Your implementation goes here...
    // Separator view in cell
    @IBOutlet private weak var separatorView: UIView!

    // This function hides the separator view
    func hideSeparator() {
        separatorView.isHidden = true
    }
}
  1. In tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) calculate if the cell is the last and hides it's separator
public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    guard let cell = cell as? YourCell else {
            return
    }
    // Here we're checking if your cell is the last one
    if indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
        // if true -> then hide it
        cell.hideSeparator()
    }
}
Nazariy Vlizlo
  • 778
  • 7
  • 16
0

I tested and is working in Swift 5, iOS 15 and in xCode 13.3.1, Just Paste this code in TableView "cellForRowAt" method just before return cell as it pushes last line out of the frame....Note:-("arrayCartItems" will be your Array which you are returning in "numberOfRowsInSection" and "tblVw" is your Outlet name of UITableView in ViewController) Written in detail so that even Newbies could easily learn:)

    if indexPath.row == arrayCartItems.count - 1 {
        cell.separatorInset = UIEdgeInsets.init(top: 0, left: tblVw.bounds.width + 1, bottom: 0, right: 0)
    } else {
        cell.separatorInset = .zero
    }
-1

The most suitable & easy way would be using this at cellForRowAt

cell.drawBottonLine = (indexPath.row != sections[0].rows.count - 1)
Chrishan
  • 4,076
  • 7
  • 48
  • 67
-2

Expanding on Tonny Xu's answer, I have multiple sections and this seems to work for removing the last cells separator

if(indexPath.row == [self.tableView numberOfRowsInSection:indexPath.section] - 1)
{
    self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 1)];
}

In the cellForRowAtIndexPath datasource

Josh Valdivieso
  • 1,168
  • 1
  • 14
  • 22
  • 1
    This depends on some a hidden implementation detail - you shouldn't really be doing this; `tableFooterView` exists for the entire table, and not just for the last cell. Overall, hacky solution, and may not always work. – Zorayr Apr 26 '15 at 01:59