web-dev-qa-db-ja.com

SwiftでAutolayout Visual Formatを使用していますか?

NSLayoutConstraint.constraintsWithVisualFormatを使用して、 Swiftの自動レイアウト視覚形式言語 を使用しようとしました。役に立つことは何もしないコードの例を次に示しますが、私が知る限り、型チェッカーを幸せにする必要があります。

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

ただし、これによりコンパイラエラーが発生します。

「式のタイプ「[AnyObject]!」を変換できません!」 「String!」と入力します」。

これがレーダーに値するバグだと思う前に、ここで見逃している明らかなものはありますか?これは、変数名を明示的にキャストしなくても、またはasを使用したその他の無償のダウンキャストでも発生します。コンパイラがこれの一部がString!に解決されることを期待する理由はわかりません。

33
Mike Walker

これは私のためにエラーなしで機能します:

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)

注:このコード行は正しい構文のみを示しています。パラメーター自体は、制約が正しい、または有効であることを保証しません!

67
holex

最初の落とし穴は、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))
13
John M. P. Knox

これを試してください-初期変数名(_format:_)を削除し、NSLayoutFormatOptions(0)を使用し、それらのオプションのNSDictionariesにnilを渡すだけです:

_let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: nil)
_
5
Nate Cook

参考: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)
    }
5
StrangeDays

これは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を使用している場合は、UIViewNSViewに置き換えます


おそらくチェックアウトする必要があります

  • Cartography 、これは新しいアプローチですが、非常に素晴らしいです。フードの下でAutolayoutを使用します。
  • SnapKit 、これは試していませんが、DSL自動レイアウトフレームワークでもあります
4
Dan Rosenstark

NSLayoutFormatOptionsOptionSetTypeを継承するSetAlgebraTypeを継承するArrayLiteralConvertibleプロトコルを実装するため、NSLayoutFormatOptionsを初期化できます:[]またはこれ:[.DirectionLeftToRight, .AlignAllTop]

そのため、次のようなレイアウト制約を作成できます。

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

_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]-|")
_
1
milos

構造体NSLayoutFormatOptionsにアクセスする必要があります。

私のために次の作品。

self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("",
options:NSLayoutFormatOptions.AlignAllBaseline,
metrics: nil, views: nil))
0
Borja Igartua
// 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)
0