0

When hiding subviews of UIStackview in tableviewcell, sometimes autolayout conflicts occured.

I tried also addArrangedSubview and removeArrangedSubview instead of hiding and unhiding. But it was same result.

The uistackview has four subviews. I printed the hidden status each subviews. As you can see below, first, second, third view is already unhidden. But autolayout can't get that status. CellHeight is set as expected, 313(basic cell height is 157, each subview's height 52. so 313). I know the reason conflict occured is cell height is already set as 313, but subviews' height isn't 313. Because second and third view isn't recognized yet.

The funny thing is UI works perfectly. Just conflict warnings. and the conflict isn't occured every time. Sometime when I unhide second view, it occured, sometime adding second view is Ok, but not third view.

I really want to know the exact reason and eliminate that warnings.

Thank you for your help.

firstChildAgeView.isHidden : NO
secondChildAgeView.isHidden : NO
thirdChildAgeView.isHidden : NO
forthChildAgeView.isHidden : YES

cellHeight : 313.0
2019-06-04 23:07:38.076484+0900 allstay[80237:18107365] [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:0x6000011c9a40 firstChildAgeView.height == 52   (active, names: firstChildAgeView:0x7fb68e0423c0 )>",
    "<NSLayoutConstraint:0x6000011c0aa0 V:|-(0)-[containerStackView]   (active, names: containerStackView:0x7fb68e03c6a0, '|':allstay.RoomCountViewCell:0x7fb68f898800'RoomCountViewCell' )>",
    "<NSLayoutConstraint:0x6000011c0b40 V:[containerStackView]-(10)-[UIView:0x7fb68e03c150]   (active, names: containerStackView:0x7fb68e03c6a0 )>",
    "<NSLayoutConstraint:0x6000011c0b90 UIView:0x7fb68e03c150.height == 1   (active)>",
    "<NSLayoutConstraint:0x6000011c0be0 V:[UIView:0x7fb68e03c150]-(0)-|   (active, names: '|':allstay.RoomCountViewCell:0x7fb68f898800'RoomCountViewCell' )>",
    "<NSLayoutConstraint:0x6000011c0e10 roomTitleContainerView.height == 32   (active, names: roomTitleContainerView:0x7fb68e03bbf0 )>",
    "<NSLayoutConstraint:0x6000011c0e60 roomAdultContainerView.height == 52   (active, names: roomAdultContainerView:0x7fb68e03b210 )>",
    "<NSLayoutConstraint:0x6000011c0eb0 roomChildrenContainerView.height == 52   (active, names: roomChildrenContainerView:0x7fb68e03bf70 )>",
    "<NSLayoutConstraint:0x6000011c28a0 'UISV-canvas-connection' containerStackView.top == roomTitleContainerView.top   (active, names: containerStackView:0x7fb68e03c6a0, roomTitleContainerView:0x7fb68e03bbf0 )>",
    "<NSLayoutConstraint:0x6000011c28f0 'UISV-canvas-connection' V:[firstChildAgeView]-(0)-|   (active, names: containerStackView:0x7fb68e03c6a0, firstChildAgeView:0x7fb68e0423c0, '|':containerStackView:0x7fb68e03c6a0 )>",
    "<NSLayoutConstraint:0x6000011c2940 'UISV-spacing' V:[roomTitleContainerView]-(10)-[roomAdultContainerView]   (active, names: roomAdultContainerView:0x7fb68e03b210, roomTitleContainerView:0x7fb68e03bbf0 )>",
    "<NSLayoutConstraint:0x6000011c2990 'UISV-spacing' V:[roomAdultContainerView]-(0)-[roomChildrenContainerView]   (active, names: roomChildrenContainerView:0x7fb68e03bf70, roomAdultContainerView:0x7fb68e03b210 )>",
    "<NSLayoutConstraint:0x6000011c29e0 'UISV-spacing' V:[roomChildrenContainerView]-(0)-[firstChildAgeView]   (active, names: firstChildAgeView:0x7fb68e0423c0, roomChildrenContainerView:0x7fb68e03bf70 )>",
    "<NSLayoutConstraint:0x6000011c30c0 'UIView-Encapsulated-Layout-Height' allstay.RoomCountViewCell:0x7fb68f898800'RoomCountViewCell'.height == 313   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x6000011c2940 'UISV-spacing' V:[roomTitleContainerView]-(10)-[roomAdultContainerView]   (active, names: roomAdultContainerView:0x7fb68e03b210, roomTitleContainerView:0x7fb68e03bbf0 )>

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.

EDIT: UPDATE CODE

This is whole code for making cell.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "countViewCell", for: indexPath) as! countViewCell
        countArray = info.getCount()

        cell.setupViews(vc: self, countInfo: countArray[indexPath.item], row:indexPath.item)

        if countArray.count == 1 {
            cell.removeCellButton.isHidden = true
        } else {
            cell.removeCellButton.isHidden = false
        }

        if countArray.count == 4 {
            countTableView.tableFooterView?.isHidden = true
        } else {
            countTableView.tableFooterView?.isHidden = false
        }

        return cell
    }

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        roomArray = info.getCount()
        let childrenCount = countArray[indexPath.item].components(separatedBy: ",").count - 1

        return 157 + CGFloat(childrenCount * 52)
    }

