如何调整 UITableView tableHeaderView 的左右插入或边距?

Posted

技术标签:

【中文标题】如何调整 UITableView tableHeaderView 的左右插入或边距?【英文标题】:How to adjust left and right insets or margins for UITableView tableHeaderView? 【发布时间】:2017-10-11 19:43:12 【问题描述】:

我有一个“普通”风格的 UITableView。我将视图设置为表格视图的tableViewHeader。该表还显示了右侧下方的部分索引。

我的问题是弄清楚如何插入标题视图的左右边缘以考虑安全区域插入,如果在 iPhone X(横向)和表格视图的部分索引(如果有的话)上运行。

我创建了一个简单的测试应用程序,它添加了一些虚拟行、一个节标题和节索引。

这是我使用 UILabel 创建简单标题视图的代码。我的真实应用不会使用标签,而是使用自定义视图。

let label = UILabel()
label.font = UIFont.systemFont(ofSize: 30)
label.backgroundColor = .green
label.text = "This is a really long Table Header label to make sure it is working properly."
label.sizeToFit()
tableView.tableHeaderView = label

没有特别尝试修复左右边缘,在iPhone X模拟器中的结果如下:

肖像:

风景:

请注意,无需任何额外的努力,单元格和部分标题会获得所需的插图,但标题视图不会。

我希望标题视图的左边缘与节标题的左边缘和单元格对齐。

我希望标题视图的右边缘在与节索引的左边缘相交处停止。请注意,纵向图片似乎已经这样做了,但是如果您仔细观察,您可以知道标题视图一直到表格视图的右边缘。您看不到椭圆的第三个.,您几乎看不到部分标题视图后面的绿色。

我所做的一个尝试是将以下内容添加到我的表格视图控制器中:

override func viewDidLayoutSubviews() 
    super.viewDidLayoutSubviews()

    if let header = tableView.tableHeaderView 
        var insets = header.layoutMargins
        insets.left = tableView.layoutMargins.left
        insets.right = tableView.layoutMargins.right
        header.layoutMargins = insets
    

该代码无效。

我应该设置哪些属性来确保标题视图的左右边缘根据需要缩进?是否有应应用的限制条件?

请注意,我在代码中做所有事情。所以请不要发布任何需要故事板或 xib 文件的解决方案。欢迎使用 Swift 或 Objective-C 的答案。


对于想要复制此内容的任何人,请创建一个新的单视图项目。调整主故事板以使用 UITableViewController 而不是计划 UIViewController 并将以下内容用于ViewController

import UIKit

class ViewController: UITableViewController 

    // MARK: - UITableViewController methods

    override func numberOfSections(in tableView: UITableView) -> Int 
        return 1
    

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 5
    

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

        cell.textLabel?.text = "Row \(indexPath.row)"
        cell.accessoryType = .disclosureIndicator

        return cell
    

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

    

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
        return "Section Header"
    

    override func sectionIndexTitles(for tableView: UITableView) -> [String]? 
        let coll = UILocalizedIndexedCollation.current()

        return coll.sectionIndexTitles
    

    override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int 
        return index
    

    // MARK: - UIViewController methods

    override func viewDidLoad() 
        super.viewDidLoad()

        tableView.sectionIndexMinimumDisplayRowCount = 1

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")

        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 30)
        label.backgroundColor = .green
        label.text = "This is a really long Table Header label to make sure it is working properly."
        label.sizeToFit()
        tableView.tableHeaderView = label
    

    override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()

        if let header = tableView.tableHeaderView 
            var insets = header.layoutMargins
            insets.left = tableView.layoutMargins.left
            insets.right = tableView.layoutMargins.right
            header.layoutMargins = insets
        
    

【问题讨论】:

【参考方案1】:

对于没有节索引的 UITableView:

label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    label.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
    label.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
])

对于带有节索引的 UITableView:

label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    label.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
    label.trailingAnchor.constraint(equalTo: tableView.layoutMarginsGuide.trailingAnchor)
])

【讨论】:

