Swift programmatically-added constraints dont work

不羁岁月 提交于 2020-04-30 06:46:07

问题


I have a view inside a scrollview:

Now I want to add the child views to that view programmatically. Thats my code to do that:

  1. Child view (works)

    //Adds header to the view again because it was removed in the clear() method
    //Header is just a label
    lv.addSubview(header)
    header.leadingAnchor.constraint(equalTo: header.superview!.leadingAnchor).isActive = true
    header.topAnchor.constraint(equalTo: header.superview!.topAnchor, constant: 2).isActive = true
    header.widthAnchor.constraint(equalTo: header.superview!.widthAnchor).isActive = true
    header.heightAnchor.constraint(equalToConstant: MainTabViewController.fontSize*3).isActive = true //Just a constant
    

Now I execute this code repeatedly:

private func makeTextLabel(text: NSAttributedString, bgColor: UIColor?, maxWidth: CGFloat?) -> UILabel {
    //Creates label
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    lv.addSubview(label)
    //Adjustments
    if(bgColor != nil) {
        label.backgroundColor = bgColor
    }
    label.textColor = UIColor.black
    label.attributedText = text
    label.numberOfLines = 0
    label.lineBreakMode = NSLineBreakMode.byWordWrapping
    let width = maxWidth ?? lv.frame.size.width-8
    label.widthAnchor.constraint(equalToConstant: width).isActive = true
    label.heightAnchor.constraint(equalToConstant: heightForLabel(attributedText: label.attributedText, width: width)).isActive = true
    label.leadingAnchor.constraint(equalTo: lv.leadingAnchor, constant: 4).isActive = true
    let previousView = lv.subviews[lv.subviews.count-1]
    label.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant:  10).isActive = true
    return label
}

All the labels are added, but the constraints dont work at all. Here is what is looks like (when I execute the method above 2 times):

EDIT: The main problem is solved. I am using a StackView now.(https://stackoverflow.com/a/59828434/6257435)

I now want my labels to have an offset to the edges, so I use those lines:

    label.leadingAnchor.constraint(equalTo: lessonStackView.leadingAnchor, constant: offset).isActive = true
    label.trailingAnchor.constraint(equalTo: lessonStackView.trailingAnchor, constant: -offset).isActive = true

But as the StackView seems to set x-constraints itself, I get this warning:

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. ( "UILabel:0x7f9c16c22c30'Falls dir die App gef\U00e4llt...' (active, names: '|':JavaProf.MultipleContentsView:0x7f9c19024a60 )>", "", "" )

Will attempt to recover by breaking constraint

How can I solve that?


回答1:


Remove width anchor. You should not care about width (and you don't really know it), use trailingAnchor instead:

label.trailingAnchor.constraint(equalTo: lv.trailingAnchor, constant: -4).isActive = true

Remove heightAnchor, instead let system calculate the height for you:

label.setContentHuggingPriority(.required, for: .vertical)
label.setContentCompressionResistancePriority(.required, for: .vertical)

That should be all needed.

However, as a side note, instead of creating a constraint to previous label, you could just use a UIStackView.




回答2:


If you want to use a UIStackView for a stack of labels (or other views) and you want them to have different leading spacing, you have a couple options:

1) Embed the label in a UIView. Let the UIView fill the width of the stack view, and give the contained label constraints to the view.

2) Set the stack view Aligment to Trailing. Then add a width constraint to each label, equal to the width of the stack view. For a label you want to have, say, a 32-point "left margin," set the constant on that label to -32.

Here is a quick example:

class LeftMarginViewController: UIViewController {
    let stackView: UIStackView = {
        let v = UIStackView()
        v.axis = .vertical
        v.alignment = .trailing
        v.distribution = .fill
        v.spacing = 8
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        for i in 1...7 {
            let v = UILabel()
            v.backgroundColor = .yellow
            v.numberOfLines = 0
            if i == 5 {
                v.text = "Label 5 has a lot of text to demonstrate what happens when it needs to wrap onto multiple lines."
            } else {
                v.text = "Label \(i)"
            }
            stackView.addArrangedSubview(v)
            switch i {
            case 6:
                v.widthAnchor.constraint(equalTo: stackView.widthAnchor, multiplier: 1.0, constant: -32.0).isActive = true
            case 2, 4, 5:
                v.widthAnchor.constraint(equalTo: stackView.widthAnchor, multiplier: 1.0, constant: -16.0).isActive = true
            default:
                v.widthAnchor.constraint(equalTo: stackView.widthAnchor, multiplier: 1.0, constant: 0.0).isActive = true
            }
        }

        stackView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackView)

        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
        ])

    }
}

Stack view has Alignment: Trailing, each label has width equal to stack view width, labels 2, 4 and 5 have constant = -16 and label 6 has constant = -32.

Result:



来源:https://stackoverflow.com/questions/59917652/swift-programmatically-added-constraints-dont-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!