33

我一直在尝试在 Swift 中使用 Autolayout 视觉格式语言,使用NSLayoutConstraint.constraintsWithVisualFormat. 这是一些代码的示例,它没有任何用处,但据我所知应该让类型检查器满意:

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

但是,这会触发编译器错误:

“无法转换表达式的类型'[AnyObject]!' 键入“字符串!”。

在我认为这是一个值得雷达发现的错误之前,我在这里有什么明显的遗漏吗?即使没有显式转换变量名,或者使用as. 我看不出编译器为什么期望其中的任何部分解析为String!.

4

9 回答 9

67

这对我有用,没有错误:

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

更新

上面的行可能无法编译,因为第 1第4参数不再是可选参数。

从语法上讲,必须设置这些,例如:

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

更新

(对于 Xcode 7、Swift 2.0)

有效的语法现在也请求参数的名称,例如:

NSLayoutFormatOptions(rawValue: 0)

注意:这行代码只显示了正确的语法,参数本身不能保证约束是正确的甚至是有效的!

于 2014-06-12T16:27:10.843 回答
13

这里的第一个问题是 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))
于 2014-06-17T19:25:08.110 回答
5

仅供参考:如果您使用带有 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($0 as NSLayoutConstraint)
    }
于 2014-09-05T11:43:54.967 回答
5

试试这个 - 删除初始变量名 ( format:),使用NSLayoutFormatOptions(0),然后只传递nil那些可选的 NSDictionaries:

let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: nil)
于 2014-06-12T16:23:13.207 回答
4

这适用于 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


你应该看看

  • 制图,这是一种新方法,但非常棒。它在引擎盖下使用自动布局。
  • SnapKit,我没有尝试过,但它也是一个 DSL 自动布局框架
于 2015-01-23T06:53:19.007 回答
3

NSLayoutFormatOptions实现OptionSetType协议,继承自SetAlgebraTypewhich 继承自,因此您可以像这样ArrayLiteralConvertible初始化:或这样:NSLayoutFormatOptions[][.DirectionLeftToRight, .AlignAllTop]

因此,您可以像这样创建布局约束:

NSLayoutConstraint.constraintsWithVisualFormat("", options: [], metrics: nil, views: [:])
于 2015-11-19T18:42:06.967 回答
1

我正在调用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]-|")
于 2015-05-20T14:32:09.827 回答
0
// 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)
于 2014-07-12T20:50:49.747 回答
0

您必须访问 struct NSLayoutFormatOptions

以下对我有用。

self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("",
options:NSLayoutFormatOptions.AlignAllBaseline,
metrics: nil, views: nil))
于 2014-07-02T18:11:04.073 回答