diff --git a/Demo/Modules/ImagePicker/ImagePickerFlowController.swift b/Demo/Modules/ImagePicker/ImagePickerFlowController.swift index fc1a2f7..5a09037 100644 --- a/Demo/Modules/ImagePicker/ImagePickerFlowController.swift +++ b/Demo/Modules/ImagePicker/ImagePickerFlowController.swift @@ -37,7 +37,7 @@ class ImagePickerFlowController: FlowController { }) alertCtrl.addAction(UIAlertAction(title: "Cancel", style: .cancel) { [unowned self] _ in - self.removeFromSuperFlowController() + self.removeFromParent() }) return alertCtrl @@ -63,7 +63,7 @@ class ImagePickerFlowController: FlowController { extension ImagePickerFlowController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) { - self.removeFromSuperFlowController() + self.removeFromParent() } } @@ -71,7 +71,7 @@ extension ImagePickerFlowController: UIImagePickerControllerDelegate, UINavigati if let pickedImage = info[.originalImage] as? UIImage { resultCompletion.reportResult(result: pickedImage) picker.dismiss(animated: true) { - self.removeFromSuperFlowController() + self.removeFromParent() } } } diff --git a/Demo/Modules/Main/MainFlowController.swift b/Demo/Modules/Main/MainFlowController.swift index 7c340cf..ec5c002 100644 --- a/Demo/Modules/Main/MainFlowController.swift +++ b/Demo/Modules/Main/MainFlowController.swift @@ -23,7 +23,7 @@ class MainFlowController: InitialFlowController { extension MainFlowController: MainFlowDelegate { func tutorialStartButtonPressed() { let tutorialFlowCtrl = TutorialFlowController() - add(subFlowController: tutorialFlowCtrl) + addChild(tutorialFlowCtrl) tutorialFlowCtrl.start(from: mainViewController!) } @@ -33,7 +33,7 @@ extension MainFlowController: MainFlowDelegate { } let imagePickerFlowCtrl = ImagePickerFlowController(resultCompletion: resultCompletion) - add(subFlowController: imagePickerFlowCtrl) + addChild(imagePickerFlowCtrl) imagePickerFlowCtrl.start(from: mainViewController!) } } diff --git a/Demo/Modules/Tutorial/TutorialFlowController.swift b/Demo/Modules/Tutorial/TutorialFlowController.swift index 291b621..96f2673 100644 --- a/Demo/Modules/Tutorial/TutorialFlowController.swift +++ b/Demo/Modules/Tutorial/TutorialFlowController.swift @@ -52,7 +52,7 @@ extension TutorialFlowController: Page2FlowDelegate { extension TutorialFlowController: Page3FlowDelegate { func completeButtonPressed() { navigationCtrl?.dismiss(animated: true) { - self.removeFromSuperFlowController() + self.removeFromParent() } } } diff --git a/Frameworks/Imperio/FlowController.swift b/Frameworks/Imperio/FlowController.swift index 6922b94..828fbe5 100644 --- a/Frameworks/Imperio/FlowController.swift +++ b/Frameworks/Imperio/FlowController.swift @@ -4,8 +4,10 @@ import UIKit /// A class to be subclassed by flow controllers. open class FlowController: NSObject { - var subFlowController: FlowController? - weak var superFlowController: FlowController? + private var subFlowController: FlowController? + + private(set) weak var parent: FlowController? + private(set) var children: [FlowController] = [] /// Starts the flow from a given view controller. /// @@ -13,24 +15,77 @@ open class FlowController: NSObject { /// - presentingViewController: The view controller to start the flow from. open func start(from presentingViewController: UIViewController) { /* for overriding purposes only */ } + /// Override this to perform logic before the flow controller will change its parent. + open func willMove(toParent: FlowController?) { /* for overriding purposes only */ } + + /// Override this to perform logic after the flow controller did change its parent. + open func didMove(toParent: FlowController?) { /* for overriding purposes only */ } + + /// Adds a flow controller to the children of this flow controller + /// + /// - Parameters: + /// - child: The flow controller to be added as child. + public func addChild(_ child: FlowController) { + precondition(child.parent == nil) + + child.willMove(toParent: self) + child.parent = self + children.append(child) + child.didMove(toParent: self) + } + + /// Moves the flow controller from its current parent to the new parent. + /// + /// - Parameters: + /// - toParent: The parent the flow controller will be moved to. + public func move(toParent: FlowController) { + removeFromParent() + toParent.addChild(self) + } + + /// Removes the flow controller from its parent. + public func removeFromParent() { + parent?.removeChildren(in: [self]) + } + + /// Remove all child flow controllers. + public func removeAllChildren() { + removeChildren(in: children) + } + + /// Removes all child flow controllers in `childrenToRemove`. + /// + /// - Parameters: + /// - childrenToRemove: The children which will be removed from the flow controller. + public func removeChildren(in childrenToRemove: [FlowController]) { + childrenToRemove.forEach(removeChild(_:)) + } + + /// Removes the given `child` from the flow controller. + /// + /// - Parameters: + /// - child: The child which will be removed from the flow controller. + public func removeChild(_ child: FlowController) { + precondition(child.parent == self) + + child.willMove(toParent: nil) + child.parent = nil + children.removeAll { $0 === child } + child.didMove(toParent: nil) + } + /// Adds a sub flow controller to the existing one. /// /// - Parameters: /// - subFlowController: The sub flow controller to be added. + @available(*, deprecated, message: "`add(subFlowController:)` is deprecated use `addChild(_:)` instead!") public func add(subFlowController: FlowController) { - // Clean up - self.subFlowController?.removeFromSuperFlowController() - subFlowController.removeFromSuperFlowController() - - // Store new - self.subFlowController = subFlowController - subFlowController.superFlowController = self + addChild(subFlowController) } /// Removes this flow controller from its super flow controller. + @available(*, deprecated, message: "`removeFromSuperFlowController` is deprecated use `removeFromParent` instead!") public func removeFromSuperFlowController() { - subFlowController?.removeFromSuperFlowController() - superFlowController?.subFlowController = nil - superFlowController = nil + removeFromParent() } } diff --git a/Frameworks/Imperio/InitialFlowController.swift b/Frameworks/Imperio/InitialFlowController.swift index 4a2ceea..d5bc299 100644 --- a/Frameworks/Imperio/InitialFlowController.swift +++ b/Frameworks/Imperio/InitialFlowController.swift @@ -9,4 +9,9 @@ open class InitialFlowController: FlowController { /// - Paramters: /// - window: The window to present the flow from. open func start(from window: UIWindow) { /* needs to be overridden */ } + + @available(*, unavailable, message: "InitialFlowController can only be started from a UIWindow!") + override open func start(from presentingViewController: UIViewController) { + fatalError("InitialFlowController can only be started from a UIWindow!") + } } diff --git a/README.md b/README.md index 05e5abd..59debbd 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ class TutorialFlowController: FlowController { ### The `start(from:)` method -Each coordinator subclass needs to override at least the `start` method which opens the initial view controller of the screen flow. For example: +Each flow controller subclass needs to override at least the `start` method which opens the initial view controller of the screen flow. For example: ``` Swift import Imperio @@ -98,7 +98,7 @@ Now, whenever you might want to start your screen flow from within another flow ``` Swift func tutorialStartButtonPressed() { let tutorialFlowCtrl = TutorialFlowController() - add(subFlowController: tutorialFlowCtrl) + addChild(tutorialFlowCtrl) tutorialFlowCtrl.start(from: someViewController!) } ``` @@ -108,7 +108,7 @@ Please note that this works pretty much like adding a subview to a `UIView` with ``` Swift func completeButtonPressed() { navigationCtrl?.dismiss(animated: true) { - self.removeFromSuperFlowController() + self.removeFromParent() } } ``` @@ -289,7 +289,7 @@ func imagePickerStartButtonPressed() { } let imagePickerFlowCtrl = ImagePickerFlowController(resultCompletion: resultCompletion) - add(subFlowController: imagePickerFlowCtrl) + addChild(imagePickerFlowCtrl) imagePickerFlowCtrl.start(from: mainViewController!) } ``` @@ -417,7 +417,7 @@ class ImagePickerFlowController: FlowController { }) alertCtrl.addAction(UIAlertAction(title: "Cancel", style: .cancel) { [unowned self] _ in - self.removeFromSuperFlowController() + self.removeFromParent() }) return alertCtrl @@ -451,7 +451,7 @@ class ImagePickerFlowController: FlowController { extension ImagePickerFlowController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) { - self.removeFromSuperFlowController() + self.removeFromParent() } } @@ -459,7 +459,7 @@ extension ImagePickerFlowController: UIImagePickerControllerDelegate, UINavigati if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage { resultCompletion.reportResult(result: pickedImage) picker.dismiss(animated: true) { - self.removeFromSuperFlowController() + self.removeFromParent() } } }