iOS UILabel 约束是否来自边距被破坏的代码?

Is iOS UILabel constraint from code with margin broken?

我正在尝试实现如下所示的弹出式布局:

这在带有边距和所有内容的情节提要中效果很好。在情节提要中它看起来像这样:

但是如果我在代码中进行相同的约束,我会得到这个结果:

标签的背景为浅蓝色,标签所在的视图为深蓝色背景。弹出背景有自己的边框。所以基本上弹出匹配 child 但 child 内的标签溢出 parent 和 grand parent 但只是因为它有边距......如果我删除边距它就会正确到边境!

我试过在代码中做出完全相同的约束。我非常愿意接受涉及自动调整宽度的其他建议。

我创建弹出窗口的代码:

func showPopup(caller: UIView) {
    closePopups()

    // setup view
    currentPopup = UIView()
    self.view.addSubview(currentPopup)
    currentPopup.backgroundColor = UIColorFromHex(Constants.Colors.white, alpha: 1)

    // setup constraints
    currentPopup.translatesAutoresizingMaskIntoConstraints = false

    // top constraint
    let topSideConstraint = NSLayoutConstraint(item: currentPopup, attribute: .Top, relatedBy: .Equal, toItem: intoWordsBar.view, attribute: .Bottom, multiplier: 1.0, constant: 0)
    self.view.addConstraint(topSideConstraint)

    // setup child elements
    var children = [PopupChildButton]()

    let childOne = createChild("writing_strategy_1", parent: currentPopup, aboveChild: nil, hasBorder: true, feature: FeatureManager.BarFeature.WriteReadLetterName)
    children.append(childOne)
    let childTwo = createChild("writing_strategy_2", parent: currentPopup, aboveChild: children[0], hasBorder: true, feature: FeatureManager.BarFeature.WriteReadLetterSound)
    children.append(childTwo)
    let childThree = createChild("writing_strategy_3", parent: currentPopup, aboveChild: children[1], hasBorder: true, feature: FeatureManager.BarFeature.WriteReadWord)
    children.append(childThree)
    let childFour = createChild("writing_strategy_4", parent: currentPopup, aboveChild: children[2], hasBorder: false, feature: FeatureManager.BarFeature.WriteReadSentence)
    children.append(childFour)

    let parentSize = getWidth(caller)

    //TODO MARK: <-- here working, need to add toggle function and graphics to childrens, documentation on methods, move to constructor class?

    // setup rest of constraints
    // add bottom constraint, equal to bottom of last child
    let bottomSideConstraint = NSLayoutConstraint(item: currentPopup, attribute: .Bottom, relatedBy: .Equal, toItem: children[children.count-1], attribute: .Bottom, multiplier: 1.0, constant: 0)
    self.view.addConstraint(bottomSideConstraint)

    // left constraint
    let leftSideConstraint = NSLayoutConstraint(item: currentPopup, attribute: .Left, relatedBy: .Equal, toItem: caller, attribute: .Right, multiplier: 1.0, constant: (-parentSize)/2)
    self.view.addConstraint(leftSideConstraint)

    // add border
    currentPopup.addBorder(edges: [.All], colour: UIColorFromHex(Constants.Colors.dark_grey, alpha: 1), thickness: 1)

    //TODO <-- last piece
    //childOne.addTarget(self, action: #selector(KeyboardViewController.childClick(_:)), forControlEvents: .TouchUpInside)
    //childTwo.addTarget(self, action: #selector(KeyboardViewController.childClick(_:)), forControlEvents: .TouchUpInside)
    //childThree.addTarget(self, action: #selector(KeyboardViewController.childClick(_:)), forControlEvents: .TouchUpInside)
    //childFour.addTarget(self, action: #selector(KeyboardViewController.childClick(_:)), forControlEvents: .TouchUpInside)
    self.view.setNeedsLayout()
    self.view.layoutIfNeeded()
}

我创建 child 的代码:

