4
votes

I have an issue with auto layout constraints for UIView inside the UIStackView.

My goal is to create a view:

private static func makeSpace(with value: CGFloat) -> UIView {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = true

    let heightConstraint =  view.heightAnchor.constraint(lessThanOrEqualToConstant: value)
    heightConstraint.priority = UILayoutPriorityDefaultHigh

    NSLayoutConstraint.activate([
        view.widthAnchor.constraint(equalToConstant: 295),
        heightConstraint
    ])

    return view
}

I want to add this as an arranged subview to UIStackView as a space between other UIStackView arranged subviews.

When I inspect these views by using visual inspector, my space view has height 0. Although, when I change constraint to equalToConstant, height is calculated properly.

I want to use lessThanOrEqualToConstant to allow these spaces to shrink in case the screen layout is too small to fit them in proper size.

Has anyone else faced this problem?

3
aren't you supposed to enter some value in that field ? (CGFloat I guess?)Ashish Bahl
What do you mean? I am passing a value: CGFloat to constraint...Daumantas Versockas
The answer depends on the exact behaviour you are trying to achieve, but you can try setting the verticalCompressionResistance of your view to 1000. This should make your spacing view shrink only when needed. You can also make the heightConstraint required instead of only high priority.David Ganster
lessThanOrEqualToConstant constraint is not sufficient to make autolayout know the acutal height but will work only if the view is hooked top and bottom to a resizable element like UILabelSh_Khan
@DavidGanster, vertical compression resistance doesn't work. Actually, I don't see why it should, because, it is used for views with intrinsic size.Daumantas Versockas

3 Answers

0
votes

The code works, because 0 is in fact less than or equal to 295. If you want it 295 or less depending on other views in stack, you need another constraint telling it to expand to 295 if it is possible:

private static func makeSpace(with value: CGFloat) -> UIView {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = true

    // this keeps it under the value, we want this to be enforced, so priority stays `required`
    let heightConstraint1 =  view.heightAnchor.constraint(lessThanOrEqualToConstant: value)

    // this constraint will drive the size (the other one is just correcting it)
    let heightConstraint2 =  view.heightAnchor.constraint(equalToConstant: value)
    // modify this priority according to compression resistance of the other views
    heightConstraint2.priority = UILayoutPriority(rawValue: 240)

    NSLayoutConstraint.activate([
        view.widthAnchor.constraint(equalToConstant: 295),
        heightConstraint1,
        heightConstraint2
    ])

    return view
}

The first constraint keeps the space under the value constant (e.g. 295). This constraint has to have required priority, because you don't want to extend this height by any means.

The second constraint tells it to keep the height equal to value. But it has a lower priority. So other constraints can override it. If that happens, the autolayout system will try to get as close to fulfilling it as possible - so if it cannot be 295, but it can be 230, that will happen.

Side note:

If you don't care about expanding it to bigger value than value (in example 295), then you don't need the first constraint at all. I kept it there because I assume that is the limit for the space that has to be kept.

Another side note:

CompressionResistancePriority and ContentHuggingPriority are meant for views with intrinsic size, which does not apply to a UIView.

0
votes

See this error in layout happing with less than Constaint enter image description here

but with top and bottom resizable like UILabel it works enter image description here

0
votes
**//1** 

 let child = UIView()
  child.translatesAutoresizingMaskIntoConstraints = false
  child.backgroundColor = .red
  view.addSubview(child)


**//2**

  child.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
  child.bottomAnchor.constraint(equalTo:     view.safeAreaLayoutGuide.bottomAnchor).isActive = true
  child.widthAnchor.constraint(equalToConstant: 128).isActive = true
  child.centerXAnchor.constraint(equalTo:   view.safeAreaLayoutGuide.centerXAnchor).isActive = true