ios - 隐藏 UIStackview 的 subview 时自动布局冲突

标签 ios swift autolayout conflict stackview

在tableviewcell中隐藏UIStackview的 subview 时,有时会发生autolayout冲突。

我还尝试了 addArrangedSubview 和 removeArrangedSubview 而不是隐藏和取消隐藏。但结果是一样的。

uistackview 有四个 subview 。 我打印了每个 subview 的隐藏状态。 正如您在下面看到的,第一、第二、第三 View 已经取消隐藏。但是自动布局无法获得该状态。 CellHeight 按预期设置,313(基本单元格高度为 157,每个 subview 的高度为 52。因此为 313)。我知道发生冲突的原因是单元格高度已设置为 313,但 subview 的高度不是 313。因为第二个和第三个 View 尚未被识别。

有趣的是 UI 完美运行。只是冲突警告。并且冲突并非每次都发生。有时当我取消隐藏第二个 View 时,它发生了,有时添加第二个 View 是好的,但不是第三个 View 。

我真的很想知道确切的原因并消除警告。

感谢您的帮助。

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.

编辑:更新代码

这是制作单元格的完整代码。

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
            }
        }
    }

编辑 2:更新警告消息

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

最佳答案

首先让我印象深刻的是:去掉你的 heightForRowAt 函数。允许自动布局根据单元格内容的适当约束来处理单元格高度。

不过,看起来您的方向是对的。如果我理解你的布局,你有:

  • containerStackView(其中包含一些已排列的 subview ),以及
  • separatorView

作为单元格中的两个“顶级”元素。您正在限制:

  • containerStackViewtop 到 cell 的top
  • containerStackView底部separatorView顶部,以及
  • separatorViewbottom到cell的bottom

哪个应该满足自动布局,并允许自动处理行高——根本不需要在 heightForRowAt 中计算。

关于ios - 隐藏 UIStackview 的 subview 时自动布局冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56445834/

相关文章:

ios - Apple Watch 通知镜像

ios - UITableViewCell 按钮在屏幕下方加载时未注册单击

ios - if 语句中的 switch 语句

ios - 超越您自己的应用程序的 UI 测试自动化?

ios - 如何将 UIImageView 限制在另一个 View 中?

ios - Storyboard 变得不稳定。编译但有时自动布局变得疯狂

ios - 将 TextKit 与 Storyboard结合使用

ios - CollectionView 单元格在滚动后更改大小 - AutoLayout

ios - 如何代表App Store位置更改iOS应用程序图标和背景?

ios - 如何从外部访问for循环并使其 swift 停止