分组 UITableview 删除外部分隔线

Posted

技术标签:

【中文标题】分组 UITableview 删除外部分隔线【英文标题】:Grouped UITableview remove outer separator line 【发布时间】:2015-05-14 09:43:14 【问题描述】:

我有一个以编程方式创建的分组 UITableview。此外,我还有一个以编程方式在 tableview 中填充 xib 文件的单元格。到目前为止,一切都很好。但我只想删除外部分隔线。我使用了下面的代码,但这次删除了所有分隔线。

self.tableView.separatorColor = [UIColor clearColor];

这不是我的情况的好选择。这是我想要做的截图;

【问题讨论】:

选项一:删除所有默认分隔符并在单元格中添加UIViews 作为假分隔符。希望有更好的选择。 同上方法。正如我之前所说,我只删除外部意味着顶部和底部线而不是内部线。如果我删除表格视图中的所有分隔符并在自定义单元格表格视图中添加新行,表格视图的外观仍然相同。 解决方案是,使用 TableView 作为Plain tableview,而不是分组。在 Grouped 中,它在页眉和页脚上放置分隔符。 @iphonic 您的解决方案有效,谢谢 :),但普通的 tableview 仍然显示分隔符,即使它什么都没有。 你是认真的吗,@iphonic?!这应该帮助谁?李2?当有人使用分组的 tableView 时,很可能有一个很好的理由。你有没有发现分隔符不是分组和普通 tableView 之间的唯一区别? 【参考方案1】:

ios 14,斯威夫特 5

在自定义单元格类中:

override func layoutSubviews()
    super.layoutSubviews()
    
    for subview in subviews where (subview != contentView && abs(subview.frame.width - frame.width) <= 0.1 && subview.frame.height < 2) 
        subview.removeFromSuperview()                           //option #1 -- remove the line completely
        //subview.frame = subview.frame.insetBy(dx: 16, dy: 0)  //option #2 -- modify the length
    

我要感谢 @cook 提供的这个解决方案,因为我是在它之上构建的。我对他们的解决方案有一些疑问:

    它正在删除默认突出显示/选定的背景视图,因此我添加了对子视图高度的额外检查。 我将代码放在layoutSubviews() 中,没有遇到任何问题。 我实现了两个 CGFloat 之间的近似值,而不是使用相等运算符 ==,这听起来很容易出错。

我在代码中添加了“选项 #2”,因为这是我个人正在寻找的解决方案(我想维护分隔符,但我希望它与常规单元格分隔符处于相同的缩进级别,在我的情况是 16)。

【讨论】:

终于,我得到了一个有效的答案!对于 iOS 14,Swift 5。【参考方案2】:
 override func layoutSubviews() 
    super.layoutSubviews()
    subviews.filter  $0 != contentView && $0.frame.width == frame.width .first?.removeFromSuperview()

【讨论】:

请不要只发布代码作为答案,还要解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常更有帮助、质量更好,并且更有可能吸引投票。【参考方案3】:

iOS 10~13 只移除部分头脚线。

-(void)layoutSubviews
    [super layoutSubviews];
    //for iOS10~iOS13: only remove section head foot line
    for (UIView * v in self.subviews) 
        if ( v != self.contentView &&
            (v.frame.size.width == self.frame.size.width))
            [v removeFromSuperview];
        
    

如果想删除所有行:

    for (UIView * v in self.subviews) 
        if (v != self.contentView)
            [v removeFromSuperview];
        
    

【讨论】:

【参考方案4】:

对于单个单元格部分,只需覆盖 layoutSubviews 并将其 为空 即可! https://***.com/a/59733571/4442627

【讨论】:

【参考方案5】:

即使在带有静态单元格的分组 UITableView 中,您也可以删除分隔符:

class CustomCell: UITableViewCell 

override func layoutSubviews() 
    super.layoutSubviews()
    for view in subviews where view != contentView 
        view.removeFromSuperview()
    

【讨论】:

但是在iOS10中也会删除一个部分的单元格之间的线。【参考方案6】:

用于删除每个部分的分隔线的顶部和底部。将此添加到您的静态单元格中。

override func layoutSubviews() 
    super.layoutSubviews()

    //Get the width of tableview
    let width = subviews[0].frame.width

    for view in subviews where view != contentView 
        //for top and bottom separator will be same width with the tableview width
        //so we check at here and remove accordingly
        if view.frame.width == width 
            view.removeFromSuperview()
        
    

结果如下图

【讨论】:

完美的答案,但我们可以为非自定义单元格实现这一点吗? 这是唯一对我有用的解决方案。谢谢! 正是我想要的!谢谢!【参考方案7】:

试试这个,它将删除顶部分隔线。

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

    if(indexPath.row == 0 && indexPath.section == 0) 
        for (UIView *view in  cell.subviews) 
            if ([NSStringFromClass([view class]) containsString:@"CellSeparator"]) 
                if (view.frame.origin.y == 0 && CGRectGetHeight(view.frame) < 1.01 )  //Hide first UITableViewCellSeparatorView
                    NSLog(@"%s Remove View From Cell[Section:0 Row:0] for top border [%@]:%@",__FUNCTION__,NSStringFromClass([view class]),view);
                    view.hidden = YES; //Hide
                
            
        
    

【讨论】:

【参考方案8】:

检查视图层次结构后,似乎每个 UITableViewCell 只有三个子视图:内容视图 (UITableViewCellContentView) 和两个分隔视图 (_UITableViewCellSeparatorView)。我不喜欢 NSStrings 的动态类实例化(Apple 也不是?)。但是,因为 UITableViewCellcontentView 可以在不使用私有 API 的情况下访问,所以解决方案非常简单。

这个想法只是遍历UITableViewCell 的子视图,并删除任何不是contentView 的视图:

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
    let subviews = cell.subviews
    if subviews.count >= 3 
        for subview in subviews 
            if subview != cell.contentView 
                subview.removeFromSuperview()
                break
            
        
    

tableView(:willDisplayCell:forRowAtIndexPath:) 在 table view 渲染过程中被多次调用,所以上面通过检查单元格有多少子视图来跟踪状态。如果它具有三个子视图,则两个分隔符仍然完好无损。它也只会删除其中一个分隔符,但您可以通过删除break 来删除这两个分隔符。您还可以通过检查子视图的框架来指定是删除顶部分隔符还是底部分隔符。如果框架的 y 轴原点为 0,则为顶部分隔符。如果不是0,就是底部。

希望这会有所帮助!

Swift 4、Swift 4.2 更新:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
    let subviews = cell.subviews
    guard subviews.count >= 3 else 
        return
    

    for subview in subviews where NSStringFromClass(subview.classForCoder) == "_UITableViewCellSeparatorView" 
        subview.removeFromSuperview()
    

【讨论】:

谢谢,但这种方法可能会在未来的 iOS 版本中出现问题? 很可能不会,除非 Apple 将 tableview 的内部结构更改为具有多个内容视图。如果您想更加小心,您也可以使用 NSStringFromClass 并将这些值与 "_UITableViewCellSeparatorView""UITableViewCellContentView" 进行比较,然后删除。 Apple 确实将 tableViewCell 视图层次结构从 iOS 7 更改为 iOS 8,因为我使用了这样的代码,所以它真的让我在一个项目上很痛苦,所以我建议不要这样做。 如果您担心弃用或内部更改,我建议对高于当前版本的任何 iOS 版本使用带有空正文的 #available 属性。【参考方案9】:

根据厨师的回答,但没有计时器:

override func didAddSubview(_ subview: UIView) 
    super.didAddSubview(subview)
    if NSStringFromClass(subview.classForCoder) != "UITableViewCellContentView" && subview.frame.width == frame.width 
        subview.removeFromSuperview()
    

只需将它添加到单元格的子类中,您就不需要其他任何东西了。适用于 iOS 12。

【讨论】:

【参考方案10】:

即使在 2018 年,Google 也会将此页面作为该问题的最高结果。我在 iOS 11 中没有任何提供的答案,所以这就是我想出的:

extension UITableViewCell 
    func removeSectionSeparators() 
        for subview in subviews 
            if subview != contentView && subview.frame.width == frame.width 
                subview.removeFromSuperview()
            
        
    

