Использование Autolayout Visual Format с Swift?

Я пытаюсь использовать язык визуального форматирования Autolayout в Swift, используя NSLayoutConstraint.constraintsWithVisualFormat. Вот пример некоторого кода, который ничего не полезен, но насколько я могу судить, нужно сделать проверку типов счастливой:

let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(
  format: "", options: 0, metrics: {}, views: {})

Однако это вызывает ошибку компилятора:

"Невозможно преобразовать тип выражения '[AnyObject]!' наберите" String!".

Прежде чем я предполагаю, что это достойная Radar ошибка, есть ли что-то очевидное, что я здесь отсутствует? Это происходит даже без явного литья имени переменной или с помощью другого бесплатного downcasting с помощью as. Я не вижу никакой причины, по которой компилятор ожидал, что какая-либо часть этого решения решится на String!.

Ответ 1

это работает для меня без ошибок:

let bar:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(
  nil, options: NSLayoutFormatOptions(0), metrics: nil, views: nil)

Обновление

приведенная выше строка не может быть скомпилирована, поскольку параметры 1 st и 4 th больше не могут быть дополнительными.

синтаксически они должны быть установлены, например, это:

let bar:[AnyObject] = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: ["": self.view])

Обновление

(для Xcode 7, Swift 2.0)

действительный синтаксис теперь также запрашивает имя параметра, например:

NSLayoutFormatOptions(rawValue: 0)

ПРИМЕЧАНИЕ: эта строка кода показывает только правильный синтаксис, сами параметры не гарантируют правильность или даже правильность ограничения!

Ответ 2

Первый из них - это то, что Swift Dictionary еще не соединен с NSDictionary. Чтобы заставить это работать, вы захотите явно создать NSDictionary для каждого параметра NSDictionary.

Кроме того, как указывает Spencer Hall, {} не является словарным литералом в Swift. Пустой словарь написан:

[:]

Начиная с версии XCode 6 Beta 2, это решение позволяет создавать ограничения с визуальным форматом:

var viewBindingsDict: NSMutableDictionary = NSMutableDictionary()
viewBindingsDict.setValue(fooView, forKey: "fooView")
viewBindingsDict.setValue(barView, forKey: "barView")
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[fooView]-[barView]-|", options: nil, metrics: nil, views: viewBindingsDict))

Ответ 3

Попробуйте это - удалите имя начальной переменной (format:), используйте NSLayoutFormatOptions(0) и просто передайте nil для этих дополнительных NS-словарей:

let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: nil)

Ответ 4

FYI: если вы используете представления с ограничениемWithVisualFormat - вместо обертывания с помощью NSMutableDict

["myLabel": self.myLabel!]

и более конкретно

var constraints = [NSLayoutConstraint]()
NSLayoutConstraint.constraintsWithVisualFormat("H:|-15-[myLabel]-15-|", 
    options:NSLayoutFormatOptions.allZeros, 
    metrics: nil, 
    views: ["myLabel": self.myLabel!]).map {
        constraints.append($0 as NSLayoutConstraint)
    }

Ответ 5

Это работает с Xcode 6.1.1:

extension NSView {

    func addConstraints(constraintsVFL: String, views: [String: NSView], metrics: [NSObject: AnyObject]? = nil, options: NSLayoutFormatOptions = NSLayoutFormatOptions.allZeros) {
        let mutableDict = (views as NSDictionary).mutableCopy() as NSMutableDictionary
        let constraints = NSLayoutConstraint.constraintsWithVisualFormat(constraintsVFL, options: options, metrics: metrics, views: mutableDict)
        self.addConstraints(constraints)
    }

}

Затем вы можете использовать такие вызовы, как:

var views : [String: NSView] = ["box": self.box]
self.view.addConstraints("V:[box(100)]", views: views)

Это работает, чтобы добавить ограничения. Если вы используете iOS, замените UIView на NSView


Вероятно, вы должны проверить

  • Cartography, что является новым подходом, но довольно удивительным. Он использует Autolayout под капотом.
  • SnapKit, который я еще не пробовал, но также представляет собой систему автоопределения DSL

Ответ 6

Меня немного раздражает, что я вызываю NSLayoutConstraint (единственное число) для генерации constraintsWithVisualFormat... (множественное число), хотя я уверен, что только я. В любом случае у меня есть две функции верхнего уровня:

фрагмент 1 (Swift 1.2)

#if os(iOS)
    public typealias View = UIView
#elseif os(OSX)
    public typealias View = NSView
#endif

