Swift Visual Format语言中心有四个按钮

时间:2018-05-15 15:37:51

标签: swift autolayout constraints visual-format-language

我想在视图的中心X和Y周围安排四个带有视觉格式语言的按钮,而不需要对任何点进行硬编码,更喜欢用约束进行缩放。

我只能实现一组按钮以对齐底部边距,如何使用您看到的间距(例如~20个点)将它们居中,而不依靠NSLayoutConstraint

我没有将它们放在堆叠中,它们都是单独的按钮 我读到堆栈不是一个好主意,但它似乎是合乎逻辑的方式,否则它们会垂直伸展 理想情况下,我想使用VFL制作计算器用户界面,但我先尝试这个。

@IBDesignable class images_and_constraints: UIButton {

    override init(frame: CGRect) {
        super.init(frame: frame)
        calcButtons()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        calcButtons()
    }

    private func calcButtons() {
        let calcPlus = UIButton()
        calcPlus.translatesAutoresizingMaskIntoConstraints = false
        calcPlus.setTitle("+", for: .normal)
        calcPlus.setTitleColor(UIColor.black, for: .normal)
        calcPlus.setTitleColor(UIColor.white, for: .highlighted)
        calcPlus.backgroundColor = UIColor.orange
        addSubview(calcPlus)

        let calcSubtract = UIButton()
        calcSubtract.translatesAutoresizingMaskIntoConstraints = false
        calcSubtract.setTitle("-", for: .normal)
        calcSubtract.setTitleColor(UIColor.black, for: .normal)
        calcSubtract.setTitleColor(UIColor.white, for: .highlighted)
        calcSubtract.backgroundColor = UIColor.orange
        addSubview(calcSubtract)

        let calcMultiply = UIButton()
        calcMultiply.translatesAutoresizingMaskIntoConstraints = false
        calcMultiply.setTitle("x", for: .normal)
        calcMultiply.setTitleColor(UIColor.black, for: .normal)
        calcMultiply.setTitleColor(UIColor.white, for: .highlighted)
        calcMultiply.backgroundColor = UIColor.orange
        addSubview(calcMultiply)

        let calcDivide = UIButton()
        calcDivide.translatesAutoresizingMaskIntoConstraints = false
        calcDivide.setTitle("/", for: .normal)
        calcDivide.setTitleColor(UIColor.black, for: .normal)
        calcDivide.setTitleColor(UIColor.white, for: .highlighted)
        calcDivide.backgroundColor = UIColor.orange
        addSubview(calcDivide)

        let views = ["calcPlus": calcPlus,
                     "calcSubtract": calcSubtract,
                     "calcMultiply": calcMultiply,
                     "calcDivide": calcDivide]

        NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[calcPlus]-[calcSubtract(==calcPlus)]-|",
                                                                   options: .alignAllBottom,
                                                                   metrics: nil,
                                                                   views: views))
        NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[calcMultiply]-[calcDivide(==calcMultiply)]-|",
                                                                   options: .alignAllTop,
                                                                   metrics: nil,
                                                                   views: views))
        NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:[calcSubtract]-[calcDivide(==calcSubtract)]-|",
                                                                   options: .alignAllCenterX,
                                                                   metrics: nil,
                                                                   views: views))
        NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:[calcSubtract]",
                                                                   options: .alignAllCenterX,
                                                                   metrics: nil,
                                                                   views: views))
    }
}

3 个答案:

答案 0 :(得分:0)

使用VFL来集中视图需要诡计 查看this question,特别是this answer了解诀窍。

对于你想要的那种布局,VFL不太合适 除了VFL之外,只有一个NSLayoutConstraint可以解决它,但由于你只对VFL感兴趣,我建议你使用这个技巧来集中容纳按钮的容器视图。

解决方案:

