UITableViewCell 使用 separatorInset 隐藏分隔符在 iOS 11 中失败
UITableViewCell hide separator using separatorInset fails in iOS 11
这是我用来在 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)];
}
}
}
在此示例中,每个部分的第一行都隐藏了分隔符。我不想完全去掉分隔符 - 只针对某些行。
在iOS11,上面的代码不起作用。单元格的内容完全向右推。
有没有办法在 iOS 11 中完成隐藏单个 UITableViewCell 分隔符的任务?
让我提前澄清一下,我知道我可以使用以下代码隐藏整个 UITableView 的分隔符(希望避免答案指示我这样做):
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
编辑:还要在下面的评论后澄清,如果我完全包含 setSeparatorInset 行,代码会做同样的事情。所以即使只有那一行,单元格的内容也会被一直推到右边。
首先通过 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:
方法中。
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 的分隔符可见性:
cell.hideSeperator = false / true
如果您不想在 UITableViewCell
中添加自定义分隔符,我可以向您展示另一种可供考虑的解决方法。
工作原理
因为分隔符的颜色是在 UITableView
级别上定义的,所以没有明确的方法可以根据 UITableViewCell
实例更改它。它不是 Apple 设计的,您唯一能做的就是破解它。
您首先需要访问分隔符视图。你可以用这个小扩展来做到这一点。
extension UITableViewCell {
var separatorView: UIView? {
return subviews .min { [=10=].frame.size.height < .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 部分发挥作用的地方。
检查这两个元素时,您会看到第一个(从顶部开始)的背景颜色设置为 nil
,第二个的背景颜色设置为您指定的值整个 UITableView
。在这种情况下,有颜色的分隔符覆盖没有颜色的分隔符。
要解决这个问题我们必须"reverse"情况。我们可以将所有分隔符的颜色设置为 .clear
,这将显示我们有权访问的分隔符。最后,我们可以将可访问分隔符的背景颜色设置为所需的颜色。
实际上,当我使用 UITableView 时,我总是创建自定义单元格 class 和分隔符,并且通常将我自己的分隔符设置为高度为 1 和左右约束的 UIView,在您的情况下,请执行以下步骤:
1. 创建自定义单元格。
2. 添加 UIView 作为分隔符。
3. Link 此分隔符为您的自定义 class。
4. 将 hideSeparator 方法添加到 class.
-(void)hideSeparator{
self.separator.hidden == YES;
}
5。隐藏您想要的任何单元格的分隔符。
希望能解决您的问题。
这个模式我也学过一次。多年来我调整了它。就在今天,我不得不删除 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。
这是我用来在 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)];
}
}
}
在此示例中,每个部分的第一行都隐藏了分隔符。我不想完全去掉分隔符 - 只针对某些行。
在iOS11,上面的代码不起作用。单元格的内容完全向右推。
有没有办法在 iOS 11 中完成隐藏单个 UITableViewCell 分隔符的任务?
让我提前澄清一下,我知道我可以使用以下代码隐藏整个 UITableView 的分隔符(希望避免答案指示我这样做):
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
编辑:还要在下面的评论后澄清,如果我完全包含 setSeparatorInset 行,代码会做同样的事情。所以即使只有那一行,单元格的内容也会被一直推到右边。
首先通过 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:
方法中。
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 的分隔符可见性:
cell.hideSeperator = false / true
如果您不想在 UITableViewCell
中添加自定义分隔符,我可以向您展示另一种可供考虑的解决方法。
工作原理
因为分隔符的颜色是在 UITableView
级别上定义的,所以没有明确的方法可以根据 UITableViewCell
实例更改它。它不是 Apple 设计的,您唯一能做的就是破解它。
您首先需要访问分隔符视图。你可以用这个小扩展来做到这一点。
extension UITableViewCell {
var separatorView: UIView? {
return subviews .min { [=10=].frame.size.height < .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 部分发挥作用的地方。
检查这两个元素时,您会看到第一个(从顶部开始)的背景颜色设置为 nil
,第二个的背景颜色设置为您指定的值整个 UITableView
。在这种情况下,有颜色的分隔符覆盖没有颜色的分隔符。
要解决这个问题我们必须"reverse"情况。我们可以将所有分隔符的颜色设置为 .clear
,这将显示我们有权访问的分隔符。最后,我们可以将可访问分隔符的背景颜色设置为所需的颜色。
实际上,当我使用 UITableView 时,我总是创建自定义单元格 class 和分隔符,并且通常将我自己的分隔符设置为高度为 1 和左右约束的 UIView,在您的情况下,请执行以下步骤: 1. 创建自定义单元格。 2. 添加 UIView 作为分隔符。 3. Link 此分隔符为您的自定义 class。 4. 将 hideSeparator 方法添加到 class.
-(void)hideSeparator{
self.separator.hidden == YES;
}
5。隐藏您想要的任何单元格的分隔符。
希望能解决您的问题。
这个模式我也学过一次。多年来我调整了它。就在今天,我不得不删除 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。