Using Autolayout Visual Format with Swift?

后端 未结 9 1965
执笔经年
执笔经年 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: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]-|")
    

提交回复
热议问题