首页 > 解决方案 > UITableViewCell contentView 具有 autoResizingMask 约束“UIView-Encapsulated-Layout-Height”与自动调整大小的自动布局冲突

问题描述

我正在尝试创建一个表格视图,其单元格在选择单元格时展开并保持展开直到再次被选中。

我试图让单元格的高度由它们的内容决定。我已经知道设置 tableView 的 rowHeight 和 estimagedRowHeight 以使自我调整大小正常工作,所以这不是问题。

ViewController.swift:

import UIKit

class ViewController: UIViewController {

    lazy var tableView: UITableView = {
        let tv = UITableView(frame: .zero, style: .plain)
        tv.register(ExpandingCell.self, forCellReuseIdentifier: ExpandingCell.reuseIdentifier)
        tv.estimatedRowHeight = 85.0
        tv.rowHeight = UITableViewAutomaticDimension
        return tv
    }()


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: self.view.topAnchor),
            tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
            tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)])


        tableView.dataSource = self
        tableView.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 50
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: ExpandingCell.reuseIdentifier, for: indexPath) as! ExpandingCell
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }
    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50.0
    }

}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    }
    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {

    }
}

ExpandingCell.swift

import UIKit

class ExpandingCell: UITableViewCell {
    static let reuseIdentifier = "dafasdfadfagr"

    let minimumDisplayedView = UIView()
    let extraContentView = UIView()
    var expandingConstraint: NSLayoutConstraint?

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    private func commonInit() {
        self.setupViews()
        self.contentView.clipsToBounds = true
    }
    private func setupViews() {
        self.addSubviews()
        self.customizeSubviews()
        self.constrainSubviews()
    }

    private func addSubviews() {
        self.contentView.addSubview(minimumDisplayedView)
        self.contentView.addSubview(extraContentView)
    }
    private func customizeSubviews() {
        self.customizeMinimumDisplayedView()
        self.customizeExtraContentView()
    }
    private func constrainSubviews() {
        self.constrainMinimumDisplayedView()
        self.constrainExtraContentView()
    }

    // MARK: - MinimumDisplayView
    private func customizeMinimumDisplayedView() {
        minimumDisplayedView.backgroundColor = .blue
    }
    private func constrainMinimumDisplayedView() {
        minimumDisplayedView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            minimumDisplayedView.heightAnchor.constraint(equalToConstant: 50),
            minimumDisplayedView.topAnchor.constraint(equalTo: self.contentView.topAnchor),
            minimumDisplayedView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
            minimumDisplayedView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor)
            ])
        expandingConstraint = minimumDisplayedView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
        expandingConstraint?.isActive = true
    }

    // MARK: - ExtraContentView
    private func customizeExtraContentView() {
        extraContentView.backgroundColor = .red
    }
    private func constrainExtraContentView() {
        extraContentView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            extraContentView.heightAnchor.constraint(equalToConstant: 200),
            extraContentView.topAnchor.constraint(equalTo: minimumDisplayedView.bottomAnchor),
            extraContentView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
            extraContentView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor)
            ])
    }

    // MARK: - Animate Expansion
//    override func setSelected(_ selected: Bool, animated: Bool) {
//        super.setSelected(selected, animated: animated)
//        print("selected: \(selected)")
//        resize(selected)
//    }

    private func resize(_ expand: Bool) {
        expandingConstraint?.isActive = false
        if expand {
            expandingConstraint = extraContentView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
            expandingConstraint?.isActive = true
        } else {
            expandingConstraint = minimumDisplayedView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
            expandingConstraint?.isActive = true
        }
        UIView.animate(withDuration: 0.3) {
            self.layoutIfNeeded()
        }
    }

}

我有 2 个视图被添加到 contentView 并在初始化单元格时将 contentView 的边缘固定到第一个视图。第一个视图是蓝色且短,而第二个视图是红色且高。

我希望 tableView 最初看起来都是蓝色的,但它看起来像这样: 错误显示的单元格

我怀疑它与视图生命周期中单元格的布局方式有关,所以我也遇到了这个答案:reload tableview after first load as a workaround

当我运行代码时,我得到了这些约束错误

2018-05-05 18:00:15.108634-0400 TestExpandingCells[1022:13932317] [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:0x6000000961c0 UIView:0x7fe583700df0.height == 50   (active)>",
"<NSLayoutConstraint:0x600000096580 V:|-(0)-[UIView:0x7fe583700df0]   (active, names: '|':UITableViewCellContentView:0x7fe583704850 )>",
"<NSLayoutConstraint:0x600000096760 UIView:0x7fe583700df0.bottom == UITableViewCellContentView:0x7fe583704850.bottom   (active)>",
"<NSLayoutConstraint:0x600000096bc0 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fe583704850.height == 50   (active)>"
)

该列表上的最后一个约束“UIView-Encapsulated-Layout-Height”似乎来自 UIKit 为布局单元格创建的一些 autoresizingmask-to-constraint 东西。

所以我的问题是,我怎样才能删除这个约束以允许 AutoLayout 完全处理我的单元格的制作,而无需解决方法。我还希望将其全部包含在 ExpansionCell 类中,而不是将其包含在 tableview 的委托方法中。

如果您还找到了使用 AutoLayout 触发单元格扩展的方法,则可以加分。

标签: iosswiftuitableviewuikit

解决方案


首先要摆脱约束的冲突,将连接到 contentView 的任何视图的底部约束的优先级设置为 999,其次触发扩展钩子高度约束并更改它,还在您的模型中声明任何 bool 值数组以确定是否为滚动后单元格是否展开以恢复单元格状态和

cellForRow

cell.viewHeighcon.constant = isExpanded ? 200 : 50

推荐阅读