Table 查看自动标注强制重复约束

Table view automatic dimension forcing duplicate constraints

让 UITableView.rowHeight 的自动维度工作需要我的 UITableViewCell class 中的重复约束。

我正在以编程方式创建一个 UITable视图(SwiftUI,没有情节提要)并且具有不同高度的单元格。我已将 table 的 rowHeight 设置为 UITableView.automaticDimension 但无法找到正确的约束组合来让 table 实际计算单元格的正确高度而不添加重复项约束。

我希望添加宽度、高度、顶部和前导约束以使事情正常工作。但是,除非我还添加底部约束,否则 table 不会正确调整行的大小。这自然会产生警告:

2019-10-23 18:06:53.515025-0700 Test[15858:7764405] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x600001651c20 V:|-(0)-[UIView:0x7f8fc5a08890]   (active, names: '|':Aries.TestTableViewCell:0x7f8fc5a084e0'TestTableViewCell' )>",
    "<NSLayoutConstraint:0x600001651e50 UIView:0x7f8fc5a08890.bottom == Aries.TestTableViewCell:0x7f8fc5a084e0'TestTableViewCell'.bottom   (active)>",
    "<NSLayoutConstraint:0x600001651ea0 UIView:0x7f8fc5a08890.height == 40   (active)>",
    "<NSLayoutConstraint:0x600001652120 'UIView-Encapsulated-Layout-Height' Aries.TestTableViewCell:0x7f8fc5a084e0'TestTableViewCell'.height == 40.3333   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600001651ea0 UIView:0x7f8fc5a08890.height == 40   (active)>

如果我删除高度约束或底部锚点约束,重复约束就会消失,警告也会消失。但是,table 不会正确调整行的大小。

观点:

import SwiftUI

struct TableViewTest: View {
    var body: some View {
        TestTableView().frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
    }
}

Table视图:

import SwiftUI

struct TestTableView: UIViewRepresentable {
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    func makeUIView(context: UIViewRepresentableContext<TestTableView>) -> UITableView {

        let tableView = UITableView(frame: .zero)
        tableView.register(TestTableViewCell.self, forCellReuseIdentifier: "TestTableViewCell")
        tableView.rowHeight = UITableView.automaticDimension

        let dataSource = UITableViewDiffableDataSource<Section, TestData>(tableView: tableView) { tableView, indexPath, data in

            let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCell", for: indexPath) as! TestTableViewCell

            cell.data = data

            return cell
        }

        populate(dataSource: dataSource)
        context.coordinator.dataSource = dataSource

        return tableView
    }

    func populate(dataSource: UITableViewDiffableDataSource<Section, TestData>) {

        let items = [
            TestData(color: .red, size: CGSize(width: 40, height: 20)),
            TestData(color: .blue, size: CGSize(width: 40, height: 40)),
            TestData(color: .green, size: CGSize(width: 40, height: 80))
        ]

        var snapshot = NSDiffableDataSourceSnapshot<Section, TestData>()
        snapshot.appendSections([.main])
        snapshot.appendItems(items)
        dataSource.apply(snapshot)
    }

    func updateUIView(_ tableView: UITableView, context: UIViewRepresentableContext<TestTableView>) {

        guard let dataSource = context.coordinator.dataSource else {
            return
        }

        populate(dataSource: dataSource)
    }

    class Coordinator {
        var dataSource: UITableViewDiffableDataSource<Section, TestData>?
    }

    enum Section {
        case main
    }

    struct TestData: Hashable {
        var id = UUID()
        var color: UIColor
        var size: CGSize

        func hash(into hasher: inout Hasher) {
            hasher.combine(id)
        }
    }
}

Table 视图单元格:

import UIKit

class TestTableViewCell: UITableViewCell {

    var data: TestTableView.TestData? {
        didSet {
            if let data = data {
                let view = UIView()
                view.backgroundColor = data.color

                addSubview(view)
                // !!! SETTING THE BOTTOM ANCHOR TO NIL OR HEIGHT TO 0 PREVENTS THE TABLE FROM SIZING THE ROWS CORRECTLY
                view.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: data.size.width, height: data.size.height, enableInsets: false)
            }
        }
    }

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

UI查看锚点扩展:

func anchor (top: NSLayoutYAxisAnchor?, left: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, right: NSLayoutXAxisAnchor?, paddingTop: CGFloat, paddingLeft: CGFloat, paddingBottom: CGFloat, paddingRight: CGFloat, width: CGFloat, height: CGFloat, enableInsets: Bool) {

        var topInset = CGFloat(0)
        var bottomInset = CGFloat(0)

        if #available(iOS 11, *), enableInsets {
            let insets = self.safeAreaInsets
            topInset = insets.top
            bottomInset = insets.bottom
        }

        translatesAutoresizingMaskIntoConstraints = false

        if let top = top {
            self.topAnchor.constraint(equalTo: top, constant: paddingTop+topInset).isActive = true
        }
        if let left = left {
            self.leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
        }
        if let right = right {
            rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
        }
        if let bottom = bottom {
            bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom-bottomInset).isActive = true
        }
        if height != 0 {
            heightAnchor.constraint(equalToConstant: height).isActive = true
        }
        if width != 0 {
            widthAnchor.constraint(equalToConstant: width).isActive = true
        }
    }

如果我只锚定左上角并在 table 视图单元格中指定宽度和高度,table 视图将无法正确计算高度。但是,如果我还指定了高度,行的大小将正确调整,但会生成有关重复约束的警告。是否有一种神奇的组合可以让布局正确但不产生警告?

不好(但没有重复约束):

好(有重复约束):

要解决此问题,您需要更改底部约束的优先级,然后警告才会消失。

 if let bottom = bottom {
    let bottomConstraint = bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom-bottomInset)
    bottomConstraint.priority = UILayoutPriority(750)
    bottomConstraint.isActive = true
  }