precedencegroup HierarchyPrecedence { lowerThan: AssignmentPrecedence associativity: left } infix operator |--: HierarchyPrecedence public protocol UIViewHierarchyProvider { associatedtype View: UIView var view: View { get } var viewController: UIViewController? { get } func append(_ view: UIView) } extension UIViewHierarchyProvider { @discardableResult public static func |-- (lpred: Self, rpred: V) -> Self { lpred.append(rpred) return lpred } @discardableResult public static func |-- (lpred: Self, rpred: V?) -> Self { guard let view = rpred else { return lpred } lpred.append(view) return lpred } @discardableResult public static func |-- (lpred: Self, rpred: [V]) -> Self { for view in rpred { lpred.append(view) } return lpred } @discardableResult public static func |-- (lpred: Self, rpred: UIViewController) -> Self { guard let parent = lpred.viewController else { return lpred } parent.addChild(rpred) lpred.append(rpred.view) rpred.didMove(toParent: parent) return lpred } } public class UIViewHierarchy: UIViewHierarchyProvider { public let view:UIView public let viewController: UIViewController? init(_ view:UIView, viewController: UIViewController?) { self.view = view self.viewController = viewController } public func append(_ view: UIView) { self.view.addSubview(view) } } public class UIStackViewHierarchy: UIViewHierarchyProvider { public let view:UIStackView public let viewController: UIViewController? init(_ view:UIStackView, viewController: UIViewController?) { self.view = view self.viewController = viewController } public func append(_ view: UIView) { self.view.addArrangedSubview(view) } } extension UIView { @discardableResult public func hierarchy(in viewController: UIViewController? = nil, _ processer:(UIViewHierarchy)->Void) -> Self { let h = UIViewHierarchy(self, viewController: viewController) processer(h) return self } } extension UIStackView { @discardableResult public func arrangedHierarchy(in viewController: UIViewController? = nil, skipCleaning: Bool = false, _ processer:(UIStackViewHierarchy)->Void) -> Self { if !skipCleaning { while let view = self.subviews.first { view.removeFromSuperview() } } let h = UIStackViewHierarchy(self, viewController: viewController) processer(h) return self } } extension UIViewController { @discardableResult public func hierarchy(_ processer:(UIViewHierarchy)->Void) -> Self { let h = UIViewHierarchy(self.view, viewController: self) processer(h) return self } }