This is the code I used to hide the separator for a single UITableViewCell prior to iOS 11:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
// Remove separator inset
if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
[cell setSeparatorInset:UIEdgeInsetsMake(0, tableView.frame.size.width, 0, 0)];
}
// 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:UIEdgeInsetsMake(0, tableView.frame.size.width, 0, 0)];
}
}
}
In this example, the separator is hidden for the first row in every section. I don't want to get rid of the separators completely - only for certain rows.
In iOS 11, the above code does not work. The content of the cell is pushed completely to the right.
Is there a way to accomplish the task of hiding the separator for a single UITableViewCell in iOS 11?
Let me clarify in advance that I do know that I can hide the separator for the entire UITableView with the following code (to hopefully avoid answers instructing me to do this):
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
EDIT: Also to clarify after a comment below, the code does exactly the same thing if I include the setSeparatorInset line at all. So even with only that one line, the content of the cell is pushed all the way to the right.
If you are not keen on adding a custom separator to your UITableViewCell
I can show you yet another workaround to consider.
Because the color of the separator is defined on the UITableView
level there is no clear way to change it per UITableViewCell
instance. It was not intended by Apple and the only thing you can do is to hack it.
The first thing you need is to get access to the separator view. You can do it with this small extension.
extension UITableViewCell {
var separatorView: UIView? {
return subviews .min { $0.frame.size.height < $1.frame.size.height }
}
}
When you have an access to the separator view, you have to configure your UITableView
appropriately. First, set the global color of all separators to .clear
(but don't disable them!)
override func viewDidLoad() {
super.viewDidLoad()
tableView.separatorColor = .clear
}
Next, set the separator color for each cell. You can set a different color for each of them, depends on you.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SeparatorCell", for: indexPath)
cell.separatorView?.backgroundColor = .red
return cell
}
Finally, for every first row in the section, set the separator color to .clear
.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
cell.separatorView?.backgroundColor = .clear
}
}
First, let's consider the structure of the UITableViewCell
. If you print out the subviews
of your cell you will see the following output.
<UITableViewCellContentView: 0x7ff77e604f50; frame = (0 0; 328 43.6667); opaque = NO; gestureRecognizers = <NSArray: 0x608000058d50>; layer = <CALayer: 0x60400022a660>>
<_UITableViewCellSeparatorView: 0x7ff77e4010c0; frame = (15 43.5; 360 0.5); layer = <CALayer: 0x608000223740>>
<UIButton: 0x7ff77e403b80; frame = (0 0; 22 22); opaque = NO; layer = <CALayer: 0x608000222500>>
As you can see there is a view which holds the content, the separator, and the accessory button. From this perspective, you only need to access the separator view and modify it's background. Unfortunately, it's not so easy.
Let's take a look at the same UITableViewCell
in the view debugger. As you can see, there are two separator views. You need to access the bottom one which is not present when the willDisplay:
is called. This is where the second hacky part comes to play.
When you will inspect these two elements, you will see that the first (from the top) has a background color set to nil
and the second has a background color set to the value you have specified for entire UITableView
. In this case, the separator with the color covers the separator without the color.
To solve the issue we have to "reverse" the situation. We can set the color of all separators to .clear
which will uncover the one we have an access to. Finally, we can set the background color of the accessible separator to what is desired.
Begin by hiding all separators via tableView.separatorStyle = .none
. Then modify your UITableViewCell
subclass to something as follows:
class Cell: UITableViewCell {
var separatorLine: UIView?
...
}
Add the following to the method body of tableView(_:cellForRowAt:)
:
if cell.separatorLine == nil {
// Create the line.
let singleLine = UIView()
singleLine.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
singleLine.translatesAutoresizingMaskIntoConstraints = false
// Add the line to the cell's content view.
cell.contentView.addSubview(singleLine)
let singleLineConstraints = [singleLine.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 8),
singleLine.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor),
singleLine.topAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -1),
singleLine.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: 0)]
cell.contentView.addConstraints(singleLineConstraints)
cell.separatorLine = singleLine
}
cell.separatorLine?.isHidden = [Boolean which determines if separator should be displayed]
This code is in Swift, so do as you must for the Objective-C translation and make sure to continue your version checking. In my tests I don't need to use the tableView(_:willDisplayCell:forRowAt:)
at all, instead everything is in the cellForRowAtIndexPath:
method.
Best way IMO is just to add a simple UIView with 1pt height. I wrote the following protocol which enables you to use it in any UITableViewCell you like:
// Base protocol requirements
protocol SeperatorTableViewCellProtocol: class {
var seperatorView: UIView! {get set}
var hideSeperator: Bool! { get set }
func configureSeperator()
}
// Specify the separator is of a UITableViewCell type and default separator configuration method
extension SeperatorTableViewCellProtocol where Self: UITableViewCell {
func configureSeperator() {
hideSeperator = true
seperatorView = UIView()
seperatorView.backgroundColor = UIColor(named: .WhiteThree)
contentView.insertSubview(seperatorView, at: 0)
// Just constraint seperatorView to contentView
seperatorView.setConstant(edge: .height, value: 1.0)
seperatorView.layoutToSuperview(.bottom)
seperatorView.layoutToSuperview(axis: .horizontally)
seperatorView.isHidden = hideSeperator
}
}
You use it like this:
// Implement the protocol with custom cell
class CustomTableViewCell: UITableViewCell, SeperatorTableViewCellProtocol {
// MARK: SeperatorTableViewCellProtocol impl'
var seperatorView: UIView!
var hideSeperator: Bool! {
didSet {
guard let seperatorView = seperatorView else {
return
}
seperatorView.isHidden = hideSeperator
}
}
override func awakeFromNib() {
super.awakeFromNib()
configureSeperator()
hideSeperator = false
}
}
And that's all. You are able to customize any UITableViewCell subclass to use a separator.
Set separator visibility from tableView:willDisplayCell:forRowAtIndexPath by:
cell.hideSeperator = false / true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With