How do I add an extra separator to the top of a UITableView?

26,817

Solution 1

To replicate the standard iOS separator lines, I use a 1 px (not 1 pt) hair line tableHeaderView with the table view's separatorColor:

// in -viewDidLoad
self.tableView.tableHeaderView = ({
    UIView *line = [[UIView alloc] 
                    initWithFrame:CGRectMake(0, 0,
                    self.tableView.frame.size.width, 1 / UIScreen.mainScreen.scale)];
    line.backgroundColor = self.tableView.separatorColor;
    line;
});

The same in Swift (thanks, Dane Jordan, Yuichi Kato, Tony Merritt):

let px = 1 / UIScreen.main.scale
let frame = CGRect(x: 0, y: 0, width: self.tableView.frame.size.width, height: px)
let line = UIView(frame: frame)
self.tableView.tableHeaderView = line
line.backgroundColor = self.tableView.separatorColor

Solution 2

I just got hit with this same problem and realised that the separator at the top is only displayed whilst scrolling the table.

What I then did was the following

  1. In Interface Builder go to "Scroll View Size"
  2. Set the Content Insets of Top to 1

Alternatively in code you could do

[tableView setContentInset:UIEdgeInsetsMake(1.0, 0.0, 0.0, 0.0)];

NOTE: This no longer works for iOS7 as the separators are no longer shown at all.

Solution 3

I had the same problem and could not find an answer. So I added a line to the bottom of my table header.

CGRect  tableFrame = [[self view] bounds] ; 
CGFloat headerHeight = 100;        
UIView * headerView = [[UIView alloc] initWithFrame:CGRectMake(0,0,tableFrame.size.width, headerHeight)];
// Add stuff to my table header...

// Create separator
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, headerHeight-1, tableFrame.size.width, 1)] ;
lineView.backgroundColor = [UIColor colorWithRed:224/255.0 green:224/255.0 blue:224/255.0 alpha:1.0];
[headerView addSubview:lineView];

self.tableView.tableHeaderView = headerView;

Solution 4

Swift 4

extension UITableView {
    func addTableHeaderViewLine() {
        self.tableHeaderView = {
            let line = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.size.width, height: 1 / UIScreen.main.scale))
            line.backgroundColor = self.separatorColor
            return line
        }()
    }
}

Solution 5

I made a UITableView extension that displays a native style separator on top of the UITableView, while the table gets scrolled.

Here is how it looks

Here's the code

fileprivate var _topSeparatorTag = 5432 // choose unused tag

extension UITableView {

    fileprivate var _topSeparator: UIView? {
        return superview?.subviews.filter { $0.tag == _topSeparatorTag }.first
    }
    
    override open var contentOffset: CGPoint {
        didSet {
            guard let topSeparator = _topSeparator else { return }
            
            let shouldDisplaySeparator = contentOffset.y > 0
            
            if shouldDisplaySeparator && topSeparator.alpha == 0 {
                UIView.animate(withDuration: 0.15, animations: {
                    topSeparator.alpha = 1
                })
            } else if !shouldDisplaySeparator && topSeparator.alpha == 1 {
                UIView.animate(withDuration: 0.25, animations: {
                    topSeparator.alpha = 0
                })
            }
        }
    }
    
    // Adds a separator to the superview at the top of the table
    // This needs the separator insets to be set on the tableView, not the cell itself
    func showTopSeparatorWhenScrolled(_ enabled: Bool) {
        if enabled {
            if _topSeparator == nil {
                let topSeparator = UIView()
                topSeparator.backgroundColor = separatorColor?.withAlphaComponent(0.85) // because while scrolling, the other separators seem lighter
                topSeparator.translatesAutoresizingMaskIntoConstraints = false

                superview?.addSubview(topSeparator)

                topSeparator.leftAnchor.constraint(equalTo: self.leftAnchor, constant: separatorInset.left).isActive = true
                topSeparator.rightAnchor.constraint(equalTo: self.rightAnchor, constant: separatorInset.right).isActive = true
                topSeparator.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
                let onePixelInPoints = CGFloat(1) / UIScreen.main.scale 
                topSeparator.heightAnchor.constraint(equalToConstant: onePixelInPoints).isActive = true
                
                topSeparator.tag = _topSeparatorTag
                topSeparator.alpha = 0

                superview?.setNeedsLayout()
            }
        } else {
            _topSeparator?.removeFromSuperview()
        }
    }
    
    func removeSeparatorsOfEmptyCells() {
        tableFooterView = UIView(frame: .zero)
    }
}

To enable it, simply call tableView.showTopSeparatorWhenScrolled(true) after you set your delegate for your UITableView

Share:
26,817
Admin
Author by

Admin

Updated on September 08, 2021

Comments

  • Admin
    Admin over 2 years

    I have a view for the iPhone that is basically split in two, with an informational display in the top half, and a UITableView for selecting actions in the bottom half. The problem is that there is no border or separator above the first cell in the UITableView, so the first item in the list looks funny. How can I add an extra separator at the top of the table, to separate it from the display area above it?

    Here's the code to build the cells - it's pretty straightforward. The overall layout is handled in a xib.

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        static NSString *CellIdentifier = @"Cell";
    
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        }
    
        switch(indexPath.row) {
            case 0: {
                cell.textLabel.text = @"Action 1";
                break;
            }
            case 1: {
                cell.textLabel.text = @"Action 2";
                break;
            }
            // etc.......
        }
        return cell;
    }
    
  • Oritm
    Oritm almost 9 years
    whats it called using the ({ object }); notation?
  • Ortwin Gentz
    Ortwin Gentz almost 9 years
    This is called a statement expression (gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html). Of course you could also write it without it but it nicely scopes the definition of the UIView.
  • Oritm
    Oritm almost 9 years
    Yeah especially when there are a lot of things happening in the viewdidload. Thanks for this!
  • Dane Jordan
    Dane Jordan over 8 years
    For swift users: var px = 1 / UIScreen.mainScreen().scale var frame = CGRectMake(0, 0, self.tableView.frame.size.width, px) var line: UIView = UIView(frame: frame) self.tableView.tableHeaderView = line line.backgroundColor = self.tableView.separatorColor
  • Yuichi Kato
    Yuichi Kato over 7 years
    For Swift 3.0 users let px = 1 / UIScreen.main.scale let frame = CGRect(x: 0, y: 0, width: self.tableView.frame.size.width, height: px) let line: UIView = UIView(frame: frame) self.tableView.tableHeaderView = line line.backgroundColor = self.tableView.separatorColor
  • Tony Merritt
    Tony Merritt over 6 years
    Still can be used swift 3 and 4... let px = 1 / UIScreen.main.scale let frame = CGRect(x: 0, y: 0, width: self.tableView.frame.size.width, height: px) let line = UIView(frame: frame) self.tableView.tableHeaderView = line line.backgroundColor = self.tableView.separatorColor
  • Voyteck
    Voyteck about 6 years
    What is LayoutHelper?
  • heyfrank
    heyfrank about 6 years
    Sorry this is my own class. I replaced this method call with generic solution.
  • David Rector
    David Rector over 5 years
    Doesn't using the frame width cause a problem if the user changes orientation while the table is visible?