Using Autolayout Visual Format with Swift?

I am trying to use Autolayout visual formatting language in Swift using NSLayoutConstraint.constraintsWithVisualFormat . Here is an example of some code that isn’t useful, but as far as I can tell, you need to make type checking happy:

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

However, this causes a compiler error:

"Unable to convert expression type '[AnyObject]!' type "String!"

Before I assume this is a Radar-worthy bug, is there something obvious that I'm missing here? This happens even without casting the variable name explicitly or using other free downcasting with as . I do not see any reason why the compiler expected that any part of this solution would be decided on String! .

+31
autolayout swift visual-format-language
Jun 12 '14 at 16:17
source share
9 answers

this works for me without errors:

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

Update

the above line cannot be compiled since the parameters 1 st and 4 th can no longer be optional.

syntactically they must be installed, for example this:

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



Update

(for Xcode 7, Swift 2.0)

valid syntax now also queries the parameter name, for example:

 NSLayoutFormatOptions(rawValue: 0) 



NOTE: this line of code only shows the correct syntax; the parameters themselves do not guarantee the correctness or even the correctness of the constraint!

+65
Jun 12 '14 at 16:27
source share

The first one is that the Swift Dictionary is not yet connected to NSDictionary. To make this work, you will want to explicitly create an NSDictionary for each NSDictionary parameter.

Also, as Spencer Hall points out, {} is not a dictionary literal in Swift. An empty dictionary is written:

 [:] 

Starting with Xcode 6 Beta 2, this solution allows you to create restrictions with a visual format:

 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
Jun 17 '14 at 19:25
source share

Try this - remove the name of the initial variable ( format: , use NSLayoutFormatOptions(0) and just pass nil for these additional NS dictionaries:

 let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: nil) 
+5
Jun 12 '14 at 16:23
source share

FYI: if you use views with a constraintWithVisualFormat - instead of wrapping with NSMutableDict

 ["myLabel": self.myLabel!] 

and more specifically

 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
Sep 05 '14 at 11:43 on
source share

This works with 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) } } 

Then you can use calls such as:

 var views : [String: NSView] = ["box": self.box] self.view.addConstraints("V:[box(100)]", views: views) 

This works to add limitations. If you are using iOS, replace UIView with NSView




You should probably check

  • Cartography , which is a new approach, but rather surprising. He uses Autolayout under the hood.
  • SnapKit , which I have not tried yet, but also is a DSL auto-detection system
+4
Jan 23 '15 at 6:53
source share

It annoys me a bit that I call NSLayoutConstraint (singular) to generate constraintsWithVisualFormat... (plural), although I'm sure that only I am. In any case, I have two top-level functions:

fragment 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] } 

What can be used like this:

 superView.addConstraints(NSLayoutConstraints("B:|[v]|", view)) 

In other words, the views are automatically called "v1" to "v\(views.count)" (except for the first view, which can also be called "v" ). In addition, with the "B:" format prefix, the restrictions "H:" and "V:" will be generated. Thus, an example line of code means that "make sure that the view always matches the superView ."

And with the following extensions:

fragment 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) } } 

We can make some common tasks much more elegant, for example, add a button with a standard offset in the lower right corner:

 superView.addSubview(button, constraints: "B:[v]-|") 

For example, on an iOS playground:

 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
May 20 '15 at 14:32
source share

NSLayoutFormatOptions implements the OptionSetType protocol, which inherits from SetAlgebraType , which inherits from ArrayLiteralConvertible , so you can initialize NSLayoutFormatOptions as follows: [] or this: [.DirectionLeftToRight, .AlignAllTop]

So you can create layout constraints, for example:

 NSLayoutConstraint.constraintsWithVisualFormat("", options: [], metrics: nil, views: [:]) 
+1
Nov 19 '15 at 18:42
source share

You need to access struct NSLayoutFormatOptions .

The following works for me.

 self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("", options:NSLayoutFormatOptions.AlignAllBaseline, metrics: nil, views: nil)) 
0
Jul 02 '14 at 18:11
source share
 // 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
Jul 12 '14 at 20:50
source share



All Articles