0

I have a view inside a scrollview:

enter image description here

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

enter image description here

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?

2 Answers 2

1

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:

enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the answer. I did not use exactly this approach, but I changed the alignment of my StackView to center instead of fill. Now the warning is gone and everything is working fine
0

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.

3 Comments

I am using a StackView now. I updated my question, it would be great, if you took another look at it!
If you want offsets from the edges, just offset the whole stack view. That's the easiest method.
Thanks, but the offset depends on the different labels, so I can't do that in this case

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.