在任何 UITableViewCell 实例上调用 .removeSectionSeparators() 现在可以解决问题。至少在我的情况下,部分分隔符是唯一与单元格本身宽度相同的部分(因为其他部分都是缩进的)。

剩下的唯一问题是我们应该从哪里调用它。你会认为 willDisplayCell 是最好的选择,但我发现初始调用发生在分隔视图本身生成之前,所以没有骰子。

在我返回一个重新加载的单元格之前,我最终把它放在了我的 cellForRowAtIndexPath 方法中:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(withIdentifier: "MyReusableIdentifier", for: indexPath)

    Timer.scheduledTimer(withTimeInterval: 0.15, repeats: false)  (timer) in
        cell.removeSectionSeparators()
    

    return cell

感觉不是那么优雅,但我还没有遇到任何问题。

编辑:看起来我们也需要这个(对于重复使用的单元格):

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
    cell.removeSectionSeparators()

这是使用此代码的屏幕截图中的之前/之后:

之前

之后

【讨论】:

像魅力一样工作,谢谢。如果您在自定义类的布局子视图中调用它,效果会更好 这是我能找到的唯一适用于 iOS 11 及更高版本的东西。谢谢。 我尝试将这个电话拨入willDisplaycellForRowAt,这两个电话都不适用于我。我正在使用 iOS 14 和 Swift5。 好东西!我把它放在override func layoutSubviews() ... 。但不要忘记调用 super ;)【参考方案11】:

您可以使用访问视图

override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) 

    let header = view as! UITableViewHeaderFooterView 
    header.backgroundView . .....

【讨论】:

【参考方案12】:

我刚刚制定了一个解决方案,因为单元格有contentView,即UIView,所以我认为您可以只关注contentView 的底线。

这是我的代码:

首先,你必须使分隔符清除

tableView.separatorColor = UIColor.clear

二、在cellForRowAt函数中:

let bottomBorder = CALayer()

bottomBorder.frame = CGRect(x: 0.0, y: 43.0, width: cell.contentView.frame.size.width, height: 1.0)
bottomBorder.backgroundColor = UIColor(white: 0.8, alpha: 1.0).cgColor
cell.contentView.layer.addSublayer(bottomBorder)

在这里你会看到这样的用户界面:

【讨论】:

【参考方案13】:

我有一个类似的问题,我有一个带有自定义单元格的 Grouped UITableView,所有这些都是使用 Interface Build 作为 .xib 文件设计的。单元格有白色背景,删除了默认分隔符,并添加了我自己的分隔符。我还为每个部分定制了标题视图。我将这些标题视图的高度设置为 44(可以是任何东西......)。我的部分之间有一个 1 点的高度视图,这看起来很奇怪。显然,系统在各个部分之间添加了一些清晰的背景视图,即使我们将自定义高度指定为 44,并且我们返回的自定义 Header View 具有白色(或一些具体的背景颜色)。我们在清晰视图后面看到的颜色实际上是 Table View 的背景颜色。在我的情况下,表格视图和单元格都必须是白色的,并且将表格视图的背景设置为白色解决了这个问题(至少在视觉上,但这就是我想要的)。第二种解决方案是保持 Table View 的样式简单,但实现 UITableView 委托方法以返回 2 个或更多部分,并在需要时创建自定义标题。但这会使标题视图停留在顶部一段时间(滚动时),直到下一部分的标题视图更接近它,​​然后它也开始上升(这可能不是你真正想要的,但是可能是解决这个问题的简单方法,但不确定)。

【讨论】:

【参考方案14】:

尝试了基于cell.separatorInset = UIEdgeInsetsMake() 解决方法的各种解决方案,但都没有正常工作。

对于我基于 iOS11 UITableViewStyleGrouped 的项目,这样做了:

self.tableView.separatorColor = self.tableView.backgroundColor;

【讨论】:

【参考方案15】:

这是我的解决方案:

self.tableView.separatorColor = self.tableView.backgroundColor

【讨论】:

@*** 它将删除所有分隔符。如果您阅读了我的问题,我说我只想删除“外”行。 @serhatsezer 是的,我读到了你的问题,这是一个技巧解决方案,它使外线“消失”并保持分隔线可见。 @*** 但是,如果 tableView 背景颜色与分隔符颜色不同或 TableView 背景颜色与单元格颜色相同,则会出现问题。假设是白色。【参考方案16】:

这是一个非常古老的问题,在搜索如何删除每个部分的顶部和底部分隔符时,它仍然是谷歌上的第一个条目。

经过一番调查,我发现 Apple 没有办法也无意在没有对视图层次结构进行愚蠢复杂的黑客攻击的情况下以某种方式实现这一点。

幸运的是,有一种绝对简单易行的方法可以实现这样的外观:

我说简单,但对于初学者来说,这可能很困难,因为缺乏对UITableView 工作原理以及如何实现自己的单元的理解。让我试着解释一下:

    创建UITableViewCell 子类 在您的单元格中,创建一个@IBOutlet weak var separatorView: UIView! 属性 在您的 Storyboard 中,选择 tableview 单元格并选择您自己的单元格作为备份单元格的类。 注意:您不必使用自定义样式。您仍然可以使用 Basic、Subtitle 等。 将您的 UITableViews Separator Style 设置为 None 以隐藏默认分隔符 将UIView 拖到您的单元格上并调整其大小,使其位于单元格的底部(或顶部)。使用 Size Inspectors Autoresizing 将其固定到 start/end/bottom 并给它一个 flex 宽度(或 Autolayout,但在这种情况下就在顶部上方) 将视图连接到单元类的出口 在您的UITableViewDataSources cellForRowAtIndexPath: 中设置自定义分隔符的isHidden 属性,具体取决于indexPath.row 是否为该部分中的最后一行(或第一行,如果您的视图位于顶部)

这里有一些示例代码:

class MyCell: UITableViewCell 
    @IBOutlet weak var separatorView: UIView!


class ViewController: UITableViewController 


    override func numberOfSections(in tableView: UITableView) -> Int 
        return 3
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 3
    

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MyCell
        cell.separatorView.isHidden = indexPath.row == 2
        return cell
    


这里有一些截图:

是的,这是一些工作,但编码时没有办法免费获得一些东西。最后,我们是程序员,由我们来做编码。花 5 到 10 分钟进行设置总是比仅仅复制粘贴一些在 Apple 决定更改视图层次结构时可能无法继续工作的 hacky 代码更好。

写这个答案实际上比实现分隔符要多。我也在寻找一种简单快捷的解决方案,但最终没有一个我觉得值得使用的足够好的解决方案。

当您看到 for 循环遍历单元格的子视图以在运行时从 Apple 为您提供的视图层次结构中隐藏甚至删除视图时,我希望您也感到怀疑,因为有一个简单、通用、稳定且面向未来的解决方案是正确的在角落附近。您只需要 7 个小步骤,而且它们很容易理解。

【讨论】:

您的解决方案是否适用于附件视图或标准 UITableViewCell 附件(V 形、信息按钮等)? 既然你可以使用默认样式,并且不强制使用自定义样式,我认为使用默认附件应该没有问题。 我试过了,无法让分隔符出现在界面生成器的人字形下方。虽然没有在设备上尝试。原因 - xcode 只允许将视图添加到单元格的 contentView。 Chevron 是附件视图的一部分,显示在 contentView 旁边。 在这种情况下,您有多种选择。您可以简单地将分隔视图向右扩展,使其部分不可见。当内容视图被推到左边时,之前不可见的部分现在将可见。不是我最喜欢的,但它有效。然后,您可以将自动布局与自定义 tableview 单元类结合使用。将分隔符固定在内容视图的底部和前导,但在单元格本身的尾部。这样,即使内容视图被收缩,分隔符也会一直延伸到最后。 如果您希望您的应用程序可以访问,请不要这样做。我目前正在解决此解决方案导致的一个问题,该问题会为 VoiceOver 用户带来糟糕的 UX。每个单元格将被视为一个包含数据的单元格,即使它们被隐藏。这意味着 VO 用户必须在空单元格上滑动。【参考方案17】:

