UIStackView 等间距,包括边缘

Posted

技术标签:

【中文标题】UIStackView 等间距,包括边缘【英文标题】:UIStackView equal spacing including edges 【发布时间】:2019-02-13 09:05:09 【问题描述】:

我有一个带有 3 个按钮的水平堆栈视图:音乐应用程序的后退、播放、前进。 这是我当前的代码:

self.controlStackView.axis = .horizontal
self.controlStackView.distribution = .equalSpacing
self.controlStackView.alignment = .center
self.controlStackView.spacing = 10.0
self.controlStackView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.controlStackView)

self.controlStackView.topAnchor.constraint(equalTo: self.artworkImageView.bottomAnchor, constant: 10.0).isActive = true
self.controlStackView.centerXAnchor.constraint(equalTo: self.contentView.centerXAnchor).isActive = true

这是什么,它按如下方式分配按钮(由于对齐从中心开始):

[backward] - 10 spacing - [play] - 10 spacing - [forward]

我可以增加间距,但它仍然是固定的。 所以我设置了前导和尾随锚来定义堆栈视图的最大宽度:

self.controlStackView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
self.controlStackView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true

这对布局有什么作用:

[left edge backward] - lots of spaaaaaaace - [centered play] - lots of spaaaaaaace - [right edge forward]

它分布在整个宽度上(中心到左右之间有一个 .equalSpacing)。但这也没有帮助。本质上,我的目标是让包括边缘在内的间距真正相等。

假设我的可用宽度为 100,我的 3 个按钮分别为 10、20、10 - 这意味着还有 60 个剩余空间是空的。

我希望它像这样分发:

space - [backward] - space - [play] - space [forward] - space

所以我的按钮之间有 4 个空格,每个空格是 15,所以我们填充了剩余的 60 个空格。 我当然可以实现对堆栈视图的填充以获取外部空间,但这将是非常静态的并且分布不均。

有人知道我是否可以通过将边缘包含在空间分布中的方式来实现它?

谢谢

【问题讨论】:

在堆栈视图的开头和结尾添加两个宽度 == 0 的空视图。这不是一个优雅的解决方案,但它确实有效。 UIStackView; Equal Spacing Between & Outside Elements的可能重复 它确实适用于占位符视图:) 间距应该是动态的而不是恒定的。如果我添加更多视图,我还需要调整外部间距常量,在其他设备上它也可能看起来不同 【参考方案1】:

这真的很简单,使用“间隔”视图。

比按钮的数量多添加一个间隔,这样你就有了:

spacer - button - spacer - button - spacer

然后,将间隔 2 到 n 的宽度限制为等于第一个间隔的宽度。 stackView 将处理剩下的!

这里是一个例子(只需要故事板中的一个viewController,其余的通过代码完成):

class DistributeViewController: UIViewController 

    let stackView: UIStackView = 
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .horizontal
        v.alignment = .fill
        v.distribution = .fill
        v.spacing = 0
        return v
    ()

    var buttonTitles = [
        "Backward",
        "Play",
        "Forward",
//      "Next",
        ]

    var numButtons = 0

    override func viewDidLoad() 
        super.viewDidLoad()

        // stackView will hold the buttons and spacers
        view.addSubview(stackView)

        // constrain it to Top + 20, Leading and Trailing == 0, height will be controlled by button height
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),
            stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0.0),
            stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0.0),
            ])

        // arrays to hold our buttons and spacers
        var buttons: [UIView] = [UIView]()
        var spacers: [UIView] = [UIView]()

        numButtons = buttonTitles.count

        // create the buttons and append them to our buttons array
        for i in 0..<numButtons 
            let b = UIButton()
            b.translatesAutoresizingMaskIntoConstraints = false
            b.backgroundColor = .blue
            b.setTitle(buttonTitles[i], for: .normal)
            buttons.append(b)
        

        // create the spacer views and append them to our spacers array
        //      we need 1 more spacer than buttons
        for _ in 1...numButtons+1 
            let v = UIView()
            v.translatesAutoresizingMaskIntoConstraints = false
            v.backgroundColor = .red        // just so we can see them... use .clear for production
            spacers.append(v)
        

        // addd spacers and buttons to stackView
        for i in 0..<spacers.count 

            stackView.addArrangedSubview(spacers[i])

            // one fewer buttons than spacers, so don't go out-of-range
            if i < buttons.count 
                stackView.addArrangedSubview(buttons[i])
            

            if i > 0 
                // constrain spacer widths to first spacer's width (this will make them all equal)
                spacers[i].widthAnchor.constraint(equalTo: spacers[0].widthAnchor, multiplier: 1.0).isActive = true

                // if you want the buttons to be equal widths, uncomment this block
                /*
                if i < buttons.count 
                    buttons[i].widthAnchor.constraint(equalTo: buttons[0].widthAnchor, multiplier: 1.0).isActive = true
                
                */

            

        

    


3 个按钮的结果:

并带有 4 个按钮:

还有一对等宽按钮:

【讨论】:

你是个天才。 不错。不过,对于一个看似常见的用例来说,似乎需要做很多工作。

以上是关于UIStackView 等间距,包括边缘的主要内容,如果未能解决你的问题,请参考以下文章

如何在 UIStackView 中等间距标签?

UIStackView背景色和自定义间距

UIStackView 中的图像未四舍五入

具有静态间距的UIStackView子类

UIStackView 调整我的视图大小

UIStackView 的约束在设置间距时中断