2

So I'm adding views into a tableviewcell's content view programmatically and using the dynamic height that tableview's offer and I seem to be getting the following autolayout warnings, should I be worried since the layout doesn't seem to be messed up but it's annoying to have a console full of these warnings.

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2018-10-09 14:51:46.748606+0100 GenericForms[78197:5088471] [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:0x600001005fe0 UIView:0x7fa582d162e0.height == 55   (active)>",
    "<NSLayoutConstraint:0x600001018960 V:|-(20)-[UIView:0x7fa582d162e0]   (active, names: '|':UITableViewCellContentView:0x7fa582d15800 )>",
    "<NSLayoutConstraint:0x60000101c910 UIView:0x7fa582d162e0.bottom == UITableViewCellContentView:0x7fa582d15800.bottom - 20   (active)>",
    "<NSLayoutConstraint:0x60000103d950 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fa582d15800.height == 95   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600001005fe0 UIView:0x7fa582d162e0.height == 55   (active)>

This is the code I'm using to add a view within the UITableViewCell content view content.

if view == nil {
    view = UIView(frame: .zero)
    view?.backgroundColor = .green
    view?.translatesAutoresizingMaskIntoConstraints = false

    contentView.addSubview(view!)

    NSLayoutConstraint.activate([
        view!.heightAnchor.constraint(equalToConstant: 55),
        view!.topAnchor.constraint(equalTo: contentView.topAnchor, constant: edgeInsets.top),
        view!.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -edgeInsets.bottom),
        view!.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: edgeInsets.left),
        view!.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -edgeInsets.right)
        ])
}

Within my view controller this the code that I'm using to set the table view.

class SelfSizedTableView: UITableView {
    var maxHeight: CGFloat = UIScreen.main.bounds.size.height

    override func reloadData() {
        super.reloadData()
        self.invalidateIntrinsicContentSize()
        self.layoutIfNeeded()
    }

    override var intrinsicContentSize: CGSize {
        let height = min(contentSize.height, maxHeight)
        return CGSize(width: contentSize.width, height: height)
    }
}
class FormViewController: UIViewController {

    private let tableView: SelfSizedTableView = {
       let tv = SelfSizedTableView()
        tv.isScrollEnabled = false
        tv.translatesAutoresizingMaskIntoConstraints = false
        tv.tableFooterView = UIView()
        tv.register(TestTableViewCell.self, forCellReuseIdentifier: "cellId")
        return tv
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.backgroundColor = .white
        tableView.dataSource = self
        view.addSubview(tableView)
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        centerTableViewContent()
    }

    private func centerTableViewContent() {

        tableView.removeConstraints(tableView.constraints)

        if tableView.intrinsicContentSize.height > view.safeAreaLayoutGuide.layoutFrame.size.height {

            NSLayoutConstraint.activate([

                tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
                tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
                tableView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor),
                tableView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor)
                ])

        } else {

            NSLayoutConstraint.activate([

                tableView.heightAnchor.constraint(equalToConstant: tableView.intrinsicContentSize.height),
                tableView.widthAnchor.constraint(equalToConstant: view.frame.width),
                tableView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                tableView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
                ])
        }
    }
}

extension FormViewController: UITableViewDataSource {

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

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

