Instantly share code, notes, and snippets.

Embed
What would you like to do?
Mini Autolayout DSL
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
typealias PairedConstraint = (_ view: UIView, _ otherView: UIView) -> NSLayoutConstraint
typealias UnpairedConstraint = (_ view: UIView) -> NSLayoutConstraint
enum ConstraintRelation {
case equal, greaterThanOrEqual, lessThanOrEqual
}
func constraint<Anchor, AnchorType>(_ keyPath: KeyPath<UIView, Anchor>,
_ otherKeyPath: KeyPath<UIView, Anchor>? = nil,
constraintRelation: ConstraintRelation = .equal,
multiplier: CGFloat? = nil,
constant: CGFloat = 0,
priority: UILayoutPriority? = nil) -> PairedConstraint where Anchor: NSLayoutAnchor<AnchorType> {
return { view, otherView in
var partialConstraint: NSLayoutConstraint
let otherKeyPath: KeyPath<UIView, Anchor> = otherKeyPath ?? keyPath
switch constraintRelation {
case .equal:
partialConstraint = view[keyPath: keyPath].constraint(equalTo: otherView[keyPath: otherKeyPath], constant: constant)
case .greaterThanOrEqual:
partialConstraint = view[keyPath: keyPath].constraint(greaterThanOrEqualTo: otherView[keyPath: otherKeyPath], constant: constant)
case .lessThanOrEqual:
partialConstraint = view[keyPath: keyPath].constraint(lessThanOrEqualTo: otherView[keyPath: otherKeyPath], constant: constant)
}
return constraint(from: partialConstraint,
withMultiplier:multiplier,
priority: priority)
}
}
func constraint<Anchor>(_ keyPath: KeyPath<UIView, Anchor>,
constraintRelation: ConstraintRelation = .equal,
multiplier: CGFloat? = nil,
constant: CGFloat = 0,
priority: UILayoutPriority? = nil) -> UnpairedConstraint where Anchor: NSLayoutDimension {
return { view in
var partialConstraint: NSLayoutConstraint
switch constraintRelation {
case .equal:
partialConstraint = view[keyPath: keyPath].constraint(equalToConstant: constant)
case .greaterThanOrEqual:
partialConstraint = view[keyPath: keyPath].constraint(greaterThanOrEqualToConstant: constant)
case .lessThanOrEqual:
partialConstraint = view[keyPath: keyPath].constraint(lessThanOrEqualToConstant: constant)
}
return constraint(from: partialConstraint,
withMultiplier:multiplier,
priority: priority)
}
}
func constraint(from constraint: NSLayoutConstraint,
withMultiplier multiplier: CGFloat? = nil,
priority: UILayoutPriority?) -> NSLayoutConstraint {
var constraint = constraint
if let multiplier = multiplier {
constraint = NSLayoutConstraint(item: constraint.firstItem as Any,
attribute: constraint.firstAttribute,
relatedBy: constraint.relation,
toItem: constraint.secondItem,
attribute: constraint.secondAttribute,
multiplier: multiplier,
constant: constraint.constant)
}
if let priority = priority {
constraint.priority = priority
}
return constraint
}
extension UIView {
func addSubview(_ child: UIView, pairingTo pairingView: UIView? = nil, constraints: [PairedConstraint]) {
addSubview(child)
child.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(constraints.map { $0(child, pairingView ?? self) })
}
func constrainToView(_ pairingView: UIView, constraints: [PairedConstraint]) {
NSLayoutConstraint.activate(constraints.map { $0(self, pairingView) })
}
func constrain(to constraints: [UnpairedConstraint]) {
NSLayoutConstraint.activate(constraints.map { $0(self) })
}
}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let label = UILabel()
label.backgroundColor = .yellow
label.textColor = .black
label.textAlignment = .center
label.text = "Hello World!"
let greenView = UIView()
greenView.backgroundColor = .green
view.addSubview(greenView, constraints: [
constraint(\.leftAnchor, constant: 20.0, priority: .required),
constraint(\.rightAnchor, constant: -20.0),
constraint(\.topAnchor, constant: 220.0),
constraint(\.bottomAnchor, constant: -20.0)
])
view.addSubview(label, pairingTo:greenView, constraints: [
constraint(\.centerXAnchor),
constraint(\.centerYAnchor),
])
let purpleView = UIView()
purpleView.backgroundColor = .purple
view.addSubview(purpleView, pairingTo:greenView, constraints: [
constraint(\.leftAnchor, \.centerXAnchor),
])
purpleView.constrainToView(label, constraints: [
constraint(\.widthAnchor, constraintRelation: .greaterThanOrEqual, multiplier: 0.5),
constraint(\.topAnchor, \.bottomAnchor, constant: 10.0, priority: UILayoutPriority(500))
])
label.constrain(to: [
constraint(\.widthAnchor, constant: 250.0, priority: .defaultHigh)
])
purpleView.constrain(to: [
constraint(\.heightAnchor, constant: 60.0)
])
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
@shaps80

This comment has been minimized.

shaps80 commented Mar 29, 2018

If you add this after the import statements, this same code will compile for macOS.

#if os(iOS)
    import UIKit
#else
    import Cocoa
    public typealias UIView = NSView
    public typealias UILayoutPriority = NSLayoutConstraint.Priority
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment