Created
December 28, 2017 04:54
-
-
Save SintraWorks/b6c2d4f3dc9110ef5ee9ec1cd89757dd to your computer and use it in GitHub Desktop.
Mini Autolayout DSL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: 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() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you add this after the
import
statements, this same code will compile for macOS.