Skip to content

Instantly share code, notes, and snippets.

@dimitris-c
Created September 27, 2017 15:49
Show Gist options
  • Save dimitris-c/cc3bf6d98d0190d2e226b8b83510a324 to your computer and use it in GitHub Desktop.
Save dimitris-c/cc3bf6d98d0190d2e226b8b83510a324 to your computer and use it in GitHub Desktop.
MiniAutolayoutKit — A super mini auto layout kit for faster programmatic layout
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