使用separatorInset的UITableViewCell隐藏分隔符在iOS 11

时间:2017-10-16 22:29:28

标签: ios uitableview ios11

这是我用来隐藏iOS 11之前的单个UITableViewCell的分隔符的代码:

- (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)];
        }
    }
}

在此示例中,分隔符将隐藏在每个部分的第一行中。我不想完全摆脱分隔符 - 只对某些行。

在iOS 11中,上述代码不起作用。单元格的内容完全向右推。

有没有办法在iOS 11中完成隐藏单个UITableViewCell分隔符的任务?

我先提前澄清一下,我确实知道我可以使用以下代码隐藏整个UITableView的分隔符(希望避免答案指示我这样做):

self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

编辑:在下面的评论之后澄清一下,如果我包含setSeparatorInset行,代码会完全相同。因此,即使只有一行,单元格的内容也会一直向右推。

5 个答案:

答案 0 :(得分:2)

如果您不想在UITableViewCell添加自定义分隔符,我可以向您展示另一种需要考虑的解决方法。

如何运作

由于分隔符的颜色是在UITableView级别定义的,因此没有明确的方法可以根据UITableViewCell实例更改它。苹果并不打算这样做,你唯一能做的就是破解它。

您需要的第一件事是访问分隔符视图。你可以用这个小扩展来做到这一点。

extension UITableViewCell {
    var separatorView: UIView? {
        return subviews .min { $0.frame.size.height < $1.frame.size.height }
    }
}

如果您有权访问分隔符视图,则必须正确配置UITableView。首先,将所有分隔符的全局颜色设置为.clear(但禁用它们!)

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.separatorColor = .clear
}

接下来,为每个单元格设置分隔符颜色。您可以为每个颜色设置不同的颜色,具体取决于您。

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "SeparatorCell", for: indexPath)
    cell.separatorView?.backgroundColor = .red

    return cell
}

最后,对于该部分中的每个第一行,将分隔符颜色设置为.clear

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if indexPath.row == 0 {
        cell.separatorView?.backgroundColor = .clear
    }
}

为什么会起作用

首先,让我们考虑一下UITableViewCell的结构。如果您打印出单元格的subviews,您将看到以下输出。

<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>>

如您所见,有一个视图包含内容,分隔符和附件按钮。从这个角度来看,您只需要访问分隔符视图并修改它的背景。不幸的是,这并不容易。

让我们看一下视图调试器中的UITableViewCell。如您所见,有两个分隔符视图。您需要访问调用willDisplay:时不存在的底部。这是第二个hacky部分发挥作用的地方。

structorue of the table view cell

当您检查这两个元素时,您将看到第一个(从顶部)背景颜色设置为nil,第二个背景颜色设置为您为整个{指定的值{1}}。在这种情况下,具有颜色的分隔符覆盖没有颜色的分隔符。

要解决这个问题,我们必须“扭转”这种情况。我们可以将所有分隔符的颜色设置为UITableView,这将揭示我们可以访问的分隔符。最后,我们可以将可访问分隔符的背景颜色设置为所需的颜色。

答案 1 :(得分:1)

首先通过tableView.separatorStyle = .none隐藏所有分隔符。然后将UITableViewCell子类修改为以下内容:

class Cell: UITableViewCell {
    var separatorLine: UIView?
    ...
}

将以下内容添加到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]

此代码在Swift中,因此您必须为Objective-C转换执行此操作,并确保继续进行版本检查。在我的测试中,我根本不需要使用tableView(_:willDisplayCell:forRowAt:),而是所有内容都在cellForRowAtIndexPath:方法中。

答案 2 :(得分:1)

最好的方式IMO只是添加一个高度为1pt的简单UIView。 我编写了以下协议,使您可以在任何您喜欢的UITableViewCell中使用它:

// 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
    }
}

你这样使用它:

// 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
    }
}

这就是全部。您可以自定义任何UITableViewCell子类以使用分隔符。

从tableView设置分隔符可见性:willDisplayCell:forRowAtIndexPath by:

cell.hideSeperator = false / true

答案 3 :(得分:0)

实际上,当我使用UITableView时,我总是创建自定义单元类和分隔符,并且通常将我自己的分隔符设置为UIView,高度为1,左右约束,在您的情况下,请执行以下步骤: 1.创建自定义单元格。 2.添加UIView作为分隔符。 3.将此分隔符链接到您的自定义类。 4.将hideSeparator方法添加到您的班级。

translate()

5。隐藏所需的任何单元格的分隔符。

希望能解决你的问题。

答案 4 :(得分:0)

我也遵循这种模式一次。多年来,我对其进行了调整。就在今天,我必须删除directionalLayoutMargins部分才能使其工作。现在,我的功能如下:

    func adjustCellSeparatorInsets(at indexPath: IndexPath,
                                   for modelCollection: ModelCollection,
                                   numberOfLastSeparatorsToHide: Int) {

        guard modelCollection.isInBounds(indexPath) else { return }

        let model = modelCollection[indexPath]
        var insets = model.separatorInsets
        let lastSection = modelCollection[modelCollection.sectionCount - 1]
        let shouldHideSeparator = indexPath.section == modelCollection.sectionCount - 1
            && indexPath.row >= lastSection.count - numberOfLastSeparatorsToHide

        // Don't show the separator for the last N rows of the last section
        if shouldHideSeparator {
            insets = NSDirectionalEdgeInsets(top: 0, leading: 9999, bottom: 0, trailing: 0)
        }

        // removing separator inset
        separatorInset = insets.edgeInsets

        // prevent the cell from inheriting the tableView's margin settings
        preservesSuperviewLayoutMargins = false
    }

如果您希望在Github上进行检查,请参见this link

有关移除的PR以及说明,可以在here中找到。