0
votes

I have a UIStackView (horizontal) addArrangedSubview into a UIStackView (vertical).

The horizontal view has 3 cells: the first is addArrangedSubview with a UILabel, the second has an UIImageView (with width and height constant constraint to keep the size I want). The third cell is addArrangedSubview with a UILabel. I expect the UIStackView to fit the whole screen width

Unfortunately, the result is stretched as follows:

enter image description here

My code is as follows:

    let sv = UIStackView()
    sv.axis = .horizontal
    sv.alignment = .center
    sv.distribution = .fill
    sv.spacing = 10

    // Name
        var label = UILabel()
        label.numberOfLines = 0
        label.textAlignment = .natural
        label.text = poi.name
        label.font = UIFont.preferredFont(forTextStyle: .title1)
        sv.addArrangedSubview(label)

    // Open/closed
        let open = true
        let _ = addImageView(open ? #imageLiteral(resourceName: "Open") : #imageLiteral(resourceName: "Closed"), to: sv)

    // Stars
        let maxRank = 5
        let rank = 3
        label = UILabel()
        label.numberOfLines = 0
        label.textAlignment = .natural
        label.text = "".padding(right: rank, withPad: "★").padding(right: maxRank-rank, withPad: "☆")
        sv.addArrangedSubview(label)

    verticalStackView.addArrangedSubview(sv)

And my tool function as follows:

    let w: CGFloat = 32.0
    let r = CGRect(x: 0, y: 0, width: w, height: w)

    func addImageView(_ image: UIImage, to sv: UIStackView) {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFit
        iv.image = image

        //iv.translatesAutoresizingMaskIntoConstraints = true
        let constraints = [
            iv.widthAnchor .constraint(equalToConstant: w),
            iv.heightAnchor.constraint(equalToConstant: w)
        ]
        //let _ = constraints.map { $0.priority = .defaultHigh }
        NSLayoutConstraint.activate(constraints)
        /*
        iv.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1000.0), for: .horizontal)
        iv.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1000.0), for: .vertical)
        */
        if true {
            sv.addArrangedSubview(iv)
        } else {
            let v = UIView()
            v.layer.borderColor = UIColor.red.cgColor
            v.layer.borderWidth = 1
            v.addSubview(iv)
            sv.addArrangedSubview(v)
        }
    }

Note 1: I did some experiments hence the commented code.

Note 2: the red bars on the screenshot are my hand drawing to ease understanding where the cells lay out.

[UPDATE]

To clear things up, what messes everything up, is the constraint I added to the UIImageView, with none of it, everything is ok. The reason I added the constraint, is to keep the UIImageView squared and at the right dimensions. For this reason, I added a UIView as an in-between element to absorb the strength the UIStackView contraints set to the inner element (Hence the if false code). But then I need to add extra constraints in the UIView to center the UIImageView, which sounds a bit overuse of the layout system. Certainly I probably missed something that streamline the whole thing.

2

2 Answers

0
votes

Can you - try adding constraints for Root-StackView, with trailing, top, bottom and leading equal to constant 0. - try changing the alignment of the Root-StackView with .fill instead of .center.

  • if you want to make the label more flexible, make the compression resistance and context hugging property with much less value(normally 248, 748) than others(251, 250, 750).

thanks, J

0
votes

I finally fixed the issue: I changed the resistance compression priority to .required for the label and the UIImageView:

label.setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal)

The same pice of code now reads as follows:

// Name
    var label = UILabel()
    label.numberOfLines = 0
    label.textAlignment = .natural
    label.text = poi.name
    label.font = UIFont.preferredFont(forTextStyle: .title1)
    label.setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal)

    sv.addArrangedSubview(label)

// Open/closed
    let open = true
    let _ = addImageView(open ? #imageLiteral(resourceName: "Open") : #imageLiteral(resourceName: "Closed"), to: sv)

// Stars
    let maxRank = 5
    let rank = 3
    label = UILabel()
    label.numberOfLines = 0
    label.textAlignment = .natural
    label.text = "".padding(right: rank, withPad: "★").padding(right: maxRank-rank, withPad: "☆")
    label.setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal)

    sv.addArrangedSubview(label)

verticalStackView.addArrangedSubview(sv)

And:

    func addImageView(_ image: UIImage, to sv: UIStackView) {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFit
        iv.image = image

        NSLayoutConstraint.activate([
            iv.widthAnchor .constraint(equalToConstant: w),
            iv.heightAnchor.constraint(equalToConstant: w)
        ])

        iv.setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal)
        iv.setContentCompressionResistancePriority(UILayoutPriority.required, for: .vertical)

        sv.addArrangedSubview(iv)
    }