Skip to content

Instantly share code, notes, and snippets.

@amosavian
Last active August 18, 2019 15:39
Show Gist options
  • Save amosavian/c7e16dc39bf9bd31fa2010284223ff55 to your computer and use it in GitHub Desktop.
Save amosavian/c7e16dc39bf9bd31fa2010284223ff55 to your computer and use it in GitHub Desktop.
Helper methods to ease usage of auto-layout
extension UIViewController {
enum LayoutGuide {
case none
case layoutMargin
case readableContent
case safeArea
}
func embed(_ viewController: UIViewController, into: UIView? = nil, inset: UIEdgeInsets = .zero, edges: UIRectEdge = .all,
guide: UILayoutGuide? = nil, priorities: UIView.EdgePriorities = .init()) {
let view = into ?? self.view!
self.addChild(viewController)
viewController.beginAppearanceTransition(<#T##isAppearing: Bool##Bool#>, animated: <#T##Bool#>)
view.embed(view: viewController.view, inset: inset, edges: edges, guide: guide, priorities: priorities)
viewController.didMove(toParent: self)
}
func unembedFromParent() {
guard parent != nil else {
return
}
willMove(toParent: nil)
view.removeFromSuperview()
removeFromParent()
}
var safeTopAnchor: NSLayoutYAxisAnchor {
if #available(iOS 11.0, *) {
return view.safeAreaLayoutGuide.topAnchor
} else {
return topLayoutGuide.bottomAnchor
}
}
var safeBottomAnchor: NSLayoutYAxisAnchor {
if #available(iOS 11.0, *) {
return view.safeAreaLayoutGuide.bottomAnchor
} else {
return bottomLayoutGuide.topAnchor
}
}
}
extension UIView {
public enum SizeContainment {
case minimum, equal, maximum
}
public struct EdgePriorities {
var top: UILayoutPriority?
var leading: UILayoutPriority?
var bottom: UILayoutPriority?
var trailing: UILayoutPriority?
static let required: EdgePriorities = .init(top: .required, leading: .required, bottom: .required, trailing: .required)
static let defaultHigh: EdgePriorities = .init(top: .defaultHigh, leading: .defaultHigh, bottom: .defaultHigh, trailing: .defaultHigh)
init(top: UILayoutPriority? = nil, leading: UILayoutPriority? = nil,
bottom: UILayoutPriority? = nil, trailing: UILayoutPriority? = nil) {
self.top = top
self.leading = leading
self.bottom = bottom
self.trailing = trailing
}
}
func embed(view: UIView, inset: UIEdgeInsets = .zero, edges: UIRectEdge = .all,
guide: UILayoutGuide? = nil, priorities: EdgePriorities = .init()) {
view.translatesAutoresizingMaskIntoConstraints = false
if view.superview != self { addSubview(view) }
var constraints: [NSLayoutConstraint] = []
if edges.contains(.top) {
let top: NSLayoutConstraint
if let guide = guide {
top = view.topAnchor.constraint(equalTo: guide.topAnchor, constant: inset.top)
} else {
top = view.topAnchor.constraint(equalTo: topAnchor, constant: inset.top)
}
top.priority = priorities.top ?? (subviews.isEmpty ? .defaultHigh : .fittingSizeLevel)
constraints.append(top)
}
if edges.contains(.bottom) {
let btm: NSLayoutConstraint
if let guide = guide {
btm = guide.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: inset.bottom)
} else {
btm = bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: inset.bottom)
}
btm.priority = priorities.bottom ?? .fittingSizeLevel
constraints.append(btm)
}
if edges.contains(.left) {
let leading: NSLayoutConstraint
if let guide = guide {
leading = view.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: inset.left)
} else {
leading = view.leadingAnchor.constraint(equalTo: leadingAnchor, constant: inset.left)
}
leading.priority = priorities.leading ?? .required
constraints.append(leading)
}
if edges.contains(.right) {
let trailing: NSLayoutConstraint
if let guide = guide {
trailing = guide.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: inset.right)
} else {
trailing = trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: inset.right)
}
trailing.priority = priorities.trailing ?? .required
constraints.append(trailing)
}
NSLayoutConstraint.activate(constraints)
}
func embedInCenter(view: UIView, offset: UIOffset = .zero, safeArea: Bool = false) {
view.translatesAutoresizingMaskIntoConstraints = false
if view.superview != self { addSubview(view) }
var constraints: [NSLayoutConstraint] = []
let parentXAnchor: NSLayoutXAxisAnchor
let parentYAnchor: NSLayoutYAxisAnchor
if safeArea {
if #available(iOS 11.0, tvOS 11.0, *) {
parentXAnchor = safeAreaLayoutGuide.centerXAnchor
parentYAnchor = safeAreaLayoutGuide.centerYAnchor
} else {
parentXAnchor = centerXAnchor
parentYAnchor = centerYAnchor
}
} else {
parentXAnchor = centerXAnchor
parentYAnchor = centerYAnchor
}
if offset.horizontal != .greatestFiniteMagnitude {
constraints.append(view.centerXAnchor.constraint(equalTo: parentXAnchor, constant: offset.horizontal))
}
if offset.vertical != .greatestFiniteMagnitude {
constraints.append(view.centerYAnchor.constraint(equalTo: parentYAnchor, constant: offset.vertical))
}
NSLayoutConstraint.activate(constraints)
}
func setSize(to size: CGSize, containment: SizeContainment = .equal, priority: UILayoutPriority = .required) {
translatesAutoresizingMaskIntoConstraints = false
var constraints: [NSLayoutConstraint] = []
switch containment {
case .equal:
if size.width > 0 {
constraints.append(widthAnchor.constraint(equalToConstant: size.width))
}
if size.height > 0 {
constraints.append(heightAnchor.constraint(equalToConstant: size.height))
}
case .minimum:
if size.width > 0 {
constraints.append(widthAnchor.constraint(greaterThanOrEqualToConstant: size.width))
}
if size.height > 0 {
constraints.append(heightAnchor.constraint(greaterThanOrEqualToConstant: size.height))
}
case .maximum:
if size.width > 0 {
constraints.append(widthAnchor.constraint(lessThanOrEqualToConstant: size.width))
}
if size.height > 0 {
constraints.append(heightAnchor.constraint(lessThanOrEqualToConstant: size.height))
}
}
constraints.forEach { $0.priority = priority }
NSLayoutConstraint.activate(constraints)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment