Last active
August 18, 2019 15:39
-
-
Save amosavian/c7e16dc39bf9bd31fa2010284223ff55 to your computer and use it in GitHub Desktop.
Helper methods to ease usage of auto-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
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