Swift Visual Format Language четыре кнопки в центре - PullRequest
0 голосов
/ 15 мая 2018

Я хотел бы расположить четыре кнопки с языком визуального формата вокруг центрального 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 голосов
/ 01 июня 2018

Существует новая альтернатива использованию VFL, которую я сейчас использую в коде.

Якоря макета

Каждое представление имеет различные anchor с.leading, trailing, top, bottom и т. Д. ...

Вы можете использовать их для создания ограничений ...

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.

Это все еще позволяет вам думать вограничения при написании кода, но предоставляют немного более современный подход.

Конечно ... вы также можете создать UIStackView в Интерфейсном Разработчике также: D

0 голосов
/ 27 августа 2018

В итоге я решил NSLayoutConstraint.activate, из которых каждая кнопка будет зависеть от той, что перед ней (строки), а ведущая (крайняя левая для читателей слева направо) ограничена кнопкой над ней.

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

Это был лучший способ обеспечить масштабирование кнопок на всех устройствах.

0 голосов
/ 01 июня 2018

Использование VFL для центрирования видов требует хитрости.
Посмотрите на этот вопрос и особенно этот ответ для уловки.

Для типа макета выхочу, VFL просто не подходит.
Только один NSLayoutConstraint в дополнение к VFL решит эту проблему, но, так как вы заинтересованы только в 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))
}
...