这对我不起作用。 1 - 为什么在viewDidLayoutSubviews 中创建和应用标题视图的代码?那属于viewDidLoad。 2 - 如果表没有节索引,您的 trailingAnchor 会留下一个间隙。 3 - 领先的锚在所有条件下都会留下不希望的间隙,除了 iPhone X 横屏。 似乎,昨天对我来说已经太晚了 ;-) 正如您在对 theMikeSwan 的回答的评论中已经指出的那样,您必须根据表视图是否具有节索引来设置不同的尾随约束或不。我删除了旧答案并添加了两个选项。我知道这个问题基本上已经由 theMikeSwan 回答了,但我宁愿没有一个糟糕的答案。如果您认为它只是重复并且没有任何价值,我会很乐意删除此答案。【参考方案2】:

添加到tableview后需要给label添加一些Auto Layout约束:

…
tableView.tableHeaderView = label
//Add this
label.translatesAutoresizingMaskIntoConstraints = false
label.leadingAnchor.constraint(equalTo: (label.superview?.safeAreaLayoutGuide.leadingAnchor)!).isActive = true
label.trailingAnchor.constraint(equalTo: (label.superview?.safeAreaLayoutGuide.trailingAnchor)!).isActive = true

另外,如果您想查看标签中的所有文本,请使用label.numberOfLines = 0

你可以摆脱你添加到viewDidLayoutSubviews的代码。

更新:

为了好玩,我在操场上做了一些实验,发现使用 layoutMarginsGuide 并没有将标题标签的后沿推得足够远(我认为它在 iPhone X 上会出现,但可能不会在所有设备,或者游乐场的行为有点不同)。我确实发现,对于至少有一个单元格的表格视图,我可以使用aCell.contentView.bounds.width,减去表格视图的宽度并将结果除以二,标题标签将很好地位于节索引旁边。结果,我编写了一个辅助函数来设置约束。表格视图是可选的,因此该功能可以与任何具有超级视图并需要保留在安全区域内的视图一起使用。如果传入一个表格视图,它可以有或没有节索引,但它确实需要在第 0 行第 0 节至少有一个单元格:

func constrain(view: UIView, inTableView aTableView: UITableView?) 
    guard let container = view.superview else 
        print("Constrain error! View must have a superview to be constrained!!")
        return
    
    view.translatesAutoresizingMaskIntoConstraints = false
    view.leadingAnchor.constraint(equalTo: container.safeAreaLayoutGuide.leadingAnchor).isActive = true
    if let table = aTableView, let aCell = table.cellForRow(at: IndexPath(row: 0, section: 0)) 
        let tableWidth = table.bounds.width
        let cellWidth = aCell.contentView.bounds.width
        view.trailingAnchor.constraint(equalTo: table.safeAreaLayoutGuide.trailingAnchor, constant: cellWidth - tableWidth).isActive = true
     else 
        view.trailingAnchor.constraint(equalTo: container.safeAreaLayoutGuide.trailingAnchor).isActive = true
    

我在使用它时确实发现了一个问题。当您的文本使用设置为 0 行的标签时,它会覆盖第一节标题和该节的第一个单元格。也需要一些滚动才能将它们从标题下方取出。不过,将标签剪裁到一行效果很好。

【讨论】:

根据安全区域布局指南设置领先锚点是完美的。如果表格视图没有部分索引,那么您对尾随锚的解决方案就是需要的。但是对于具有节索引的表视图,安全区域尾随锚点是不正确的。然后我将 layoutMarginGuide 用于尾随锚点,当有节索引时,这是完美的。所以适当的尾随锚点需要基于表格视图是否显示部分索引。

以上是关于如何调整 UITableView tableHeaderView 的左右插入或边距?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Autolayout 调整 UITableView 的大小?

如何让 UITableView 行高自动调整为 UITableViewCell 的大小?

如何根据 UITableView 滚动动态调整 UITableViewCell 的大小?

如何在 Today Extension 中调整 UITableView 的大小

自行调整特定 UITableView 单元的大小

如何在 UITableView 控制器中调整表格视图的大小?