首页 > 解决方案 > 代码中的 UIStackView 设置 layoutMargins 会中断对齐

问题描述

我正在尝试将 layoutMargins 添加到UIStackView我在代码中创建的某些元素中。我在 IB 中尝试过同样的事情并且效果很好,但是当我添加layoutMargins并设置时isLayoutMarginsRelativeArrangement = true,我的堆栈视图的对齐会中断。

我的堆栈视图的代码如下:

@objc lazy var buttonsStackView: UIStackView = {
        let stack = UIStackView(arrangedSubviews: [doneButton, separatorView, giftButton])
        stack.layoutMargins = UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 4)
        stack.isLayoutMarginsRelativeArrangement = true
        stack.axis = .horizontal
        stack.frame = CGRect(x: 0, y: 0, width: 150, height: 44)
        stack.spacing = 4
        stack.distribution = .equalCentering
        stack.alignment = .center

        let bgView = UIView(frame: stack.bounds)
        stackViewBackground = bgView
        bgView.backgroundColor = ColorManager.shared.grayColor
        bgView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        stack.insertSubview(bgView, at: 0)

        separatorView.heightAnchor.constraint(equalTo: stack.heightAnchor, multiplier: 0.8).isActive = true

        return stack
    }()

SeparatorView 只有 1pt 宽度约束,而两个按钮不受约束以保持它们的intrinsicContentSize.

这是我的 stackView 的isLayoutMarginsRelativeArrangement外观false在此处输入图像描述

但显然,需要左右边距,所以当设置isLayoutMarginsRelativeArrangement为时true,我的堆栈视图的对齐中断: 在此处输入图像描述

不幸的是,我不能将 IB 用于这个特定的视图,我需要从代码中初始化它。非常感谢有关如何解决此问题的任何想法。谢谢!

标签: iosswiftuistackview

解决方案


这是一个制作自定义视图的示例,separator水平居中,按钮在每个“边”中居中:

protocol DoneGiftDelegate: class {
    func doneButtonTapped()
    func giftButtonTapped()
}

class DoneGiftView: UIView {

    weak var delegate: DoneGiftDelegate?

    let doneButton: UIButton = {
        let v = UIButton(type: .system)
        v.setTitle("Done", for: [])
        v.tintColor = .white
        return v
    }()

    let giftButton: UIButton = {
        let v = UIButton(type: .system)
        v.setImage(UIImage(systemName: "gift.fill"), for: [])
        v.tintColor = .white
        return v
    }()

    let separatorView: UIView = {
        let v = UIView()
        v.backgroundColor = .white
        return v
    }()

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

    func commonInit() -> Void {

        backgroundColor = .lightGray // ColorManager.shared.grayColor

        [doneButton, separatorView, giftButton].forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
            addSubview($0)
        }

        // width and height constraints: 150 x 44
        //  set Priority to 999 so it can be overriden by controller if desired
        let widthConstraint = widthAnchor.constraint(equalToConstant: 150.0)
        widthConstraint.priority = UILayoutPriority(rawValue: 999)
        let heightConstraint = heightAnchor.constraint(equalToConstant: 44.0)
        heightConstraint.priority = UILayoutPriority(rawValue: 999)

        NSLayoutConstraint.activate([

            // doneButton Leading to Leading
            doneButton.leadingAnchor.constraint(equalTo: leadingAnchor),
            // separator Leading to doneButton Trailing
            separatorView.leadingAnchor.constraint(equalTo: doneButton.trailingAnchor),
            // giftButton Leading to separator Trailing
            giftButton.leadingAnchor.constraint(equalTo: separatorView.trailingAnchor),
            // giftButton Trailing to Trailing
            giftButton.trailingAnchor.constraint(equalTo: trailingAnchor),

            // all centered vertically
            doneButton.centerYAnchor.constraint(equalTo: centerYAnchor),
            separatorView.centerYAnchor.constraint(equalTo: centerYAnchor),
            giftButton.centerYAnchor.constraint(equalTo: centerYAnchor),

            // doneButton Height = Height
            doneButton.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 1.0),
            // separator Height = 80% of Height
            separatorView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.8),
            // giftButton Height = Height
            giftButton.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 1.0),

            // separator Width = 1
            separatorView.widthAnchor.constraint(equalToConstant: 1.0),

            // doneButton Width = giftButton Width
            doneButton.widthAnchor.constraint(equalTo: giftButton.widthAnchor, multiplier: 1.0),

            // self Width and Height
            widthConstraint,
            heightConstraint,
        ])

        // add target actions for buttons
        doneButton.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
        giftButton.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)

    }

    @objc func buttonTapped(_ sender: UIButton) -> Void {
        if sender == doneButton {
            delegate?.doneButtonTapped()
        } else {
            delegate?.giftButtonTapped()
        }
    }

}

class DemoViewController: UIViewController, DoneGiftDelegate {

    let doneGiftView: DoneGiftView = DoneGiftView()

    let testView: UIView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()

        doneGiftView.translatesAutoresizingMaskIntoConstraints = false
        testView.translatesAutoresizingMaskIntoConstraints = false

        testView.addSubview(doneGiftView)
        view.addSubview(testView)

        // so we can see the view frame
        testView.backgroundColor = .cyan

        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([

            // testView Top: 100
            //  Leading / Trailing with 20-pts "padding"
            //  Height: 80
            testView.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
            testView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            testView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            testView.heightAnchor.constraint(equalToConstant: 80.0),

            // doneGiftView Trailing to testView Trailing
            doneGiftView.trailingAnchor.constraint(equalTo: testView.trailingAnchor),
            // doneGiftView centered vertically in testView
            doneGiftView.centerYAnchor.constraint(equalTo: testView.centerYAnchor),

        ])

        doneGiftView.delegate = self
    }

    func doneButtonTapped() {
        print("Done button tapped!")
        // do what we want when Done button tapped
    }
    func giftButtonTapped() {
        print("Gift button tapped")
        // do what we want when Gift button tapped
    }
}

示例结果:

在此处输入图像描述


推荐阅读