【问题标题】:Using Autolayout Visual Format with Swift?在 Swift 中使用 Autolayout 视觉格式?
【发布时间】:2014-08-03 01:31:57
【问题描述】:

我一直在尝试使用Autolayout Visual Format Language in Swift,使用NSLayoutConstraint.constraintsWithVisualFormat。这是一些代码的示例,它没有任何用处,但据我所知应该让类型检查器满意:

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

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

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

在我认为这是一个值得雷达发现的错误之前,我在这里有什么明显的遗漏吗?即使没有显式转换变量名或使用as 进行其他无偿向下转换,也会发生这种情况。我看不出编译器为什么期望其中的任何部分解析为String!

【问题讨论】:

  • 请注意,Swift 字典使用 [] 而不是 {}
  • 如果我将其复制并粘贴到操场上,我不会遇到与您相同的错误...

标签: autolayout swift visual-format-language


【解决方案1】:

这对我有用,没有错误:

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

更新

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

语法必须设置这些,例如这个:

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

更新

(对于 Xcode 7、Swift 2.0)

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

NSLayoutFormatOptions(rawValue: 0)

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

【讨论】:

  • 这不会为我编译(Xcode 6.0.1),可能是因为第一个和第四个参数不接受nil
  • NSLayoutFormatOptions(0)救了我!像在 obj-c 中那样使用 0 作为选项是行不通的,并且不会告诉你错误的原因......谢谢
  • Xcode 7 告诉我选项现在是:NSLayoutFormatOptions(rawValue: 0)
  • 正如 juanjo 所说,没有必要做NSLayoutFormatOptions(rawValue: 0)。只需输入[]
【解决方案2】:

这里的第一个问题是 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))

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      仅供参考: 如果您使用带有 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)
          }
      

      【讨论】:

      • BOOM,allZeros 来自哪里以及为什么会起作用。急速赶来。
      • :-D ...没有在当前版本上尝试过... 5.Sep它有效;-)
      • allZeros 未使用 XCode 7.3 定义。如上所述使用 NSLayoutFormatOptions(rawValue: 0)。
      【解决方案5】:

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


      你应该去看看

      • Cartography,这是一种新方法,但非常棒。它在后台使用自动布局。
      • SnapKit,没试过但也是一个DSL自动布局框架

      【讨论】:

      • 即使您使用 Cartography 或 Snapkit,您也需要阅读 Autolayout 上的文档以了解为什么某些事情会以它们的方式工作。此外,这是在任何时候都很有用的建议,有一个单独的项目,您可以在这个项目中不断地在干净的环境中尝试新事物。我的称为“PlayingWithUI”,它使用 Git 分支来处理我想了解的每个新概念,从 Carthage 集成到 Autolayout 再到使用某些控件类型。
      【解决方案6】:

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

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

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

      【讨论】:

        【解决方案7】:

        我调用NSLayoutConstraint(单数)来生成constraintsWithVisualFormat...(复数)让我有点恼火,但我确定这就是我。无论如何,我有这两个顶级功能:

        sn-p 1(斯威夫特 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”。

        并带有以下扩展名:

        sn-p 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]-|")
        

        【讨论】:

        • @WanbokChoi,我刚刚检查了 iOS 上的代码,它可以工作(至少在 Swift 1.2 中)。您是否忘记将 NSView 更改为 UIView 并将 translatesAutoresizingMaskIntoConstraints 更改为等效的 UIView?无论如何,我已经更新了答案以自动执行此操作...
        • 感谢您检查了代码。我忘记了我的情况,但是,我想你是对的。 :)
        【解决方案8】:

        您必须访问结构体NSLayoutFormatOptions

        以下对我有用。

        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("",
        options:NSLayoutFormatOptions.AlignAllBaseline,
        metrics: nil, views: nil))
        

        【讨论】:

          【解决方案9】:
          // 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)
          

          【讨论】:

          • 使用普通的 Swift 字典,例如 let views: [NSObject : AnyObject] = ["icon" : iconImageView] 在 Xcode 6.1.1 中显然可以正常工作。
          猜你喜欢
          • 1970-01-01
          • 2013-08-13
          • 2017-07-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多