简体   繁体   中英

Autolayout conflict when hiding subview of UIStackview

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)>"

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM