67

By default there is a single line separator in uitableview.

But I want to put my customized line as a separator.

Is it possible? How?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
sagarkothari
  • 24,520
  • 50
  • 165
  • 235

12 Answers12

81

If you want to do more than change the color of the separator using the separatorColor property of the UITableView then you could set the separatorStyle property to UITableViewCellSeparatorStyleNone and then either:

  • create custom UITableViewCells that include your custom seperator within them
  • create alternate [UITableViewCell][6]s that include your custom separator

For example, if your table currently displays 5 rows you could update it to display 9 rows and the rows at index 1, 3, 5, 7 would be separator cells.

Samuel Liew
  • 76,741
  • 107
  • 159
  • 260
Daniel Richardson
  • 5,114
  • 2
  • 22
  • 20
  • 39
    Great big NO to using cells for separators. That is just ugly, but subclassing UITableViewCell and overriding layoutSubviews is what I recommended. http://stackoverflow.com/questions/12509747/uitableviewcell-spacing-between-cells/17885077#17885077 – Cameron Lowell Palmer Jan 16 '14 at 09:25
  • @CameronLowellPalmer Using cells for separators is perfect in some scenarios. In fact, we just wanted a 3 point-wide, transparent separator. Putting in invisible, spacer cells was perfect for that! – mbm29414 Jul 23 '14 at 19:22
34

A better solution is to use the cell's current width and height. Something like this:

UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, cell.contentView.frame.size.height - 1.0, cell.contentView.frame.size.width, 1)];

lineView.backgroundColor = [UIColor darkGrayColor];
[cell.contentView addSubview:lineView];
Jerry Destremps
  • 448
  • 4
  • 6
  • 2
    If you have a scrolling tableview with a larger number of cells, this will keep adding subviews as the cells are recycled when the user scrolls the tableview. Not a good solution. – Matjan Mar 04 '16 at 19:11
  • -1 for this solution because I agree with the comment above. This is a bad solution if you have more than 10 or so cells (and you are reusing/dequeueing them) because the divider you used on one cell, will appear when that cell is reused, and that may not be where you want it. – Ben Patch Apr 18 '16 at 22:26
  • @Matjan how can that happen if you put those lines in the init of your cell(or its contentView's init) hence add it only once? Recycled cell will get dequeued with this separator and the only thing you'd have to do is update cell's contentView data. – Jan Jun 13 '19 at 19:53
  • @BenPatch although it's good to think of such cases, it does not concern this particular answer to this question, since the OP just wants to use some custom view. Moreover, if you use constraints and stick the separator to the bottom of the cell's contentView, IMHO, it would be the most expected behavior that you can get with custom separators. Now, it can be true if you don't use any particular type of UITableViewCell subclass, but if you're dequeueing a specific type of the cell, the separator's position in it is expected as well. Could you please elaborate a bit more on the examples? – Jan Jun 13 '19 at 19:58
31

If you need different seperator colors for different rows OR you want the seperator to remain visible when the row is highlighted on tap then try this:

self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

// We have to use the borderColor/Width as opposed to just setting the 
// backgroundColor else the view becomes transparent and disappears during 
// the cell's selected/highlighted animation
UIView *separatorView = [[UIView alloc] initWithFrame:CGRectMake(0, 43, 1024, 1)];
separatorView.layer.borderColor = [UIColor redColor].CGColor;
separatorView.layer.borderWidth = 1.0;
[cell.contentView addSubview:separatorView];

This assumes your cell's background color is transparent.


The above solution came out of some extensive experimentation. Here's some notes on my findings that I'm sure will help people:

In the normal “not selected” state

  • The contentView (whats in your XIB unless you coded it otherwise) is drawn normally
  • The selectedBackgroundView is HIDDEN
  • The backgroundView is visible (so provided your contentView is transparent you see the backgroundView or (if you have not defined a backgroundView you'll see the background colour of the UITableView itself)

A cell is selected, the following occurs immediately with-OUT any animation:

  • All views/subviews within the contentView have their backgroundColor cleared (or set to transparent), label etc text color's change to their selected colour
  • The selectedBackgroundView becomes visible (this view is always the full size of the cell (a custom frame is ignored, use a subview if you need to). Also note the backgroundColor of subViews are not displayed for some reason, perhaps they're set transparent like the contentView). If you didn't define a selectedBackgroundView then Cocoa will create/insert the blue (or gray) gradient background and display this for you)
  • The backgroundView is unchanged

When the cell is deselected, an animation to remove the highlighting starts:

  • The selectedBackgroundView alpha property is animated from 1.0 (fully opaque) to 0.0 (fully transparent).
  • The backgroundView is again unchanged (so the animation looks like a crossfade between selectedBackgroundView and backgroundView)
  • Only once the animation has finished does the contentView get redrawn in the "not-selected" state and its subview backgroundColor's become visible again (this can cause your animation to look horrible so it is advisable that you don't use UIView.backgroundColor in your contentView)
Oliver Pearmain
  • 19,885
  • 13
  • 86
  • 90
14

These answers result in the highlight rect being overwritten by the separator you add to your cell (on iOS 6 with my testing at least). You need to set the separatorColor to [UIColor clearColor] so that cells are still separated by 1px, you can then draw your separator in the gap:

- (void)viewDidLoad {
    self.tableView.separatorColor = [UIColor clearColor];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // snip
    CALayer *separator = [CALayer layer];
    separator.backgroundColor = [UIColor redColor].CGColor;
    separator.frame = CGRectMake(0, 43, self.view.frame.size.width, 1);
    [cell.layer addSublayer:separator];
    return cell;
}
mxcl
  • 26,392
  • 12
  • 99
  • 98
8

You add the following code cellForRowAtIndexPath delegate of tableView to create a custom image view of 1px height and 200px width for everyCell

UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 200, self.view.bounds.size.width, 1)];
lineView.backgroundColor = [UIColor blackColor];
[cell.contentView addSubview:lineView];

