Skip to content

Instantly share code, notes, and snippets.

@asobolevsky
Last active June 6, 2020 11:08
Show Gist options
  • Save asobolevsky/e56abfe65b877f417cf3f21d28b6e028 to your computer and use it in GitHub Desktop.
Save asobolevsky/e56abfe65b877f417cf3f21d28b6e028 to your computer and use it in GitHub Desktop.
import UIKit
typealias BehaviorClosure = (_ viewController: UIViewController) -> ()
protocol ViewControllerLifecycleBehavior {
func afterLoading(_ viewController: UIViewController)
func beforeAppearing(_ viewController: UIViewController)
func afterAppearing(_ viewController: UIViewController)
func beforeDisappearing(_ viewController: UIViewController)
func afterDisappearing(_ viewController: UIViewController)
func beforeLayingOutSubviews(_ viewController: UIViewController)
func afterLayingOutSubviews(_ viewController: UIViewController)
}
extension ViewControllerLifecycleBehavior {
func afterLoading(_ viewController: UIViewController) {}
func beforeAppearing(_ viewController: UIViewController) {}
func afterAppearing(_ viewController: UIViewController) {}
func beforeDisappearing(_ viewController: UIViewController) {}
func afterDisappearing(_ viewController: UIViewController) {}
func beforeLayingOutSubviews(_ viewController: UIViewController) {}
func afterLayingOutSubviews(_ viewController: UIViewController) {}
}
func createAnonimousBehavior(afterLoading: BehaviorClosure? = nil,
beforeAppearing: BehaviorClosure? = nil,
afterAppearing: BehaviorClosure? = nil,
beforeDisappearing: BehaviorClosure? = nil,
afterDisappearing: BehaviorClosure? = nil,
beforeLayingOutSubviews: BehaviorClosure? = nil,
afterLayingOutSubviews: BehaviorClosure? = nil) -> ViewControllerLifecycleBehavior {
class AnonimousBehavior: ViewControllerLifecycleBehavior {
private var afterLoadingClosure: BehaviorClosure?
private var beforeAppearingClosure: BehaviorClosure?
private var afterAppearingClosure: BehaviorClosure?
private var beforeDisappearingClosure: BehaviorClosure?
private var afterDisappearingClosure: BehaviorClosure?
private var beforeLayingOutSubviewsClosure: BehaviorClosure?
private var afterLayingOutSubviewsClosure: BehaviorClosure?
init(afterLoading: BehaviorClosure? = nil,
beforeAppearing: BehaviorClosure? = nil,
afterAppearing: BehaviorClosure? = nil,
beforeDisappearing: BehaviorClosure? = nil,
afterDisappearing: BehaviorClosure? = nil,
beforeLayingOutSubviews: BehaviorClosure? = nil,
afterLayingOutSubviews: BehaviorClosure? = nil) {
self.afterLoadingClosure = afterLoading
self.beforeAppearingClosure = beforeAppearing
self.afterAppearingClosure = afterAppearing
self.beforeDisappearingClosure = beforeDisappearing
self.afterDisappearingClosure = afterDisappearing
self.beforeLayingOutSubviewsClosure = beforeLayingOutSubviews
self.afterLayingOutSubviewsClosure = afterLayingOutSubviews
}
func afterLoading(_ viewController: UIViewController) {
afterLoadingClosure?(viewController)
}
func beforeAppearing(_ viewController: UIViewController) {
beforeAppearingClosure?(viewController)
}
func afterAppearing(_ viewController: UIViewController) {
afterAppearingClosure?(viewController)
}
func beforeDisappearing(_ viewController: UIViewController) {
beforeDisappearingClosure?(viewController)
}
func afterDisappearing(_ viewController: UIViewController) {
afterDisappearingClosure?(viewController)
}
func beforeLayingOutSubviews(_ viewController: UIViewController) {
beforeLayingOutSubviewsClosure?(viewController)
}
func afterLayingOutSubviews(_ viewController: UIViewController) {
afterLayingOutSubviewsClosure?(viewController)
}
}
let behavior = AnonimousBehavior(afterLoading: afterLoading,
beforeAppearing: beforeAppearing,
afterAppearing: afterAppearing,
beforeDisappearing: beforeDisappearing,
afterDisappearing: afterDisappearing,
beforeLayingOutSubviews: beforeLayingOutSubviews,
afterLayingOutSubviews: afterLayingOutSubviews)
return behavior
}
extension UIViewController {
/*
Add behaviors to be hooked into this view controller’s lifecycle.
This method requires the view controller’s view to be loaded, so it’s best to call
in `viewDidLoad` to avoid it being loaded prematurely.
- parameter behaviors: Behaviors to be added.
*/
func addBehaviors(behaviors: [ViewControllerLifecycleBehavior]) {
let behaviorViewController = LifecycleBehaviorViewController(behaviors: behaviors)
addChild(behaviorViewController)
view.addSubview(behaviorViewController.view)
behaviorViewController.didMove(toParent: self)
}
private final class LifecycleBehaviorViewController: UIViewController {
private let behaviors: [ViewControllerLifecycleBehavior]
// MARK: - Initialization
init(behaviors: [ViewControllerLifecycleBehavior]) {
self.behaviors = behaviors
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - UIViewController
override func viewDidLoad() {
super.viewDidLoad()
view.isHidden = true
applyBehaviors { behavior, viewController in
behavior.afterLoading(viewController)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
applyBehaviors { behavior, viewController in
behavior.beforeAppearing(viewController)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
applyBehaviors { behavior, viewController in
behavior.afterAppearing(viewController)
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
applyBehaviors { behavior, viewController in
behavior.beforeDisappearing(viewController)
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
applyBehaviors { behavior, viewController in
behavior.afterDisappearing(viewController)
}
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
applyBehaviors { behavior, viewController in
behavior.beforeLayingOutSubviews(viewController)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
applyBehaviors { behavior, viewController in
behavior.afterLayingOutSubviews(viewController)
}
}
// MARK: - Private
private func applyBehaviors(body: (_ behavior: ViewControllerLifecycleBehavior, _ viewController: UIViewController) -> ()) {
guard let parentViewController = parent else { return }
for behavior in behaviors {
body(behavior, parentViewController)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment