【问题标题】:How to remove mysterious top margin in UIStackView when `isLayoutMarginsRelativeArrangement=true`?当`isLayoutMarginsRelativeArrangement = true`时如何删除UIStackView中的神秘上边距?
【发布时间】:2020-08-20 21:39:38
【问题描述】:

我正在创建一个带有水平轴UIStackView 的简单“工具栏”组件。看起来不错,除了当我打开isLayoutMarginsRelativeArrangement 时,在项目上方的顶部添加了一个奇怪的边距,导致堆栈视图的高度不正确。

我尝试为堆栈视图directionalLayoutMargins 属性提供许多不同的值,包括根本没有值。然而,这种不需要的间距仍然存在。为什么这个边距存在,我该如何移除它?

override func viewDidLoad() {
    super.viewDidLoad()

    self.stackView = UIStackView(frame: CGRect.zero)
    self.stackView.axis = .horizontal
    self.stackView.alignment = .center
    self.stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
    self.stackView.isLayoutMarginsRelativeArrangement = true
    self.stackView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(self.stackView)

        NSLayoutConstraint.activate([
            self.stackView.topAnchor.constraint(equalTo: view.topAnchor),
            self.stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            self.stackView.widthAnchor.constraint(equalTo: view.widthAnchor),
            self.stackView.heightAnchor.constraint(equalTo: view.heightAnchor)
        ])

        let title = UILabel(frame: .zero)
        title.text = "My Toolbar"
        self.stackView.addArrangedSubview(title)
        title.sizeToFit()

        let button = MDCButton()
        button.setTitle("Recipes", for: .normal)
        button.applyContainedTheme(withScheme: containerScheme)
        button.minimumSize = CGSize(width: 64, height: 48)
        self.stackView.addArrangedSubview(button)

}

【问题讨论】:

  • 请发布您的代码,以显示您将如何以及将堆栈视图添加到什么。这可能是safeArea 问题。
  • @elliott-io 好的,我添加了代码。
  • @zakdances - 为什么将.directionalLayoutMargins 设置为默认值(0,0,0,0),然后将.isLayoutMarginsRelativeArrangement = true 设置为开头?
  • 你从哪里调用上面的代码,你的view到底是什么?看起来您的视图对于您的 stackView 来说太大了,或者有一个安全区域。请发布该代码。
  • @elliott-io 我刚刚将堆栈视图上的insetsLayoutMarginsFromSafeArea 设置为false,现在它可以工作了。所以我猜你对safeArea 是正确的。不知道我的安全区出了什么问题...

标签: swift layout uikit uistackview


【解决方案1】:

这里有几件事可以尝试...

首先,StackBarViewControllerA 创建一个newView(纯UIView)来保存带有标签和按钮的堆栈视图:

class StackBarViewControllerA: UIViewController {

    var stackView: UIStackView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let newView = UIView()
        newView.translatesAutoresizingMaskIntoConstraints = false
        newView.backgroundColor = .cyan
        view.addSubview(newView)

        self.stackView = UIStackView(frame: CGRect.zero)
        self.stackView.axis = .horizontal
        self.stackView.alignment = .center
        self.stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
        self.stackView.isLayoutMarginsRelativeArrangement = true
        self.stackView.translatesAutoresizingMaskIntoConstraints = false
        newView.addSubview(self.stackView)

        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([

            self.stackView.topAnchor.constraint(equalTo: newView.topAnchor),
            self.stackView.bottomAnchor.constraint(equalTo: newView.bottomAnchor),
            self.stackView.widthAnchor.constraint(equalTo: newView.widthAnchor),
            self.stackView.heightAnchor.constraint(equalTo: newView.heightAnchor),

            newView.topAnchor.constraint(equalTo: g.topAnchor),
            newView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            newView.trailingAnchor.constraint(equalTo: g.trailingAnchor),

        ])

        let title = UILabel(frame: .zero)
        title.text = "My Toolbar"
        self.stackView.addArrangedSubview(title)
        title.sizeToFit()

        let button = UIButton() // MDCButton()
        button.setTitle("Recipes", for: .normal)
        button.backgroundColor = .blue
        button.heightAnchor.constraint(equalToConstant: 48).isActive = true
        //button.applyContainedTheme(withScheme: containerScheme)
        //button.minimumSize = CGSize(width: 64, height: 48)
        self.stackView.addArrangedSubview(button)
    }

}

第二,StackBarViewControllerB使用自定义StackBarView

class StackBarView: UIView {

    var stackView: UIStackView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    func commonInit() -> Void {

        self.stackView = UIStackView(frame: CGRect.zero)
        self.stackView.axis = .horizontal
        self.stackView.alignment = .center
        self.stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
        self.stackView.isLayoutMarginsRelativeArrangement = true
        self.stackView.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(self.stackView)

        NSLayoutConstraint.activate([
            self.stackView.topAnchor.constraint(equalTo: self.topAnchor),
            self.stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
            self.stackView.widthAnchor.constraint(equalTo: self.widthAnchor),
            self.stackView.heightAnchor.constraint(equalTo: self.heightAnchor)
        ])

        let title = UILabel(frame: .zero)
        title.text = "My Toolbar"
        self.stackView.addArrangedSubview(title)
        title.sizeToFit()

        let button = UIButton() // MDCButton()
        button.setTitle("Recipes", for: .normal)
        button.backgroundColor = .blue
        //button.applyContainedTheme(withScheme: containerScheme)
        //button.minimumSize = CGSize(width: 64, height: 48)
        button.widthAnchor.constraint(greaterThanOrEqualToConstant: 64).isActive = true
        button.heightAnchor.constraint(greaterThanOrEqualToConstant: 48).isActive = true
        button.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        self.stackView.addArrangedSubview(button)

    }

}

class StackBarViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let v = StackBarView()

        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .cyan

        view.addSubview(v)

        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([

            v.topAnchor.constraint(equalTo: g.topAnchor),
            v.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            v.trailingAnchor.constraint(equalTo: g.trailingAnchor),

        ])

    }

}

两者都给出了这个结果(我给它一个青色背景,以便我们可以看到框架):

【讨论】:

    【解决方案2】:

    看起来问题出在您的锚点设置上。尝试移除你的bottomAnchor并将你的heightAnchor设置为你想要的按钮大小加上一些填充,而不是仅仅匹配view的heightAnchor:

    NSLayoutConstraint.activate([
        self.stackView.topAnchor.constraint(equalTo: view.topAnchor),
        // self.stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        self.stackView.widthAnchor.constraint(equalTo: view.widthAnchor),
        // self.stackView.heightAnchor.constraint(equalTo: view.heightAnchor),
        self.stackView.heightAnchor.constraint(equalToConstant: 80)
    ])
    

    注意:您可能需要为顶部锚点设置偏移常量,例如:self.stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50)

    【讨论】:

    • 当然。但是某些东西需要有一个固定的大小,或者它们都需要相互关联。如果您的 childButton 具有固定高度,请将您的 stackView.heightAnchor 设置为匹配 childButton.heightAnchor。确保在添加 stackView 约束之前调用childButton.setNeedsLayout
    • 显然,您需要先创建子按钮并将其添加到 stackView,然后再按照上述注释强制更新布局。