Note: i dont know how heavy it is on the performance.

carbonr
  • 6,049
  • 5
  • 46
  • 73
4

I dont know if this can be done "automatically" with some setting. But a suggestion would be to set the line separator as none, and in the borders of your cells actually draw your line separator that you want..

Daniel
  • 22,363
  • 9
  • 64
  • 71
3

If you are using Custom Cells in Swift: An alternative approach is to extend UITableViewCell with a function that can draw a line at the top of that cell.

import UIKit

extension UITableViewCell {
    func addSeparatorLineToTop(){
        let lineFrame = CGRectMake(0, 0, bounds.size.width, 1)
        let line = UIView(frame: lineFrame)
        line.backgroundColor = UIColor.myGray_300()
        addSubview(line)
    }
}

Then you can add this line to any custom cell, for example in the awakeFromNib

override func awakeFromNib() {
    super.awakeFromNib()
    addSeparatorLineToTop()
}

This solution is nice because it does not mess up your storyboard and limits your extra code to 1 line.

Marwen Doukh
  • 1,946
  • 17
  • 26
Pbk
  • 2,004
  • 14
  • 11
1

On a retina display, even drawing a 0.5 unit line will result in a two-pixel line, due to anti-aliasing. To render it as a single pixel on both displays, shift it up one quarter of a unit:

UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, self.contentView.frame.size.height - (1.0 - 0.25), self.contentView.frame.size.wi
lineView.backgroundColor = [UIColor colorWithRed:(230.0/255.0f) green:(233/255.0f) blue:(237.0/255.0f) alpha:1.0f];
[self.contentView addSubview:lineView];
tooluser
  • 1,481
  • 15
  • 21
1

Tested on iOS 7 (GM):

@implementation MyTableViewController

- (void)viewDidLayoutSubviews {
    for (UIView *view in self.view.subviews) {
        if ([view isKindOfClass:NSClassFromString(@"_UITableViewCellSeparatorView")])
            view.backgroundColor = [UIColor redColor];
    }
}

@end

NOTE: it appears the UITableView moves separators to the cells in some configurations, which would make this code not work unless you descend into all UITableViewCells as well.

mxcl
  • 26,392
  • 12
  • 99
  • 98
  • 1
    I believe I may be having a similar issue to you as I got here searching for _UITableViewCellSeparatorView. I have a custom separator implementation that works on iOS 6 but in iOS 7 seems to be showing the default separator in addition to mine. Setting separator style to UITableViewCellSeparatorStyleNone causes both to disappear. Were you able to get any further with your issue? – markdorison Sep 19 '13 at 22:35
  • 1
    It's use of private API and may result with app rejection as well as it may brok in any future iOS version. – Nat Jun 20 '15 at 21:18
  • It's not private API. But you still should be careful with it because you are right that it is private view hierarchy and may change at anytime. However this is merely aesthetics and is crash safe. – mxcl Jun 22 '15 at 17:39
  • It IS a private API - notice an underscore in the beginning of `_UITableViewCellSeparatorView` class name you're using. Moreover, you even don't have access to the class declaration in a `.h` file. It could easily be changed to, let's say, `_UITableViewCellSeparatorView**2**` in any future release without notice and this approach fails. – Alexander Abakumov Jun 14 '17 at 18:02
  • It isn't private API, we aren't calling a private method. However this *is* the view's *private view hierarchy*, and as you say that may change at any time. I provided this example because sometimes this is your only choice. You have to decide if you want the risk of the app failing to have styled separators or not. But there is no risk of rejection because you are not *calling* any private API. And there is no risk of crash with my example. – mxcl Jun 14 '17 at 18:29
1

Swift version:

private let kSeparatorTag = 123
private let kSeparatorHeight: CGFloat = 1.5

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath)
{
    if cell.viewWithTag(kSeparatorTag) == nil //add separator only once
    {
        let separatorView = UIView(frame: CGRectMake(0, cell.frame.height - kSeparatorHeight, cell.frame.width, kSeparatorHeight))
        separatorView.tag = kSeparatorId
        separatorView.backgroundColor = UIColor.redColor()
        separatorView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]

        cell.addSubview(separatorView)
    }
}
ChikabuZ
  • 10,031
  • 5
  • 63
  • 86
0

The cell in gist below is a UITableViewCell's subclass where each cell could have its own separator w/ many styles (currently only .None and .Default are supported). It is written in Swift and assumes Autolayout usage.

https://gist.github.com/evgeniyd/fa36b6f586a5850bca3f

How to use the class:

  1. set UITableView object's separator style to UITableViewCellSeparatorStyle.None

    tableView.separatorStyle = .None
    
  2. Create a subclass of MPSTableViewCell

  3. Somewhere inside awakeFromNib() set cell's separator style

Note: the code below assumes cell is loaded from xib/storyboard

    class FASWorkoutCell: FASTableViewCell {

    required init(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
     }

     override func awakeFromNib() {
         super.awakeFromNib()

         separatorType = .Default
     }

     // ...

     }
Yevhen Dubinin
  • 4,657
  • 3
  • 34
  • 57
0

If you use a customized UITableViewCell, You can simply add UIView on the bottom of the UItableViewCell.xib and put the background colour as the colour you want.

For example, on xib I add a UIView on the bottom and set the height as 1. Using autolayout, I set left constraint to 12, bottom constraint to 0, right constraint to 0 and height to 1.

Kyle Ju
  • 1
  • 2