Created
September 27, 2017 15:49
-
-
Save dimitris-c/cc3bf6d98d0190d2e226b8b83510a324 to your computer and use it in GitHub Desktop.
MiniAutolayoutKit — A super mini auto layout kit for faster programmatic layout
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
import UIKit | |
extension UIView { | |
public func prepareForAutolayout() { | |
translatesAutoresizingMaskIntoConstraints = false | |
} | |
public func autopin(constant: CGFloat = 0) { | |
autopin(.top, constant: constant) | |
autopin(.right, constant: constant) | |
autopin(.bottom, constant: constant) | |
autopin(.left, constant: constant) | |
} | |
public func autopin(_ attribute: NSLayoutAttribute, constant: CGFloat = 0) { | |
guard let superview = superview else { return } | |
self.pin(attribute, to: superview, attribute, constant: constant) | |
} | |
public func pin(_ attr: NSLayoutAttribute, to view: UIView, _ otherAttr: NSLayoutAttribute, constant: CGFloat) { | |
let shouldInvert = attr == .right || attr == .bottom | |
let finalConstant = shouldInvert ? -constant : constant | |
let constraint = NSLayoutConstraint(item: self, attribute: attr, relatedBy: .equal, toItem: view, attribute: otherAttr, multiplier: 1, constant: finalConstant) | |
self.superview?.addConstraint(constraint) | |
} | |
public func pinGreater(_ attr: NSLayoutAttribute, constant: CGFloat) { | |
let shouldInvert = attr == .right || attr == .bottom | |
let finalConstant = shouldInvert ? -constant : constant | |
let constraint = NSLayoutConstraint(item: self, attribute: attr, relatedBy: .greaterThanOrEqual, toItem: superview, attribute: attr, multiplier: 1, constant: finalConstant) | |
self.superview?.addConstraint(constraint) | |
} | |
@discardableResult | |
public func autoHeight(_ constant: CGFloat) -> NSLayoutConstraint { | |
let constraint = NSLayoutConstraint(item: self, | |
attribute: .height, | |
relatedBy: .equal, | |
toItem: nil, | |
attribute: .notAnAttribute, | |
multiplier: 1, | |
constant: constant) | |
self.addConstraint(constraint) | |
return constraint | |
} | |
public func autoHeight(ratio: CGFloat) { | |
let constraint = NSLayoutConstraint(item: self, | |
attribute: .height, | |
relatedBy: .equal, | |
toItem: superview, | |
attribute: .height, | |
multiplier: ratio, | |
constant: 0) | |
superview?.addConstraint(constraint) | |
} | |
public func autoWidth(_ constant: CGFloat) { | |
let constraint = NSLayoutConstraint(item: self, | |
attribute: .width, | |
relatedBy: .equal, | |
toItem: nil, | |
attribute: .notAnAttribute, | |
multiplier: 1, | |
constant: constant) | |
self.addConstraint(constraint) | |
} | |
public func autoWidth(ratio: CGFloat) { | |
let constraint = NSLayoutConstraint(item: self, | |
attribute: .width, | |
relatedBy: .equal, | |
toItem: superview, | |
attribute: .width, | |
multiplier: ratio, | |
constant: 0) | |
superview?.addConstraint(constraint) | |
} | |
public func pinToTop(margins: CGFloat = 0) { | |
autopin(.top, constant: margins) | |
autopin(.right, constant: margins) | |
autopin(.left, constant: margins) | |
} | |
public func pinToBottom(margins: CGFloat = 0) { | |
autopin(.bottom, constant: margins) | |
autopin(.right, constant: margins) | |
autopin(.left, constant: margins) | |
} | |
public func autopin(_ attributes: [NSLayoutAttribute], edgeInsets: UIEdgeInsets) { | |
attributes.forEach { autopin($0, constant: edgeInsets.insetFor($0)) } | |
} | |
/// stacks the views from the given attribute. | |
public func stack(_ views: [UIView], from attribute: NSLayoutAttribute, edgeInsets: UIEdgeInsets = .zero, spacing: CGFloat = 0) { | |
var ref: UIView? = nil | |
for v in views { | |
v.prepareForAutolayout() | |
addSubview(v) | |
if let ref = ref { | |
v.pin(attribute, to: ref, attribute.opposite, constant: spacing) | |
v.autopin(attribute.sides, edgeInsets: edgeInsets) | |
} else { | |
v.autopin([attribute] + attribute.sides, edgeInsets: edgeInsets) | |
} | |
ref = v | |
} | |
ref?.autopin(attribute.opposite, constant: edgeInsets.insetFor(attribute.opposite)) | |
ref?.setContentHuggingPriority(200, for: attribute.axis) | |
} | |
public func stackEqual(_ views: [UIView], from attribute: NSLayoutAttribute, edgeInsets: UIEdgeInsets = .zero, spacing: CGFloat = 0) { | |
var ref: UIView? = nil | |
for v in views { | |
v.prepareForAutolayout() | |
addSubview(v) | |
if let ref = ref { | |
v.pin(attribute, to: ref, attribute.opposite, constant: spacing) | |
v.autopin(attribute.sides, edgeInsets: edgeInsets) | |
v.pin(.width, to: ref, .width, constant: 0) | |
} else { | |
v.autopin([attribute] + attribute.sides, edgeInsets: edgeInsets) | |
} | |
ref = v | |
} | |
ref?.autopin(attribute.opposite, constant: edgeInsets.insetFor(attribute.opposite)) | |
} | |
} | |
public extension NSLayoutAttribute { | |
public var opposite: NSLayoutAttribute { | |
switch self { | |
case .right: return .left | |
case .left: return .right | |
case .bottom: return .top | |
case .top: return .bottom | |
default: return .notAnAttribute | |
} | |
} | |
public var sides: [NSLayoutAttribute] { | |
switch self { | |
case .right, .left: return [.top, .bottom] | |
case .bottom, .top: return [.left, .right] | |
default: return [] | |
} | |
} | |
public var axis: UILayoutConstraintAxis { | |
switch self { | |
case .right, .left: return .horizontal | |
default: return .vertical | |
} | |
} | |
} | |
extension UIEdgeInsets { | |
public func insetFor(_ attribute: NSLayoutAttribute) -> CGFloat { | |
switch attribute { | |
case .right: return right | |
case .left: return left | |
case .bottom: return bottom | |
case .top: return top | |
default: return 0 | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment