Last active
March 29, 2018 14:13
-
-
Save shaps80/6d4e351c1fd22e2680f6e7e2e04e5331 to your computer and use it in GitHub Desktop.
Swift Constraints Micro 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
| #if os(iOS) | |
| import UIKit | |
| #else | |
| import Cocoa | |
| public typealias UIView = NSView | |
| public typealias UILayoutPriority = NSLayoutConstraint.Priority | |
| #endif | |
| public typealias Constraint = (_ view: UIView, _ otherView: UIView?) -> NSLayoutConstraint | |
| public enum ConstraintRelation { | |
| case equal, greaterThanOrEqual, lessThanOrEqual | |
| } | |
| public 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) -> Constraint where Anchor: NSLayoutAnchor<AnchorType> { | |
| return { view, otherView in | |
| guard let otherView = otherView else { fatalError("Pairing view missing")} | |
| var partialConstraint: NSLayoutConstraint | |
| let otherKeyPath = 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 fullConstraint(from: partialConstraint, withMultiplier: multiplier, priority: priority) | |
| } | |
| } | |
| public func constraint<Anchor>(_ keyPath: KeyPath<UIView, Anchor>, _ otherKeyPath: KeyPath<UIView, Anchor>? = nil, constraintRelation: ConstraintRelation = .equal, multiplier: CGFloat? = nil, constant: CGFloat = 0, priority: UILayoutPriority? = nil) -> Constraint where Anchor: NSLayoutDimension { | |
| return { view, otherView in | |
| func constraint(otherView: UIView, | |
| otherKeyPath: KeyPath<UIView, Anchor>?) -> NSLayoutConstraint { | |
| if let otherKeyPath = otherKeyPath { | |
| switch constraintRelation { | |
| case .equal: | |
| return view[keyPath: keyPath].constraint(equalTo: otherView[keyPath: otherKeyPath], constant: constant) | |
| case .greaterThanOrEqual: | |
| return view[keyPath: keyPath].constraint(greaterThanOrEqualTo: otherView[keyPath: otherKeyPath], constant: constant) | |
| case .lessThanOrEqual: | |
| return view[keyPath: keyPath].constraint(lessThanOrEqualTo: otherView[keyPath: otherKeyPath], constant: constant) | |
| } | |
| } else { | |
| switch constraintRelation { | |
| case .equal: | |
| return view[keyPath: keyPath].constraint(equalToConstant: constant) | |
| case .greaterThanOrEqual: | |
| return view[keyPath: keyPath].constraint(greaterThanOrEqualToConstant: constant) | |
| case .lessThanOrEqual: | |
| return view[keyPath: keyPath].constraint(lessThanOrEqualToConstant: constant) | |
| } | |
| } | |
| } | |
| return fullConstraint(from: constraint(otherView: otherView ?? view, otherKeyPath: otherView == nil ? otherKeyPath : otherKeyPath ?? keyPath), withMultiplier: multiplier, priority: priority) | |
| } | |
| } | |
| public func fullConstraint(from partialConstraint: NSLayoutConstraint, withMultiplier multiplier: CGFloat? = nil, priority: UILayoutPriority?) -> NSLayoutConstraint { | |
| var constraint = partialConstraint | |
| 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 { | |
| public func addSubview(_ child: UIView, pairingTo pairingView: UIView? = nil, constraints: [Constraint]) { | |
| addSubview(child) | |
| child.translatesAutoresizingMaskIntoConstraints = false | |
| NSLayoutConstraint.activate(constraints.map { $0(child, pairingView ?? self) }) | |
| } | |
| public func constrainToView(_ pairingView: UIView, constraints: [Constraint]) { | |
| NSLayoutConstraint.activate(constraints.map { $0(self, pairingView) }) | |
| } | |
| public func constrain(to constraints: [Constraint]) { | |
| NSLayoutConstraint.activate(constraints.map { $0(self, nil) }) | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Credit to https://gist.github.com/SintraWorks/b6c2d4f3dc9110ef5ee9ec1cd89757dd and Chris Eidhof for this technique.