Last active
June 11, 2018 09:40
-
-
Save amlcurran/2e2db6fe7e830d25426652a35eabe390 to your computer and use it in GitHub Desktop.
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 { | |
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