我们可以访问 UIStackView 子视图大小吗?

Can we acces a UIStackView's subview's size?

我的问题很简单,但我还没有找到任何答案。

我正在制作一种 table 在水平堆栈中使用两个垂直堆栈。两个 vStack 都有不同的对象(在我的例子中是 Button,每个对象都有边框)但数量相同(因此它们像经典 table 一样水平配对)。 我已将我的两个 vStack 分布设置为 .fillProportionally,因此每个按钮的大小都不同,具体取决于它们的 titleLabel 长度。

但是,我想让我的每个按钮与其配对按钮(水平旁边的那个,在另一个 vStack 中)具有相同的大小,以便我的单元格边框对齐(使用最大的按钮尺寸作为每对的参考)。

我认为这涉及找到一种方法来访问一个堆栈的子视图的大小,然后将另一个堆栈子视图的大小限制为相同。或者,因为通常只有一个大按钮弄乱了分布和偏移按钮对的边框,访问一个堆栈显示其子视图的方式并强制另一个堆栈采用相同的方式。不管怎样,我还不知道该怎么做。

如果你能帮助我或引导我找到答案,我将非常高兴!

(我不认为我需要用代码来解释我的问题,因为它是一个相对抽象的问题,但如果你需要它,我可以分享它)

编辑:

左:我想要的,右:我得到的

每个单元格都是一个带有边框的按钮(在这里没用,但在我的应用程序中它会有一个功能),我想将“描述”按钮的高度设置为等于“文本”按钮。我希望现在更清楚了 :) 我尝试反转布局(两个水平堆叠在一个垂直堆叠中)但问题仍然存在,这次是宽度而不是高度。

编辑 2:

根据您的建议,这里是一些代码:

import UIKit


class ViewController: UIViewController {

@IBOutlet weak var bottomButton: UIButton!


@IBOutlet weak var vstack: UIStackView!

@objc func buttonAction(sender: UIButton) {
    sender.isEnabled = true
    print(sender.frame)
}

override func viewDidLoad() {
    super.viewDidLoad()
    
    
    let button = newButton(text: "name")
    let button2 = newButton(text: "John Smith")
    let button3 = newButton(text: "Description")
    let button4 = newButton(text: "text text text text text text text text text text text \n text text text text text \n text text text text text")
    
    let hStack = UIStackView()
    hStack.axis = .horizontal
    hStack.distribution = .fillEqually
    
    let hStack2 = UIStackView()
    hStack2.axis = .horizontal
    hStack2.distribution = .fillEqually
    
    hStack.addArrangedSubview(button)
    hStack.addArrangedSubview(button2)

    hStack2.addArrangedSubview(button3)
    hStack2.addArrangedSubview(button4)

    vstack.addArrangedSubview(hStack)
    vstack.addArrangedSubview(hStack2)
    
}
}

 
func newButton(text: String) -> UIButton {

    let button = UIButton(type: .system)
    button.isEnabled = true
    button.setTitle(text, for: .disabled)
    button.setTitle(text, for: .normal)
    button.setTitleColor(.black, for: .disabled)
    button.layer.borderWidth = 1
    button.titleLabel?.numberOfLines = 0
    button.titleLabel?.textAlignment = NSTextAlignment.center

    return button
}

`

在垂直堆叠中使用水平堆叠和 Fill Equally 部分解决了这个问题,因为它只在我的文本低于一定长度时有效,否则它会被裁剪(见下图),这就是我使用 fillProportionally 的原因。

好的 - 部分问题是您正在修改 titleLabel 属性 - 具体来说,设置其 .numberOfLines = 0。 Auto-layout 没有 考虑到这一点,没有一点帮助。

您需要使用子按钮class,例如:

class MultiLineButton: UIButton {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    func commonInit() -> Void {
        titleLabel?.numberOfLines = 0
        titleLabel?.textAlignment = .center
        // if you want to give your buttons some "padding" around the title
        //contentEdgeInsets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0)
    }

    override var intrinsicContentSize: CGSize {
        guard let tl = titleLabel else {
            return .zero
        }
        let size = tl.intrinsicContentSize
        return CGSize(width: size.width + contentEdgeInsets.left + contentEdgeInsets.right, height: size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        guard let tl = titleLabel else { return }
        tl.preferredMaxLayoutWidth = tl.frame.size.width
    }

}

使用 class,这是一个示例视图控制器,我们在其中添加了一个垂直堆栈视图和 2 个水平“行”堆栈视图:

class PruViewController: UIViewController {

    func newButton(text: String) -> MultiLineButton {
        let b = MultiLineButton()
        b.titleLabel?.font = .systemFont(ofSize: 15.0)
        b.setTitle(text, for: [])
        b.setTitleColor(.blue, for: .normal)
        b.setTitleColor(.lightGray, for: .highlighted)
        b.setTitleColor(.black, for: .disabled)
        b.layer.borderWidth = 1
        b.layer.borderColor = UIColor.black.cgColor
        return b
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = newButton(text: "name")
        let button2 = newButton(text: "John Smith")
        let button3 = newButton(text: "Description")
        let button4 = newButton(text: "text text text text text text text text text text text \n text text text text text \n text text text text text")
        
        let vStack = UIStackView()
        vStack.axis = .vertical
        vStack.distribution = .fill
        
        let hStack = UIStackView()
        hStack.axis = .horizontal
        hStack.distribution = .fillEqually
        
        let hStack2 = UIStackView()
        hStack2.axis = .horizontal
        hStack2.distribution = .fillEqually
        
        hStack.addArrangedSubview(button)
        hStack.addArrangedSubview(button2)
        
        hStack2.addArrangedSubview(button3)
        hStack2.addArrangedSubview(button4)
        
        vStack.addArrangedSubview(hStack)
        vStack.addArrangedSubview(hStack2)
        
        vStack.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(vStack)
        
        // respect safe area
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            vStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            vStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            vStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
        ])
        
    }
}

结果:

而且,您会在我的 MultiLineButton class 中注意到一条关于在按钮标题标签周围添加“填充”的评论...这是该行的外观 un-commented :