Skip to content

Instantly share code, notes, and snippets.

@shaps80
Last active March 29, 2018 14:13
Show Gist options
  • Select an option

  • Save shaps80/6d4e351c1fd22e2680f6e7e2e04e5331 to your computer and use it in GitHub Desktop.

Select an option

Save shaps80/6d4e351c1fd22e2680f6e7e2e04e5331 to your computer and use it in GitHub Desktop.
Swift Constraints Micro DSL
#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) })
}
}
@shaps80

shaps80 commented Mar 29, 2018

Copy link
Copy Markdown
Author

Credit to https://gist.github.com/SintraWorks/b6c2d4f3dc9110ef5ee9ec1cd89757dd and Chris Eidhof for this technique.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment