Использование визуального формата Autolayout с Swift?
Я пытался использовать язык визуального формата Autolayout в Swift, используя NSLayoutConstraint.constraintsWithVisualFormat
. Вот пример некоторого кода, который не делает ничего полезного, но, насколько я могу судить, должен сделать type checker счастливым:
let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(
format: "", options: 0, metrics: {}, views: {})
однако это вызывает ошибку компилятора:
" не удается преобразовать тип выражения '[AnyObject]!'to type' String!'".
прежде чем я предположу, что это ошибка, достойная радара, есть ли что-нибудь очевидно, что меня здесь нет? Это происходит даже без явного приведения имени переменной или с другим безвозмездным downcasting с использованием as
. Я не вижу причин, по которым компилятор ожидал бы, что какая-либо часть этого разрешится до String!
.
9 ответов
это работает для меня без ошибки:
let bar:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(
nil, options: NSLayoutFormatOptions(0), metrics: nil, views: nil)
обновление
строка выше не может быть скомпилирована с 1st и 4th параметры не могут быть optionals
первый gotcha здесь заключается в том, что Swift Dictionary еще не соединен с NSDictionary. Чтобы это работало, вам нужно явно создать NSDictionary для каждого типа параметров NSDictionary.
кроме того, как указывает Спенсер Холл, {} не является словарным литералом в 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))
попробуйте это-удалите начальное имя переменной (format:
), используйте NSLayoutFormatOptions(0)
, и nil
для этих необязательных NSDictionaries:
let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: nil)
к вашему сведению: если вы используете представления с constraintWithVisualFormat-вместо упаковки с 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( as NSLayoutConstraint)
}
это работает с 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
вы, вероятно, должны проверить
- картография, что является новым подходом, но довольно удивительным. Он использует Autolayout под капотом.
- SnapKit, который я не пробовал, но также является DSL основы автомакет
NSLayoutFormatOptions
осуществляет OptionSetType
протокол, который наследует от SetAlgebraType
, который наследует от ArrayLiteralConvertible
, так что вы можете инициализировать NSLayoutFormatOptions
такой: []
или такой: [.DirectionLeftToRight, .AlignAllTop]
Итак, вы можете создать ограничения макета такой:
NSLayoutConstraint.constraintsWithVisualFormat("", options: [], metrics: nil, views: [:])
меня немного раздражает, что я звоню 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"
to "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]-|")
вы должны получить доступ к структуре NSLayoutFormatOptions
.
следующие работы для меня.
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("",
options:NSLayoutFormatOptions.AlignAllBaseline,
metrics: nil, views: nil))
// 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)