NSLayoutConstraint.constraintsWithVisualFormat
を使用して、 Swiftの自動レイアウト視覚形式言語 を使用しようとしました。役に立つことは何もしないコードの例を次に示しますが、私が知る限り、型チェッカーを幸せにする必要があります。
let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(
format: "", options: 0, metrics: {}, views: {})
ただし、これによりコンパイラエラーが発生します。
「式のタイプ「[AnyObject]!」を変換できません!」 「String!」と入力します」。
これがレーダーに値するバグだと思う前に、ここで見逃している明らかなものはありますか?これは、変数名を明示的にキャストしなくても、またはas
を使用したその他の無償のダウンキャストでも発生します。コンパイラがこれの一部がString!
に解決されることを期待する理由はわかりません。
これは私のためにエラーなしで機能します:
let bar:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(
nil, options: NSLayoutFormatOptions(0), metrics: nil, views: nil)
上記の行は、st および4番目 パラメーターをオプションにすることはできません。
構文的にこれらは、たとえばこの:
let bar:[AnyObject] = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: ["": self.view])
(Xcode 7の場合、Swift 2.0)
有効な構文は、次のようにパラメーターの名前も要求するようになりました。
NSLayoutFormatOptions(rawValue: 0)
注:このコード行は正しい構文のみを示しています。パラメーター自体は、制約が正しい、または有効であることを保証しません!
最初の落とし穴は、Swift辞書はまだ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)
を使用し、それらのオプションのNSDictionariesにnil
を渡すだけです:
_let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: nil)
_
参考:NSMutableDictでラップする代わりにconstraintWithVisualFormatでビューを使用する場合
["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)
}
これは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
に置き換えます
おそらくチェックアウトする必要があります
NSLayoutFormatOptions
はOptionSetType
を継承するSetAlgebraType
を継承するArrayLiteralConvertible
プロトコルを実装するため、NSLayoutFormatOptions
を初期化できます:[]
またはこれ:[.DirectionLeftToRight, .AlignAllTop]
そのため、次のようなレイアウト制約を作成できます。
NSLayoutConstraint.constraintsWithVisualFormat("", options: [], metrics: nil, views: [:])
_constraintsWithVisualFormat...
_(複数)を生成するためにNSLayoutConstraint
(単数形)を呼び出しているのは少し気になりますが、それは私だけだと確信しています。いずれにせよ、次の2つのトップレベル関数があります。
スニペット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]-|")
_
構造体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)