带有 UIImageView 的 UIStackView 没有按预期处理约束

UIStackView with UIImageView doesn't handle constraint as expected

我有一个 UIStackView(水平)addArrangedSubview 到一个 UIStackView(垂直)。

水平视图有 3 个单元格:第一个是带有 UILabel 的 addArrangedSubview,第二个是 UIImageView(具有宽度和高度恒定约束以保持我想要的大小)。第三个单元格是带有 UILabel 的 addArrangedSubview。 我希望 UIStackView 适合整个屏幕宽度

不幸的是,结果拉伸如下:

我的代码如下:

    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)

而我的工具功能如下:

    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 { [=11=].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)
        }
    }

注意 1:我做了一些实验,因此注释了代码。

注2:屏幕截图中的红色条是我手绘的,以便于理解单元格的布局。

[更新]

为了解决问题,让一切变得混乱的是我添加到 UIImageView 的约束,加上 none,一切正常。我添加约束的原因是为了使 UIImageView 保持正方形并保持正确的尺寸。 出于这个原因,我添加了一个 UIView 作为 in-between 元素,以吸收 UIStackView 约束设置到内部元素的强度(因此是 if false 代码)。但是我需要在 UIView 中添加额外的约束以使 UIImageView 居中,这听起来有点过度使用布局系统。当然 我可能错过了一些简化整个事情的东西。

你能 - 尝试为 Root-StackView 添加约束,尾随、顶部、底部和前导等于常量 0。 - 尝试使用 .fill 而不是 .center 更改 Root-StackView 的对齐方式。

  • 如果你想让标签更灵活,使压缩阻力和上下文拥抱 属性 的值(通常为 248、748)比其他值(251、250、750)小得多。

谢谢, J

我终于解决了这个问题:我将标签和 UIImageView 的阻力压缩优先级更改为 .required:

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

同一段代码现在如下所示:

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

并且:

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