func calcButtons() {
    //1. Create a container view that will contain your operator buttons
    let buttonContainerView = UIView()
    buttonContainerView.backgroundColor = UIColor.lightGray
    buttonContainerView.translatesAutoresizingMaskIntoConstraints = false
    self.addSubview(buttonContainerView)

    //Place it vertically in the center of the superview
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:[superview]-(<=1)-[childView]",
                                                               options: .alignAllCenterX,
                                                               metrics: nil,
                                                               views: ["superview" : self,
                                                                       "childView" : buttonContainerView]))

    //Place it horizontally in the center of the superview + equal widths to superview
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:[superview]-(<=1)-[childView(==superview)]",
                                                               options: .alignAllCenterY,
                                                               metrics: nil,
                                                               views: ["superview" : self,
                                                                       "childView" : buttonContainerView]))

    //2. Create your buttons as you were:

    //DRY Fix: Helper function to create button and add it to `buttonContainerView`
    func addButton(title: String, selector: Selector? = nil) -> UIButton {
        let button = UIButton()
        button.backgroundColor = UIColor.orange
        button.setTitle(title, for: .normal)
        button.setTitleColor(UIColor.black, for: .normal)
        button.setTitleColor(UIColor.white, for: .highlighted)

        //You might need this later cuz a button gotta do wat a button gotta do
        if let selector = selector {
            button.addTarget(self, action: selector, for: UIControlEvents.touchUpInside)
        }

        button.translatesAutoresizingMaskIntoConstraints = false
        buttonContainerView.addSubview(button)

        return button
    }

    let calcPlus = addButton(title: "+", selector: #selector(CalculatorView.add))
    let calcSubtract = addButton(title: "-")
    let calcMultiply = addButton(title: "x")
    let calcDivide = addButton(title: "/")

    let views = ["calcPlus": calcPlus,
                 "calcSubtract": calcSubtract,
                 "calcMultiply": calcMultiply,
                 "calcDivide": calcDivide]

    //Same as before
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[calcPlus]-[calcSubtract(==calcPlus)]-|",
                                                               options: .alignAllBottom,
                                                               metrics: nil,
                                                               views: views))

    //Same as before
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[calcMultiply]-[calcDivide(==calcMultiply)]-|",
                                                               options: .alignAllTop,
                                                               metrics: nil,
                                                               views: views))
    /*
     Same as before but this time we give a top constraint too
     i.e.
     "V:|-[calcSubtract]..."
     instead of
     "V:[calcSubtract]..."
     */
    //
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[calcSubtract]-[calcDivide(==calcSubtract)]-|",
                                                               options: .alignAllCenterX,
                                                               metrics: nil,
                                                               views: views))
}

答案 1 :(得分:0)

最后,我决定NSLayoutConstraint.activate中的每个按钮都依赖于它之前的那个(行),而前导按钮(从左到右的阅读器位于最左边)限制在上面的那个它。

calculatriceButtons["7"]!.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: 1.0),
calculatriceButtons["7"]!.topAnchor.constraint(equalTo: calculatriceButtons["C"]!.bottomAnchor, constant: 1.0),

这是确保在所有设备上缩放按钮的最佳方法。

答案 2 :(得分:-1)

使用VFL有一种新的替代方法,这就是我现在在代码中使用的方法。

布局主播

每个视图都有不同的anchor个。 leadingtrailingtopbottom等......

您可以使用它们为您创建约束......

NSLayoutConstraint.activate([
    viewB.leadingAnchor.constraint(equalTo: viewA.leadingAnchor, constant: 20),
    viewA.widthAnchor.constraint(equalTo: viewB.widthAnchor)
])

例如。

堆叠视图

除此之外,还有一种更现代的方法是使用UIStackView。这是一个非常有用的视图,它不需要添加约束并为您完成。

let stackView = UIStackView(arrangedSubViews: [viewA, viewB])
stackView.spacing = 20
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fillEqually

您还可以嵌套堆栈视图以创建更复杂的布局。

绝对值得期待...

https://developer.apple.com/documentation/uikit/uistackview?changes=_6

制作布局

let upperStackView = UIStackView(arrangedSubviews: [topLeft, topRight])
upperStackView.axis = .horizontal
upperStackView.distribution = .fillEqually
upperStackView.spacing = 20

let lowerStackView = UIStackView(arrangedSubviews: [bottomLeft, bottomRight])
lowerStackView.axis = .horizontal
lowerStackView.distribution = .fillEqually
lowerStackView.spacing = 20

let mainStackView = UIStackView(arrangedSubviews: [upperStackView, lowerStackView])
mainStackView.axis = .vertical
mainStackView.distribution = .fillEqually
mainStackView.spacing = 20

view.addSubview(mainStackView)

NSLayoutConstraint.activate([
    mainStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    mainStackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    mainStackView.widthAnchor.constraint(equalToConstant: 200),
    mainStackView.heightAnchor.constraint(equalToConstant: 200),
])

为什么不参加VFL?

虽然VFL在AutoLayout上是一次不错的首次尝试,但我觉得Apple现在已经离开了它,并且正朝着这些更简洁的方法创建AutoLayout约束。

它仍然允许您在编写代码时考虑约束,但提供稍微更现代的方法。

当然......你也可以在Interface Builder中创建UIStackView:D