这是我最终想到的:

我找不到比这更好的方法。但这对我很有用。最后尝试使用 Xcode 版本 7.3.1 (7D1014)。这个过程是通过故事板完成的。

基本上我在 UITableViewCell 上添加一个 0.5 pt 高度的 UIView,然后为该 UIView 设置背景颜色。设置父 UITableView 的 Separator 为 None。

详情如下:

考虑到您已经设置了 UITableViewCell,自定义或默认。在第一阶段,将 UITableView 的分隔符设置为 None。

接下来添加一个 1 pt 高度的 UIView 并根据需要设置背景,在我的情况下它是红色。

开始设置约束。问题是将 UIView 的高度设置为 0.5 pt。这是此工作流程的唯一问题。

0.5 pt 高度的 UIView:

分享设置UIView 0.5 pt高度的方法。

首先 (1) 固定视图,然后 (2) 将高度设置为 0.5。按 Enter。

最后,你的 UIView 将如下所示。

除此之外,我无法将高度设置为 0.5。

【讨论】:

【参考方案18】:

这是一个适用于 iOS 8 和 9 的 Swift 解决方案。

使用默认实现定义协议:

protocol CellSeparatorRemovable  

extension CellSeparatorRemovable 
    func removeSeparatorLinesFromCell(cell: UITableViewCell, section: Int, row: Int, indexPath: NSIndexPath) 
        guard (section, row) == (indexPath.section, indexPath.row) else  return 

        for view in cell.subviews where view != cell.contentView 
            view.removeFromSuperview()
        
    

然后,无论你想在哪里使用它,都符合CellSeparatorRemovable协议并从…willDisplayCell…调用它的方法:

class SomeVC: UITableViewController, CellSeparatorRemovable 
    override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
        removeSeparatorLinesFromCell(cell, section: 1, row: 1, indexPath: indexPath)
    

这是一个最小的解决方案;如果您要处理许多单元格,则可能需要对其进行重构以避免过度递归和/或单元格重用问题。

【讨论】:

你好@Aaron我试过你的解决方案,但分隔符只有在重绘单元格后才会消失(当你滚动它离开屏幕然后你向后滚动时),但第一次屏幕是加载分隔符就在那里。我尝试了 cell.setNeedsLayout() 和 cell.setNeedsDisplay()。知道如何解决吗? 最后我不得不继承 UITableViewCell 并覆盖 layoutSubviews() 破解视图层次结构可能不是一个好的解决方案。它可能随时改变。也不需要自定义绘图。请查看我关于如何以更好的方式解决 OPs 问题的答案 我将代码放在 UITableViewCell 的 layoutSubview() 中,稍作改动,它就可以工作了。感谢您的快速解决方案!【参考方案19】:

解决方案在这里。这是针对静态单元格的。如果你想要动态,那么只需重写“count”。希望对您有所帮助。

extension NSObject 
   var theClassName: String 
       return    NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
   


override func viewDidLoad() 
    super.viewDidLoad()
    tableView.separatorStyle = .None

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
    let count = tableView.numberOfRowsInSection(indexPath.section)
    if ( indexPath.row != count - 1 ) 
        for view in cell.subviews 
            if view.theClassName == "_UITableViewCellSeparatorView" 
                view.backgroundColor = UIColors.redColor()
            
        
    

【讨论】:

这看起来是一个非常复杂的解决方案。如果您有一个静态表格视图,为什么不将分隔符设置为无,并将一些高度为 1p 的自定义视图添加到您想要它们的静态单元格? 老实说,我不喜欢 iOS 框架内的调整,因为它可以在以后更改。所以发布的应用可能有问题。

以上是关于分组 UITableview 删除外部分隔线的主要内容,如果未能解决你的问题,请参考以下文章

从分组的 UItableview 中删除分隔符

如何减少/删除分组 UITableView 中的左/右手边距?

如何删除(UITableView)分组文本字段周围的边框

如何删除 UITableView 单元格之间的分隔

用部分动态填充分组的 uitableView

仅删除一个单元格的分隔线