        switch indexPath.row {
        case 0:
            cell.backgroundColor = .red
            cell.configure(with: "Item at: \(indexPath.row)", edgeInsets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
        case 1:
            cell.backgroundColor = .yellow
            cell.configure(with: "Item at: \(indexPath.row)", edgeInsets: UIEdgeInsets(top: 20, left: 10, bottom: 20, right: 10))
        default:
            cell.backgroundColor = .blue
            cell.configure(with: "Item at: \(indexPath.row)", edgeInsets: UIEdgeInsets(top: 70, left: 10, bottom: 70, right: 10))

        }

        return cell
    }
}
Tunds
  • 1,804
  • 2
  • 15
  • 30
  • Seems like the cell has conflicting heights. Do you set the cell height anywhere else? – Rakesha Shastri Oct 09 '18 at 14:05
  • @RakeshaShastri Nope – Tunds Oct 09 '18 at 14:06
  • If you are using dynamic height, then why are you setting it to 55? If you want that as a min or max, maybe try (a) setting things to greater/less than instead of equal to or (b) place a lower priority on the equal to 55 constraint. (I usually prefer the latter, as all the warnings are saying is just that - the engine doesn't know which constraint to break.) –  Oct 09 '18 at 14:10
  • @dfd The height that I'm setting is for a view within the content view not the actual content view itself – Tunds Oct 09 '18 at 14:21
  • @Tunds Conflict is between `heightAnchor`, `topAnchor ` and `bottomAnchor ` in `UITableViewCell `. Remove one from these three constraints – Satish Oct 09 '18 at 14:33
  • @Satish If i remove the height anchor then nothing gets shown since the view doesn’t have a height. – Tunds Oct 09 '18 at 14:34
  • @Tunds try removing `bottomAnchor` constraint – Satish Oct 09 '18 at 14:45
  • what is `SelfSizedTableView`? Can you share it's code? – Milan Nosáľ Oct 09 '18 at 15:45
  • Are you setting `tableView.rowHeight = UITableViewAutomaticDimension` somewhere? It's not shown in the code you've posted. Also, are there other elements in your cell? Might help if you post the full cell code as well. – DonMag Oct 09 '18 at 15:47
  • Nope i don't need to since that's the default behaviour of rowHeight – Tunds Oct 09 '18 at 15:48
  • OK .. are you setting rowHeight to 95 somewhere? – DonMag Oct 09 '18 at 15:50
  • Nope the code i've provided is everything that i'm doing in the project, It seems to be clashing with the constraints on the tableviewcell content that seems to be issue, it's not that it's laying it out incorrectly since by breaking the constraint it's fine. It's just annoying having my log with all these warnings – Tunds Oct 09 '18 at 15:52

2 Answers2

8

Just to share: There's a great online tool (which I am not associated with, btw.) that helps understanding the logged error message. Just copy the list of conflicting constraints (including the surrounding parentheses, see below) and paste them in the text area of the tool. It will then give you a nice visualization of the constraints that usually helps understanding what's wrong. Give it a try!

Copy and paste this:

(
    "<NSLayoutConstraint:0x600001005fe0 UIView:0x7fa582d162e0.height == 55   (active)>",
    "<NSLayoutConstraint:0x600001018960 V:|-(20)-[UIView:0x7fa582d162e0]   (active, names: '|':UITableViewCellContentView:0x7fa582d15800 )>",
    "<NSLayoutConstraint:0x60000101c910 UIView:0x7fa582d162e0.bottom == UITableViewCellContentView:0x7fa582d15800.bottom - 20   (active)>",
    "<NSLayoutConstraint:0x60000103d950 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fa582d15800.height == 95   (active)>"
)
Lutz
  • 1,734
  • 9
  • 18
  • 1
    Does this answer the OP's question? – DonMag Oct 09 '18 at 15:44
  • @DonMag some things are not comments/ nor they are answers. The decision of where to put them is tricky. In my opinion this example is better written in the answer section. Nonetheless it can also lead to solving the problem all together... – mfaani Oct 09 '18 at 15:51
  • 1
    @Honey - it may be true that some things *could* be considered comments **or** answers, I'd say that's not true in this case. Had the answer also included "here is your fix, based on the results of using that tool" then I'd say, yes, it's an answer. That's not the case here. – DonMag Oct 09 '18 at 15:55
  • @DonMag you are probably right. I should have posted it as a comment instead. – Lutz Oct 10 '18 at 18:16
  • It may not directly answers OP's question, but this answer is much more useful to me and probably other people struggling with a layout constraint problem out there. Specific answers are great for the OP, but often less useful to people with a similar but different problem. For these people a helpful tool to provide an answer to their own question is great. Putting this as a comment would not give it the same visibility. – Philip De Vries Nov 09 '19 at 04:53
2

If you'll read the warning properly, you'll understand that you are setting an explicit height (55) on the view in the cell's contentView and you are constraining it to top and bottom of the contentView. Thus you are basically specifying the height of the cell using autolayout on its content.

To allow that, you should set rowHeight to UITableViewAutomaticDimension and estimatedRowHeight to a constant that estimates its height the best.

Then the UIView-Encapsulated-Layout-Height constraint in the warning tells you that the conflict emerges just because of the height of the cell that is set by the tableView based on the rowHeight/estimatedRowHeight - for more on this topic, read this question.

Now you have to decide whether you want to use the row height set throught tableView.rowHeight, or height specified by the autolayout constraints on cell's content. If you want to use autolayout, as I assume based on that constraints, use the following code.

Try using this code to setup the tableView:

private let tableView: SelfSizedTableView = {
    let tv = SelfSizedTableView()
    tv.isScrollEnabled = false
    tv.translatesAutoresizingMaskIntoConstraints = false
    tv.tableFooterView = UIView()

    // explicitly set the row height and its estimated row height
    tv.estimatedRowHeight = 55 // ideally use 55 + edgeInsets.top + edgeInsets.bottom
    tv.rowHeight = UITableViewAutomaticDimension // automatic height based on content

    tv.register(TestTableViewCell.self, forCellReuseIdentifier: "cellId")
    return tv
}()

And then use this code to setup the cell's content view:

if view == nil {
    view = UIView(frame: .zero)
    view?.backgroundColor = .green
    view?.translatesAutoresizingMaskIntoConstraints = false

    contentView.addSubview(view!)

    // set priority on one of the constraints to 999:
    let heightConstraint = view!.heightAnchor.constraint(equalToConstant: 55)
    heightConstraint.priority = UILayoutPriority(rawValue: 999)
    // for reasoning behind this, read https://stackoverflow.com/q/44651241/2912282

    NSLayoutConstraint.activate([
        heightConstraint,
        view!.topAnchor.constraint(equalTo: contentView.topAnchor, constant: edgeInsets.top),
        view!.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -edgeInsets.bottom),
        view!.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: edgeInsets.left),
        view!.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -edgeInsets.right)
        ])
}
Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90