0

我目前正在使用 aUIViewController并将 a 添加UITableView到视图中。有了这个tableView,我在它UIView的. 我设置了容器视图的高度,然后在其子视图中添加了第二个,该子视图固定在.containerViewtableHeaderViewUIViewcontainerView

当我将它添加到标题视图时,单元格正在重叠。奇怪的是,如果我不将子视图添加到容器视图中headerView,那么单元格不会重叠,它只会在我将第二个视图作为子视图添加到容器视图时发生。

class ViewController: UIViewController {

    private var containerView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.alpha = 0.7
        view.backgroundColor = .red
        return view
    }()

    private var bottomView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .blue
        return view
    }()

    private(set) lazy var tableView: UITableView = {
        let tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.dataSource = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        return tableView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(tableView)

        containerView.addSubview(bottomView)
        tableView.tableHeaderView = containerView

        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),

            containerView.topAnchor.constraint(equalTo: tableView.topAnchor),
            containerView.heightAnchor.constraint(equalToConstant: 214),
            containerView.widthAnchor.constraint(equalToConstant: view.frame.size.width),

            bottomView.topAnchor.constraint(equalTo: containerView.bottomAnchor),
            bottomView.heightAnchor.constraint(equalToConstant: 114),
            bottomView.widthAnchor.constraint(equalToConstant: view.frame.size.width),
        ])
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        tableView.contentInset = UIEdgeInsets(top: -view.safeAreaInsets.top, left: 0, bottom: 0, right: 0)
        tableView.tableHeaderView?.autoresizingMask = []
        tableView.tableHeaderView?.layoutIfNeeded()
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
    }
} 

在此处输入图像描述

4

1 回答 1

1

您的“蓝色视图”与单元格重叠的原因是因为您将其顶部限制为红色视图的底部,但您没有更新标题视图大小。

一种好的方法是创建一个UIView子类以用作您的标题视图。使用适当的自动布局约束设置其所有内容。

然后,在控制器中viewDidLayoutSubviews(),我们使用.systemLayoutSizeFitting(...)来确定标题视图的高度并更新其框架:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    // update table header size

    guard let headerView = tableView.tableHeaderView else { return }
    
    let height = headerView.systemLayoutSizeFitting(CGSize(width: tableView.frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow).height
    
    var frame = headerView.frame
    
    // avoids infinite loop!
    if height != frame.height {
        frame.size.height = height
        headerView.frame = frame
        tableView.tableHeaderView = headerView
    }
}

这是一个完整的例子......

首先,我们的自定义视图类:

class SampleHeaderView: UIView {

    let redView: UIView = {
        let v = UIView()
        v.backgroundColor = .systemRed
        return v
    }()
    let blueView: UIView = {
        let v = UIView()
        v.backgroundColor = .systemBlue
        return v
    }()
    let redTopLabel: UILabel = {
        let v = UILabel()
        v.backgroundColor = .yellow
        v.numberOfLines = 0
        return v
    }()
    let redBottomLabel: UILabel = {
        let v = UILabel()
        v.backgroundColor = .green
        v.numberOfLines = 0
        return v
    }()
    let multiLineLabel: UILabel = {
        let v = UILabel()
        v.backgroundColor = .cyan
        v.numberOfLines = 0
        return v
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    func commonInit() -> Void {
        
        // all views will use auto-layout
        [redView, blueView, redTopLabel, redBottomLabel, multiLineLabel].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
        }
        
        // prevent label vertical compression
        [redTopLabel, redBottomLabel, multiLineLabel].forEach { v in
            v.setContentCompressionResistancePriority(.required, for: .vertical)
        }
        
        // add top and bottom labels to red view
        redView.addSubview(redTopLabel)
        redView.addSubview(redBottomLabel)

        // add multi-line label to blue view
        blueView.addSubview(multiLineLabel)
        
        // add red and blue views to self
        addSubview(redView)
        addSubview(blueView)

        // the following constraints need to have less-than required to avoid
        //  auto-layout warnings
        
        // blue view bottom to self
        let c1 = blueView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0)
        
        // labels trailing contraints
        let c2 = redTopLabel.trailingAnchor.constraint(equalTo: redView.trailingAnchor, constant: -8.0)
        let c3 = redBottomLabel.trailingAnchor.constraint(equalTo: redView.trailingAnchor, constant: -8.0)
        let c4 = multiLineLabel.trailingAnchor.constraint(equalTo: blueView.trailingAnchor, constant: -8.0)
        
        [c1, c2, c3, c4].forEach { c in
            c.priority = .required - 1
        }
        
        NSLayoutConstraint.activate([
            
            // red view top to self
            redView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),

            // leading / trailing to self
            redView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            redView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

            // blue view top to red view bottom
            blueView.topAnchor.constraint(equalTo: redView.bottomAnchor, constant: 0.0),

            //  leading / trailing to self
            blueView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            blueView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
            
            // top and bottom labels, constrained in red view
            //  with a little "padding"
            redTopLabel.topAnchor.constraint(equalTo: redView.topAnchor, constant: 8.0),
            redTopLabel.leadingAnchor.constraint(equalTo: redView.leadingAnchor, constant: 8.0),

            redBottomLabel.topAnchor.constraint(equalTo: redTopLabel.bottomAnchor, constant: 8.0),
            redBottomLabel.leadingAnchor.constraint(equalTo: redView.leadingAnchor, constant: 8.0),

            redBottomLabel.bottomAnchor.constraint(equalTo: redView.bottomAnchor, constant: -8.0),

            // multi-line label, constrained in blue view
            //  with a little "padding"
            multiLineLabel.topAnchor.constraint(equalTo: blueView.topAnchor, constant: 8.0),
            multiLineLabel.leadingAnchor.constraint(equalTo: blueView.leadingAnchor, constant: 8.0),
            multiLineLabel.bottomAnchor.constraint(equalTo: blueView.bottomAnchor, constant: -8.0),

            // the less-than-required priority constraints
            c1, c2, c3, c4,

        ])
        
    }
}

和一个示例控制器:

class TableHeaderViewController: UIViewController {
    
    var sampleHeaderView = SampleHeaderView()
    
    private(set) lazy var tableView: UITableView = {
        let tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.dataSource = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        return tableView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(tableView)
        
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        ])

        sampleHeaderView.redTopLabel.text = "The Red Top Label"
        sampleHeaderView.redBottomLabel.text = "The Red Bottom Label, with enough text that is should wrap."
        sampleHeaderView.multiLineLabel.text = "This text is for the Label in the Blue View. It is also long enough that it will require word-wrapping. Note that the header updates itself when the frame changes, such as on device rotation."
        tableView.tableHeaderView = sampleHeaderView

    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        // update table header size

        guard let headerView = tableView.tableHeaderView else { return }
        
        let height = headerView.systemLayoutSizeFitting(CGSize(width: tableView.frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow).height
        
        var frame = headerView.frame
        
        // avoids infinite loop!
        if height != frame.height {
            frame.size.height = height
            headerView.frame = frame
            tableView.tableHeaderView = headerView
        }
    }
    
}

extension TableHeaderViewController: UITableViewDataSource, UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        c.textLabel?.text = "\(indexPath)"
        return c
    }
    
}

输出:

在此处输入图像描述

并旋转:

在此处输入图像描述

于 2021-11-24T19:23:38.830 回答