func createChild(text: String, parent: UIView, aboveChild: UIView?, hasBorder: Bool, feature: FeatureManager.BarFeature) -> PopupChildButton {
    // setup child element
    let childBtn = PopupChildButton()
    childBtn.setRelatedFeature(feature)

    // set the right background color
    if intoWordsBar.getFeatureManager().isFeatureActive(feature) {
        childBtn.backgroundColor = UIColorFromHex(Constants.Colors.light_blue, alpha: 1)
        //childBtn.setImage(UIImage(named: "Checkmark"))
    } else {
        childBtn.backgroundColor = UIColorAndAlphaFromHex(Constants.Colors.transparent)//TODO Highlight implementation needs to be optimized, icon should be moved all the way to the left... somehow //TODO Add new checkmark icon
        //childBtn.setImage(nil)
    }

    childBtn.translatesAutoresizingMaskIntoConstraints = false

    parent.addSubview(childBtn)


    // add constraints
    // top constraint
    if let aboveChild = aboveChild {
        let topSideConstraint = NSLayoutConstraint(item: childBtn, attribute: .Top, relatedBy: .Equal, toItem: aboveChild, attribute: .Bottom, multiplier: 1.0, constant: 0)
        parent.addConstraint(topSideConstraint)
    } else {
        let topSideConstraint = NSLayoutConstraint(item: childBtn, attribute: .Top, relatedBy: .Equal, toItem: parent, attribute: .Top, multiplier: 1.0, constant: 0)
        parent.addConstraint(topSideConstraint)
    }

    // height constraint
    let heightConstraint = NSLayoutConstraint(item: childBtn, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: CGFloat(Constants.Sizes.popupChildHeight))
    parent.addConstraint(heightConstraint)

    // left constraint
    let leftSideConstraint = NSLayoutConstraint(item: parent, attribute: .Leading, relatedBy: .Equal, toItem: childBtn, attribute: .Leading, multiplier: 1.0, constant: 0)
    parent.addConstraint(leftSideConstraint)

    // right constraint
    let rightSideConstraint = NSLayoutConstraint(item: parent, attribute: .Trailing, relatedBy: .Equal, toItem: childBtn, attribute: .Trailing, multiplier: 1.0, constant: 0)
    parent.addConstraint(rightSideConstraint)

    // add border
    if hasBorder {
        childBtn.addBorder(edges: .Bottom, colour: UIColorFromHex(Constants.Colors.dark_grey, alpha: 1), thickness: 1)
    }

    // create grandchildren
    let label = UILabel()

    // setup looks
    label.textColor = UIColorFromHex(Constants.Colors.black, alpha: 1)
    label.textAlignment = .Center

    childBtn.backgroundColor = UIColorFromHex(Constants.Colors.dark_blue, alpha: 1)
    label.backgroundColor = UIColorFromHex(Constants.Colors.light_blue, alpha: 1)
    label.text = text.localized

    label.translatesAutoresizingMaskIntoConstraints = false

    childBtn.addSubview(label)

    // add constraints
    // left constraint label
    let leftLabelConstraint = NSLayoutConstraint(item: label, attribute: .Left, relatedBy: .Equal, toItem: childBtn, attribute: .Left, multiplier: 1.0, constant: CGFloat(Constants.Sizes.popupMargin))
    childBtn.addConstraint(leftLabelConstraint)

    // right constraint label
    let rightLabelConstraint = NSLayoutConstraint(item: label, attribute: .Right, relatedBy: .Equal, toItem: childBtn, attribute: .Right, multiplier: 1.0, constant: CGFloat(Constants.Sizes.popupMargin))
    childBtn.addConstraint(rightLabelConstraint)

    // top constraint
    let labelTopSideConstraint = NSLayoutConstraint(item: label, attribute: .Top, relatedBy: .Equal, toItem: childBtn, attribute: .Top, multiplier: 1.0, constant: 0)
    childBtn.addConstraint(labelTopSideConstraint)

    // bottom constraint
    //let labelBottomSideConstraint = NSLayoutConstraint(item: label, attribute: .Bottom, relatedBy: .Equal, toItem: childBtn, attribute: .Bottom, multiplier: 1.0, constant: 0)
    //childBtn.addConstraint(labelBottomSideConstraint)


    return childBtn
}

不,它没有坏。 定义尾随约束时,必须将 parent 视图设置为第一项,将 child 视图设置为第二项。与前导约束相比,这是相反的顺序。

我从故事板中提取了约束来说明这一点。这些约束确保 header 与 parent 视图的前导和尾随有 10 像素的边距。