Using Autolayout Visual Format with Swift?

后端 未结 9 1962
执笔经年
执笔经年 2021-02-01 14:57

I\'ve been trying to use the Autolayout Visual Format Language in Swift, using NSLayoutConstraint.constraintsWithVisualFormat. Here\'s an example of some code that

相关标签:
9条回答
  • 2021-02-01 15:35

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

    ["myLabel": self.myLabel!]
    

    and to be more specific

    var constraints = [NSLayoutConstraint]()
    NSLayoutConstraint.constraintsWithVisualFormat("H:|-15-[myLabel]-15-|", 
        options:NSLayoutFormatOptions.allZeros, 
        metrics: nil, 
        views: ["myLabel": self.myLabel!]).map {
            constraints.append($0 as NSLayoutConstraint)
        }
    
    0 讨论(0)
  • 2021-02-01 15:43

    It slightly annoys me that I'm calling NSLayoutConstraint (singular) to generate constraintsWithVisualFormat... (plural), though I'm sure that's just me. In any case, I have these two top level functions:

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

    Which can be used like so:

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

    In other words, views are auto-named "v1" to "v\(views.count)" (except the first view which can be also referred to as "v"). In addition, prefixing the format with "B:" will generate both the "H:" and "V:" constraints. The example line of code above therefore means, "make sure the view always fits the superView".

    And with the following extensions:

    snippet 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 do some common tasks much more elegantly, like adding a button at a standard offset from the bottom right corner:

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

    For example, in 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]-|")
    
    0 讨论(0)
  • 2021-02-01 15:43
    // 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 讨论(0)
  • 2021-02-01 15:44

    The first gotcha here is that Swift Dictionary is not yet bridged with NSDictionary. To get this to work, you'll want to explicitly create a NSDictionary for each NSDictionary-typed parameters.

    Also, as Spencer Hall points out, {} isn't a dictionary literal in Swift. The empty dictionary is written:

    [:]
    

    As of XCode 6 Beta 2, this solution allows you to create constraints with the 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))
    
    0 讨论(0)
  • 2021-02-01 15:44

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

    So, you can create the layout constraints like this:

    NSLayoutConstraint.constraintsWithVisualFormat("", options: [], metrics: nil, views: [:])
    
    0 讨论(0)
  • 2021-02-01 15:52

    You have to access to the struct NSLayoutFormatOptions.

    Following works for me.

    self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("",
    options:NSLayoutFormatOptions.AlignAllBaseline,
    metrics: nil, views: nil))
    
    0 讨论(0)
提交回复
热议问题