public func NSLayoutConstraints(visualFormat: String, options: NSLayoutFormatOptions = .allZeros, views: View...) -> [NSLayoutConstraint] {
    return NSLayoutConstraints(visualFormat, options: options, views: views)
}

public func NSLayoutConstraints(visualFormat: String, options: NSLayoutFormatOptions = .allZeros, views: [View] = []) -> [NSLayoutConstraint] {
    if visualFormat.hasPrefix("B:") {
        let h = NSLayoutConstraints("H\(dropFirst(visualFormat))", options: options, views: views)
        let v = NSLayoutConstraints("V\(dropFirst(visualFormat))", options: options, views: views)
        return h + v
    }
    var dict: [String:View] = [:]
    for (i, v) in enumerate(views) {
        dict["v\(i + 1)"] = v
    }
    let format = visualFormat.stringByReplacingOccurrencesOfString("[v]", withString: "[v1]")
    return NSLayoutConstraint.constraintsWithVisualFormat(format, options: options, metrics: nil, views: dict) as! [NSLayoutConstraint]
}

Что можно использовать так:

superView.addConstraints(NSLayoutConstraints("B:|[v]|", view))

Другими словами, представления автоматически называются "v1" до "v\(views.count)" (кроме первого представления, которое также можно назвать "v"). Кроме того, при префиксе формата "B:" будут генерироваться ограничения "H:" и "V:". Таким образом, примерная строка кода означает, что "убедитесь, что view всегда соответствует superView".

И со следующими расширениями:

фрагмент 2

public extension View {

    // useMask of nil will not affect the views' translatesAutoresizingMaskIntoConstraints
    public func addConstraints(visualFormat: String, options: NSLayoutFormatOptions = .allZeros, useMask: Bool? = false, views: View...) {
        if let useMask = useMask {
            for view in views {
                #if os(iOS)
                    view.setTranslatesAutoresizingMaskIntoConstraints(useMask)
                #elseif os(OSX)
                    view.translatesAutoresizingMaskIntoConstraints = useMask
                #endif
            }
        }
        addConstraints(NSLayoutConstraints(visualFormat, options: options, views: views))
    }

    public func addSubview(view: View, constraints: String, options: NSLayoutFormatOptions = .allZeros, useMask: Bool? = false) {
        addSubview(view)
        addConstraints(constraints, options: options, useMask: useMask, views: view)
    }
}

Мы можем сделать некоторые общие задачи намного элегантнее, например, добавить кнопку со стандартным смещением в нижнем правом углу:

superView.addSubview(button, constraints: "B:[v]-|")

Например, на игровой площадке iOS:

import UIKit
import XCPlayground

// paste here `snippet 1` and `snippet 2`

let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
XCPShowView("view", view)
view.backgroundColor = .orangeColor()
XCPShowView("view", view)
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
button.setTitle("bottom right", forState: .Normal)

view.addSubview(button, constraints: "B:[v]-|")

Ответ 7

NSLayoutFormatOptions реализует протокол OptionSetType, который наследует от SetAlgebraType, который наследует от ArrayLiteralConvertible, поэтому вы можете инициализировать NSLayoutFormatOptions следующим образом: [] или это: [.DirectionLeftToRight, .AlignAllTop]

Итак, вы можете создать ограничения макета, например:

NSLayoutConstraint.constraintsWithVisualFormat("", options: [], metrics: nil, views: [:])

Ответ 8

Вам нужно получить доступ к struct NSLayoutFormatOptions.

Следующие работы для меня.

self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("",
options:NSLayoutFormatOptions.AlignAllBaseline,
metrics: nil, views: nil))

Ответ 9

// topLayoutGuide constraint
    var views: NSMutableDictionary = NSMutableDictionary()
    views.setValue(taskNameField, forKey: "taskNameField")
    views.setValue(self.topLayoutGuide, forKey: "topLayoutGuide")
    let verticalConstraint = "V:[topLayoutGuide]-20-[taskNameField]"
    let constraints:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(verticalConstraint, options: NSLayoutFormatOptions(0), metrics: nil, views: views)
    self.view.addConstraints(constraints)

// bottomLayoutGuide constraint

    var views: NSMutableDictionary = NSMutableDictionary()
    views.setValue(logoutButton, forKey: "logoutButton")
    views.setValue(self.bottomLayoutGuide, forKey: "bottomLayoutGuide")
    let verticalConstraint = "V:[logoutButton]-20-[bottomLayoutGuide]"
    let constraints:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(verticalConstraint, options: NSLayoutFormatOptions(0), metrics: nil, views: views)
    self.view.addConstraints(constraints)