func changeChildrenCountRow(row:Int) {

        let range = Range(NSRange(location: 0, length: 1))

        let sectionToReload = IndexSet(integersIn: range!)
        countTableView.reloadSections(sectionToReload, with: .fade)
    }



cell class ----------------------------------------------------------

func setupViews(vc:countViewController, guestInfo:String, row:Int) {

        countViewController = vc

        numberLabel.text = "\(row+1)"

        let guestArray = guestInfo.components(separatedBy: ",")
        childrenCount = guestArray.count - 1
        adultCount = Int(guestArray[0])!

        adultCountLabel.text = "\(adultCount)"
        childrenCountLabel.text = "\(childrenCount)"

        setChildrenAgeViews()

        addSubview(containerStackView)
        addSubview(separatorView)
        containerStackView.addArrangedSubview(titleContainerView)
        containerStackView.addCustomSpacing(10, after: titleContainerView)
        containerStackView.addArrangedSubview(adultContainerView)
        containerStackView.addArrangedSubview(childrenContainerView)
        titleContainerView.addSubview(numberLabel)
        titleContainerView.addSubview(removeButton)
        adultContainerView.addSubview(adultLabel)
        adultContainerView.addSubview(reduceAdultCountButton)
        adultContainerView.addSubview(adultCountLabel)
        adultContainerView.addSubview(addAdultCountButton)
        childrenContainerView.addSubview(childrenLabel)
        childrenContainerView.addSubview(reduceChildrenCountButton)
        childrenContainerView.addSubview(childrenCountLabel)
        childrenContainerView.addSubview(addChildrenCountButton)

        firstChildAgeView.accessibilityIdentifier = "firstChildAgeView"
        secondChildAgeView.accessibilityIdentifier = "secondChildAgeView"
        thirdChildAgeView.accessibilityIdentifier = "thirdChildAgeView"
        forthChildAgeView.accessibilityIdentifier = "forthChildAgeView"
        titleContainerView.accessibilityIdentifier = "titleContainerView"
        adultContainerView.accessibilityIdentifier = "adultContainerView"
        childrenContainerView.accessibilityIdentifier = cChildrenContainerView"
        containerStackView.accessibilityIdentifier = "containerStackView"

        firstChildAgeView.addSubview(firstChildAgeButton)
        firstChildAgeView.addConstraintsWithFormat(format: "H:[v0(80)]-29-|", views: firstChildAgeButton)
        firstChildAgeView.addConstraintsWithFormat(format: "V:|[v0]|", views: firstChildAgeButton)

        secondChildAgeView.addSubview(secondChildAgeButton)
        secondChildAgeView.addConstraintsWithFormat(format: "H:[v0(80)]-29-|", views: secondChildAgeButton)
        secondChildAgeView.addConstraintsWithFormat(format: "V:|[v0]|", views: secondChildAgeButton)

        thirdChildAgeView.addSubview(thirdChildAgeButton)
        thirdChildAgeView.addConstraintsWithFormat(format: "H:[v0(80)]-29-|", views: thirdChildAgeButton)
        thirdChildAgeView.addConstraintsWithFormat(format: "V:|[v0]|", views: thirdChildAgeButton)

        forthChildAgeView.addSubview(forthChildAgeButton)
        forthChildAgeView.addConstraintsWithFormat(format: "H:[v0(80)]-29-|", views: forthChildAgeButton)
        forthChildAgeView.addConstraintsWithFormat(format: "V:|[v0]|", views: forthChildAgeButton)

        print("firstChildAgeView.isHidden : \(firstChildAgeView.isHidden ? "YES" : "NO")")
        print("secondChildAgeView.isHidden : \(secondChildAgeView.isHidden ? "YES" : "NO")")
        print("thirdChildAgeView.isHidden : \(thirdChildAgeView.isHidden ? "YES" : "NO")")
        print("forthChildAgeView.isHidden : \(forthChildAgeView.isHidden ? "YES" : "NO")")

        containerStackView.addArrangedSubview(firstChildAgeView)
        containerStackView.addConstraintsWithFormat(format: "H:|[v0]|", views: firstChildAgeView)
        containerStackView.addConstraintsWithFormat(format: "V:[v0(52)]", views: firstChildAgeView)
        containerStackView.addArrangedSubview(secondChildAgeView)
        containerStackView.addConstraintsWithFormat(format: "H:|[v0]|", views: secondChildAgeView)
        containerStackView.addConstraintsWithFormat(format: "V:[v0(52)]", views: secondChildAgeView)
        containerStackView.addArrangedSubview(thirdChildAgeView)
        containerStackView.addConstraintsWithFormat(format: "H:|[v0]|", views: thirdChildAgeView)
        containerStackView.addConstraintsWithFormat(format: "V:[v0(52)]", views: thirdChildAgeView)
        containerStackView.addArrangedSubview(forthChildAgeView)
        containerStackView.addConstraintsWithFormat(format: "H:|[v0]|", views: forthChildAgeView)
        containerStackView.addConstraintsWithFormat(format: "V:[v0(52)]", views: forthChildAgeView)

        addConstraintsWithFormat(format: "H:|[v0]|", views: containerStackView)
        addConstraintsWithFormat(format: "H:|[v0]|", views: separatorView)
        addConstraintsWithFormat(format: "V:|[v0]-10-[v1(1)]|", views: containerStackView, separatorView)

        let tempConstraint = NSLayoutConstraint(item: separatorView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
        tempConstraint.priority = .defaultLow

        NSLayoutConstraint.activate([
            containerStackView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            containerStackView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            containerStackView.topAnchor.constraint(equalTo: self.topAnchor),
            containerStackView.bottomAnchor.constraint(equalTo: separatorView.topAnchor, constant: -10),
            separatorView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            separatorView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            separatorView.heightAnchor.constraint(equalToConstant: 1),
            tempConstraint
            ])

        containerStackView.addConstraintsWithFormat(format: "H:|[v0]|", views: titleContainerView)
        containerStackView.addConstraintsWithFormat(format: "H:|[v0]|", views: adultContainerView)
        containerStackView.addConstraintsWithFormat(format: "H:|[v0]|", c: childrenContainerView)
        containerStackView.addConstraintsWithFormat(format: "V:[v0(32)]", views: titleContainerView)
        containerStackView.addConstraintsWithFormat(format: "V:[v0(52)]", views: adultContainerView)
        containerStackView.addConstraintsWithFormat(format: "V:[v0(52)]", c: childrenContainerView)

        titleContainerView.addConstraintsWithFormat(format: "H:|-20-[v0][v1(100)]-20-|", views: numberLabel, removeButton)
        titleContainerView.addConstraintsWithFormat(format: "V:|[v0]|", views: numberLabel)
        titleContainerView.addConstraintsWithFormat(format: "V:|[v0]|", views: removeButton)

        adultContainerView.addConstraintsWithFormat(format: "H:|-20-[v0][v1(28)][v2(60)][v3(28)]-20-|", views: adultLabel, reduceAdultCountButton, adultCountLabel, addAdultCountButton)
        adultContainerView.addConstraintsWithFormat(format: "V:|[v0]|", views: adultLabel)
        adultContainerView.addConstraintsWithFormat(format: "V:|-12-[v0(28)]-12-|", views: reduceAdultCountButton)
        adultContainerView.addConstraintsWithFormat(format: "V:|[v0]|", views: adultCountLabel)
        adultContainerView.addConstraintsWithFormat(format: "V:|-12-[v0(28)]-12-|", views: addAdultCountButton)

        childrenContainerView.addConstraintsWithFormat(format: "H:|-20-[v0][v1(28)][v2(60)][v3(28)]-20-|", views: childrenLabel,
                                                           reduceChildrenCountButton, childrenCountLabel, addChildrenCountButton)
        childrenContainerView.addConstraintsWithFormat(format: "V:|[v0]|", views: childrenLabel)
        childrenContainerView.addConstraintsWithFormat(format: "V:|-12-[v0(28)]-12-|", views: reduceChildrenCountButton)
        childrenContainerView.addConstraintsWithFormat(format: "V:|[v0]|", views: childrenCountLabel)
        childrenContainerView.addConstraintsWithFormat(format: "V:|-12-[v0(28)]-12-|", views: addChildrenCountButton)

        removeButton.addTarget(self, action: #selector(remove), for: .touchUpInside)

        addAdultCountButton.addTarget(self, action: #selector(countChangeAction(sender:)), for: .touchUpInside)
        reduceAdultCountButton.addTarget(self, action: #selector(countChangeAction(sender:)), for: .touchUpInside)
        addChildrenCountButton.addTarget(self, action: #selector(countChangeAction(sender:)), for: .touchUpInside)
        reduceChildrenCountButton.addTarget(self, action: #selector(countChangeAction(sender:)), for: .touchUpInside)

    }

func setChildrenAgeViews() {
        var childrenViewArray = [firstChildAgeView, secondChildAgeView, thirdChildAgeView, forthChildAgeView]
        for index in 0..<4 {
            if index < childrenCount {
                childrenViewArray[index].isHidden = false
            } else {
                childrenViewArray[index].isHidden = true
            }
        }
    }

EDIT 2: Update warning message

"<NSLayoutConstraint:0x6000023699a0 V:|-(0)-[containerStackView]   (active, names: containerStackView:0x7fa33ad23270, '|':allstay.RoomCountViewCell:0x7fa33c068600'RoomCountViewCell' )>",
    "<NSLayoutConstraint:0x600002369900 V:[containerStackView]-(10)-[UIView:0x7fa33ad3d6f0]   (active, names: containerStackView:0x7fa33ad23270 )>",
    "<NSLayoutConstraint:0x6000023698b0 UIView:0x7fa33ad3d6f0.height == 1   (active)>",
    "<NSLayoutConstraint:0x600002369860 V:[UIView:0x7fa33ad3d6f0]-(0)-|   (active, names: '|':allstay.RoomCountViewCell:0x7fa33c068600'RoomCountViewCell' )>",
    "<NSLayoutConstraint:0x600002369540 roomTitleContainerView.height == 32   (active, names: roomTitleContainerView:0x7fa33ad3c880 )>",
    "<NSLayoutConstraint:0x600002369590 roomAdultContainerView.height == 52   (active, names: roomAdultContainerView:0x7fa33ad3d0e0 )>",
    "<NSLayoutConstraint:0x6000023692c0 roomChildrenContainerView.height == 52   (active, names: roomChildrenContainerView:0x7fa33ad3d510 )>",
    "<NSLayoutConstraint:0x600002369630 firstChildAgeView.height == 52   (active, names: firstChildAgeView:0x7fa33ad435c0 )>",
    "<NSLayoutConstraint:0x60000236ac10 secondChildAgeView.height == 52   (active, names: secondChildAgeView:0x7fa33ad43bd0 )>",
    "<NSLayoutConstraint:0x60000237ae90 'UISV-canvas-connection' containerStackView.top == roomTitleContainerView.top   (active, names: containerStackView:0x7fa33ad23270, roomTitleContainerView:0x7fa33ad3c880 )>",
    "<NSLayoutConstraint:0x60000236c140 'UISV-canvas-connection' V:[secondChildAgeView]-(0)-|   (active, names: containerStackView:0x7fa33ad23270, secondChildAgeView:0x7fa33ad43bd0, '|':containerStackView:0x7fa33ad23270 )>",
    "<NSLayoutConstraint:0x60000236c4b0 'UISV-spacing' V:[roomTitleContainerView]-(10)-[roomAdultContainerView]   (active, names: roomAdultContainerView:0x7fa33ad3d0e0, roomTitleContainerView:0x7fa33ad3c880 )>",
    "<NSLayoutConstraint:0x60000236c500 'UISV-spacing' V:[roomAdultContainerView]-(0)-[roomChildrenContainerView]   (active, names: roomChildrenContainerView:0x7fa33ad3d510, roomAdultContainerView:0x7fa33ad3d0e0 )>",
    "<NSLayoutConstraint:0x60000236c550 'UISV-spacing' V:[roomChildrenContainerView]-(0)-[firstChildAgeView]   (active, names: firstChildAgeView:0x7fa33ad435c0, roomChildrenContainerView:0x7fa33ad3d510 )>",
    "<NSLayoutConstraint:0x60000236c5a0 'UISV-spacing' V:[firstChildAgeView]-(0)-[secondChildAgeView]   (active, names: secondChildAgeView:0x7fa33ad43bd0, firstChildAgeView:0x7fa33ad435c0 )>",
    "<NSLayoutConstraint:0x6000023777f0 'UIView-Encapsulated-Layout-Height' allstay.RoomCountViewCell:0x7fa33c068600'RoomCountViewCell'.height == 365   (active)>"
Justin.Shim
  • 317
  • 2
  • 14
  • I'm assuming you have a bottom constraint from the stack view to the bottom of the cell? If so, change the priority of that constraint to `999` - layout should remain unchanged, but the constraint warnings should go away. – DonMag Jun 04 '19 at 14:28
  • @DonMag actually cell has separator view at the bottom. I made constraints with VFL. addConstraintsWithFormat(format: "V:|[v0]-10-[v1(1)]|", views: containerStackView, separatorView). I changed VFL to normal NSLayoutConstraint and give separatorView's bottom constraint priority as low. But nothing changes. The warning keep showing. – Justin.Shim Jun 04 '19 at 14:58
  • So, you're creating your cell via code? Can you show the code for your cell? And... are you hiding the view *after* the cell is shown? If so, show how you're doing that as well. – DonMag Jun 04 '19 at 15:13
  • @DonMag I update code. I am not sure the info is enough. As you said I create whole UI by code. also autolayout. Hiding those subviews when cellForRowAt indexPath: is excuted.(cell.setupViews) – Justin.Shim Jun 05 '19 at 03:05
  • Unfortunately, I would need a bit more of your code to try and run it to see exactly what's happening (and I'm on my way out of town for a week), however... I'll leave an answer that *may* solve your issue. – DonMag Jun 05 '19 at 13:48

1 Answers1

1

First thing that jumps out at me: get rid of your heightForRowAt func. Allow auto-layout to handle the cell height based on proper constraints of the cell's content.

It looks like you're on the right track though. If I understand your layout, you have:

  • containerStackView (which holds some arranged subviews), and
  • separatorView

as the two "top-level" elements in your cell. You are constraining:

  • the top of containerStackView to the top of the cell,
  • the bottom of containerStackView to the top of separatorView, and
  • the bottom of separatorView to the bottom of the cell

Which should satisfy auto-layout, and allow the row heights to be handled automatically -- no need at all to calculate in heightForRowAt.

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Yes, You understand setting of my constraint exactly. And I thought I have to add heightForRowAt to control cell height. Thank you for your advice. But unfortunately, there's still warning. The warning message slightly change. The problem is cell height. I updated the message. The containerStackView has titleview, adultCountView, ChildrenCountView(its height is 157.) and four childrenAgeView can be hidden(each view height is 52). As you can see in the warning message firstview and secondview is unhidden. so Height should be 261 but cell says 365. – Justin.Shim Jun 10 '19 at 06:13
  • it's because I unhiddened all four views and hide third, forth view. The thing I don't understand is UI works perfectly, Autolayout also says the each view hide/unhide successfully. But not cell height. it works like a step behind. Unhiding firstView makes height 209, but 157, unhiding until secondView makes height 261, but 209, unhiding until thirdView makes height 313, but 261, unhiding until forthView makes height 365, but 313, hiding forthView makes height 313, but 365, hiding thirdView makes height 261, but 313...Maybe the reason is I don't know when cell height would be calculated. – Justin.Shim Jun 10 '19 at 06:22
  • I found the reason why it works a step behind. It's because dequeueReusableCell. the reusable cell has the before height and change the view hidden state. so it doesn't match. so I set the different identifier per each view. none children view cell, having one child view cell, having two children view cell, etc... And the warning message is gone. I'm not sure it was proper way but problem has solved. Thanks to @DonMag. – Justin.Shim Jun 10 '19 at 07:01