Skip to content

Instantly share code, notes, and snippets.

@asobolevsky
Created June 6, 2020 11:12
Show Gist options
  • Save asobolevsky/499f351f039169edc008d5b81356424d to your computer and use it in GitHub Desktop.
Save asobolevsky/499f351f039169edc008d5b81356424d to your computer and use it in GitHub Desktop.
import UIKit
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) {}
}
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)
}
}
}
}
typealias BehaviorClosure = (_ viewController: UIViewController) -> ()
// MARK: - ViewControllerLifecycle
enum ViewControllerLifecycle : CaseIterable {
typealias AllCases = [ViewControllerLifecycle]
case afterLoading(BehaviorClosure?)
case afterAppearing(BehaviorClosure?)
case beforeAppearing(BehaviorClosure?)
case beforeDisappearing(BehaviorClosure?)
case afterDisappearing(BehaviorClosure?)
case beforeLayingOutSubviews(BehaviorClosure?)
case afterLayingOutSubviews(BehaviorClosure?)
static var allCases: [ViewControllerLifecycle] {
return [afterLoading(nil),
beforeAppearing(nil),
beforeDisappearing(nil),
beforeLayingOutSubviews(nil),
afterLayingOutSubviews(nil)]
}
}
// MARK: - createAnonimousBehavior
func createAnonimousBehavior(with lifecycle: [ViewControllerLifecycle]) -> ViewControllerLifecycleBehavior {
class AnonimousBehavior: ViewControllerLifecycleBehavior {
private var lifeCycle: [ViewControllerLifecycle]
init(lifeCycle:[ViewControllerLifecycle]) {
self.lifeCycle = lifeCycle
}
func afterLoading(_ viewController: UIViewController) {
lifeCycle.forEach {
guard case .afterLoading(let closure) = $0 else { return }
closure?(viewController)
}
}
func afterAppearing(_ viewController: UIViewController) {
lifeCycle.forEach {
guard case .afterAppearing(let closure) = $0 else { return }
closure?(viewController)
}
}
func beforeAppearing(_ viewController: UIViewController) {
lifeCycle.forEach {
guard case .beforeAppearing(let closure) = $0 else { return }
closure?(viewController)
}
}
func beforeDisappearing(_ viewController: UIViewController) {
lifeCycle.forEach {
guard case .beforeDisappearing(let closure) = $0 else { return }
closure?(viewController)
}
}
func afterDisappearing(_ viewController: UIViewController) {
lifeCycle.forEach {
guard case .afterDisappearing(let closure) = $0 else { return }
closure?(viewController)
}
}
func beforeLayingOutSubviews(_ viewController: UIViewController) {
lifeCycle.forEach {
guard case .beforeLayingOutSubviews(let closure) = $0 else { return }
closure?(viewController)
}
}
func afterLayingOutSubviews(_ viewController: UIViewController) {
lifeCycle.forEach {
guard case .afterLayingOutSubviews(let closure) = $0 else { return }
closure?(viewController)
}
}
}
let behavior = AnonimousBehavior(lifeCycle: ViewControllerLifecycle.allCases)
return behavior
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment