使用 VFL 在 NSView 内部对齐 NSView

Aligning NSView inside NSView using VFL

我有一个 NSView 的子视图(比如 MainView),里面有 NSTextField。

注意:此代码基于此github.repo

MainView 已指定初始化程序如下

init(title : String, layout :NSLayoutAttribute) {
    innerView = self.createTextField(title)//Text field with stringValue = title
    mainView.addSubview(innerView)
    createViewConstrants(innerView, layout: layout)
}

布局可以是 LeftRightCenterX
(内部 TextView 将始终垂直居中,只有水平对齐可以变化)


以下函数为此目的进行约束

func createViewConstrants(view : NSView, layout :NSLayoutAttribute) {

    let viewMap = ["innerView" : view, "superview" : mainView]
    var constraints :[AnyObject]!

    switch layout {
    case .Left:
        println("Left")
        constraints = NSLayoutConstraint.constraintsWithVisualFormat(
            "H:[superview]-(<=1)-[innerView]",
            options: NSLayoutFormatOptions.AlignAllCenterY,
            metrics: nil,
            views: viewMap)
        mainView.addConstraints(constraints)

    case .Right:
        println("Right")
        constraints = NSLayoutConstraint.constraintsWithVisualFormat(
            "H:[superview]-(<=1)-[innerView]-|",
            options: NSLayoutFormatOptions.AlignAllCenterY,
            metrics: nil,
            views: viewMap)
        mainView.addConstraints(constraints)

    case .CenterX:
        println("Center")

        constraints = NSLayoutConstraint.constraintsWithVisualFormat(
            "V:[superview]-(<=1)-[innerView]",
            options: NSLayoutFormatOptions.AlignAllCenterX,
            metrics: nil,
            views: viewMap)
        mainView.addConstraints(constraints)

        constraints = NSLayoutConstraint.constraintsWithVisualFormat(
            "H:[superview]-(<=1)-[innerView]",
            options: NSLayoutFormatOptions.AlignAllCenterY,
            metrics: nil,
            views: viewMap)
        mainView.addConstraints(constraints)

    default:
        fatalError("Invalid layout")

    }
}

Align RightCenter 工作正常,但 Align left 不工作。

此外,由于垂直居中对于所有外壳都是通用的,因此可以将其从开关外壳中取出吗?

几个问题:

  1. 您通常不会在 VFL 中显式列出父视图。相反,您使用垂直线 (|) 字符。这个:

    H:[superview]-(<=1)-[innerView]
    

    并不意味着 innerView 的前沿小于 1 点 "after" 父视图的前沿。这意味着 innerView 的前沿在父视图的 尾随 边缘之后不到 1 点,这可能会将 innerView 置于其父视图的边界之外,这将导致它被剪掉了。

  2. 如果这些是您添加的影响 innerView 的唯一约束,那么您的布局不明确。不等式允许任何可能的值,并且系统没有可靠的方法来选择其中一个值而不是其他任何值。

  3. 您无法真正表达 VFL 中的居中。您可以将对齐标志传递给 constraintsWithVisualFormat(_:options:metrics:views:)(如您所知),但是 a) 这并不是视觉格式语言本身的真正一部分,并且 b) 如果不创建一些伪造的,您就不能这样做,另一个方向上不需要的约束。也就是说,除非您还创建了一些伪造的水平约束,否则您不能使用对齐标志来垂直居中。 (反之亦然。)这是因为对齐标志仅适用于 VFL 字符串中列出的视图,而 VFL 字符串始终暗示约束。

  4. 为什么坚持使用VFL?如果您使用 init(item:attribute:relatedBy:toItem:attribute:multiplier:constant:),您可以简单地传递您的方法接收的 layout 参数。不需要 switch 多个案例。你只需要做:

    var constraint = NSLayoutConstraint(item: innerView,
                                   attribute: layout,
                                   relatedBy: .Equal,
                                      toItem: mainView,
                                   attribute: layout
                                  multiplier: 1,
                                    constant: 0)
    mainView.addConstraint(constraint)
    constraint = NSLayoutConstraint(item: innerView,
                               attribute: .CenterY,
                               relatedBy: .Equal,
                                  toItem: mainView,
                               attribute: .CenterY
                              multiplier: 1,
                                constant: 0)
    mainView.addConstraint(constraint)
    

    这将处理所有三种情况。