Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@amlcurran
Last active June 11, 2018 09:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amlcurran/2e2db6fe7e830d25426652a35eabe390 to your computer and use it in GitHub Desktop.
Save amlcurran/2e2db6fe7e830d25426652a35eabe390 to your computer and use it in GitHub Desktop.
import UIKit
extension UIView {
func constrain(toSuperview edges: NSLayoutAttribute..., insetBy inset: CGFloat = 0) {
for edge in edges {
constrain(toSuperview: edge, withOffset: offset(for: edge, ofInset: inset))
}
}
func constrain(toSuperviewSafeArea edges: NSLayoutAttribute..., insetBy inset: CGFloat = 0) {
_ = prepareForConstraints()
var edges = edges
if edges.contains(.leading) {
leadingAnchor.constraint(equalTo: superview!.leadingSafeAnchor,
constant: offset(for: .leading, ofInset: inset)).isActive = true
edges.remove(.leading)
}
if edges.contains(.trailing) {
trailingAnchor.constraint(equalTo: superview!.trailingSafeAnchor,
constant: offset(for: .trailing, ofInset: inset)).isActive = true
edges.remove(.trailing)
}
if edges.contains(.top) {
topAnchor.constraint(equalTo: superview!.topSafeAnchor,
constant: offset(for: .top, ofInset: inset)).isActive = true
edges.remove(.top)
}
if edges.contains(.bottom) {
bottomAnchor.constraint(equalTo: superview!.bottomSafeAnchor,
constant: offset(for: .bottom, ofInset: inset)).isActive = true
edges.remove(.bottom)
}
if edges.isEmpty == false {
debugPrint("Constraining to superview safe area was left with the following unconstrained attributes: \(edges)")
}
}
private var leadingSafeAnchor: NSLayoutXAxisAnchor {
if #available(iOS 11.0, *) {
return safeAreaLayoutGuide.leadingAnchor
} else {
return leadingAnchor
}
}
private var trailingSafeAnchor: NSLayoutXAxisAnchor {
if #available(iOS 11.0, *) {
return safeAreaLayoutGuide.trailingAnchor
} else {
return trailingAnchor
}
}
private var topSafeAnchor: NSLayoutYAxisAnchor {
if #available(iOS 11.0, *) {
return safeAreaLayoutGuide.topAnchor
} else {
return topAnchor
}
}
private var bottomSafeAnchor: NSLayoutYAxisAnchor {
if #available(iOS 11.0, *) {
return safeAreaLayoutGuide.bottomAnchor
} else {
return bottomAnchor
}
}
func constrain(toSafeAreaTopOf viewController: UIViewController, insetBy inset: CGFloat = 0) {
if #available(iOS 11.0, *) {
topAnchor.constraint(equalTo: viewController.view.safeAreaLayoutGuide.topAnchor, constant: inset).isActive = true
} else {
topAnchor.constraint(equalTo: viewController.topLayoutGuide.bottomAnchor, constant: inset).isActive = true
}
}
private func offset(for edge: NSLayoutAttribute, ofInset inset: CGFloat) -> CGFloat {
switch edge {
case .top, .topMargin, .leading, .leadingMargin:
return inset
case .bottom, .bottomMargin, .trailing, .trailingMargin:
return -inset
default:
print("Warning, offset not handled for edge \(edge)")
return inset
}
}
@available(iOS 11.0, *)
func constrainToSafeAreaBottom(of viewController: UIViewController, insetBy inset: CGFloat = 0) {
bottomAnchor.constraint(equalTo: viewController.view.safeAreaLayoutGuide.bottomAnchor, constant: -inset).isActive = true
}
@discardableResult func constrain(toSuperview edge: NSLayoutAttribute, withOffset offset: CGFloat = 0) -> NSLayoutConstraint {
let superview = prepareForConstraints()
let constraint = NSLayoutConstraint(item: self, attribute: edge, relatedBy: .equal, toItem: superview, attribute: edge, multiplier: 1, constant: offset)
constraint.isActive = true
return constraint
}
@discardableResult func constrain(to view: UIView, _ edge: NSLayoutAttribute, withOffset offset: CGFloat = 0) -> NSLayoutConstraint {
let constraint = NSLayoutConstraint(item: self, attribute: edge, relatedBy: .equal, toItem: view, attribute: edge, multiplier: 1, constant: offset)
constraint.isActive = true
return constraint
}
func constrain(to view: UIView, edges: NSLayoutAttribute..., withOffset offset: CGFloat = 0) {
for edge in edges {
constrain(to: view, edge, withOffset: offset)
}
}
@discardableResult func constrain(height: CGFloat) -> NSLayoutConstraint {
let constraint = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: height)
constraint.isActive = true
return constraint
}
@discardableResult func constrain(width: CGFloat) -> NSLayoutConstraint {
let constraint = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: width)
constraint.isActive = true
return constraint
}
func hideConstraint() -> NSLayoutConstraint {
return constrain(height: 0)
}
@discardableResult func constrain(_ edge: NSLayoutAttribute, to view: UIView, _ otherEdge: NSLayoutAttribute, withOffset offset: CGFloat = 0) -> NSLayoutConstraint {
_ = prepareForConstraints()
let constraint = NSLayoutConstraint(item: self, attribute: edge, relatedBy: .equal, toItem: view, attribute: otherEdge, multiplier: 1, constant: offset)
constraint.isActive = true
return constraint
}
func add(_ view: UIView, constrainedTo edges: [NSLayoutAttribute], withInset inset: CGFloat = 0) {
addSubview(view)
for edge in edges {
view.constrain(toSuperview: edge, insetBy: inset)
}
}
private func prepareForConstraints() -> UIView {
guard let superview = self.superview else {
fatalError("view doesn't have a superview")
}
translatesAutoresizingMaskIntoConstraints = false
return superview
}
func hugContent(_ axis: UILayoutConstraintAxis) {
setContentHuggingPriority(.required, for: axis)
}
}
private extension Array where Element == NSLayoutAttribute {
mutating func remove(_ attribute: NSLayoutAttribute) {
if let foundIndex = index(of: attribute) {
remove(at: foundIndex)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment