StateViewController
open class StateViewController<State> : UIViewController where State : Equatable
A container view controller that manages the appearance of one or more child view controller for any given state.
Overview
This class is designed to make stateful view controller programming easier. Typically in iOS development,
views representing multiple states are managed in one single view controller, leading to large view controller
classes that quickly become hard to work with and overlook at a glance. For instance, a view controller may
display an activity indicator while a network call is performed, leaving the view controller to have to directly
manipulate view hierarhy for each state. Furthermore, the state of a view controller tends to be represented by
conditions that are hard to synchronize, easily becoming a source of bugs and unexpected behavior.
With StateViewController
each state can be represented by one or more view controllers.
This allows you to composite view controllers in self-contained classes resulting in smaller view
controllers and better ability modularize your view controller code, with clear separation between states.
Subclassing notes
You must subclass StateViewController
and define a state for the view controller you are creating.
enum MyViewControllerState: Equatable {
case loading
case ready
}
Note: Your state must conform to Equatable
in order for StateViewController
to distinguish between states.
Override loadAppearanceState()
to determine which state is being represented each time this view controller
is appearing on screen. In this method is appropriate to query your model layer to determine whether data needed
for a certain state is available or not.
override func loadAppearanceState() -> MyViewControllerState {
if model.isDataAvailable {
return .ready
} else {
return .loading
}
}
To determine which content view controllers represent a particular state, you must override
children(for:)
.
override func children(for state: MyViewControllerState) -> [UIViewController] {
switch state {
case .loading:
return [ActivityIndicatorViewController()]
case .empty:
return [myChild]
}
}
Callback methods are overridable, notifying you when a state transition is being performed, and what child view controllers are being presented as a result of a state transition.
Using willTransition(to:animated:)
you should prepare view controller representing the state being transition to
with the appropriate data.
override func willTransition(to state: MyViewControllerState, animated: Bool) {
switch state {
case .ready:
myChild.content = myLoadedContent
case .loading:
break
}
}
Overriding didTransition(to:animated:)
is an appropriate place to invoke methods that eventually results in
a state transition being requested using setNeedsTransition(to:animated:)
, as it ensures that any previous state
transitions has been fully completed.
override func didTransition(from previousState: MyViewControllerState?, animated: Bool) {
switch state {
case .ready:
break
case .loading:
model.loadData { result in
self.myLoadedContent = result
self.setNeedsTransition(to: .ready, animated: true)
}
}
}
You may also override loadChildContainerView()
to provide a custom container view for your
content view controllers, allowing you to manipulate the view hierarchy above and below the content view
controller container view.
Animating state transitions
By default, no animations are performed between states. To enable animations, you have three options:
- Set
defaultStateTransitioningCoordinator
- Override
stateTransitionCoordinator(for:)
in yourStateViewController
subclasses - Conform view controllers contained in
StateViewController
toStateViewControllerTransitioning
.
-
Indicates whether the view controller currently is transitioning between states.
Declaration
Swift
public var isTransitioningBetweenStates: Bool { get }
-
Indicates the current state, or invokes
loadAppearanceState()
is a current state transition has not yet began.Declaration
Swift
public var currentState: State { get }
-
Undocumented
Declaration
Swift
public var hasDeterminedState: Bool { get }
-
Undocumented
Declaration
Swift
open func loadAppearanceState() -> State
-
Notifies the state view controller that a new state is needed. As soon as the state view controller is ready to change state, a state transition will begin.
Note
Multiple calls to this method will result in the last state provided being transitioned to.
Declaration
Swift
public func setNeedsStateTransition(to state: State, animated: Bool)
Parameters
state
State to transition to.
animated
Whether to animate the state transition.
-
Returns an array of content view controllers representing a state. The order of the view controllers matter – first in array will be placed first in the container views view hierarchy.
Declaration
Swift
open func children(for state: State) -> [UIViewController]
Parameters
state
State being represented
Return Value
An array of view controllers
-
Container view placed directly in the
StateViewController
s view. Content view controllers are placed inside this view, edge to edge.Important
You should not directly manipulate the view hierarchy of this viewDeclaration
Swift
public var childContainerView: UIView { get }
-
Note
This method is only called once.
Declaration
Swift
open func loadChildContainerView() -> UIView
Return Value
A
UIView
if not overridden.
-
Notifies the view controller that a state transition is to be performed.
Use this method to prepare view controller representing the given state for display.
Declaration
Swift
open func willTransition(to nextState: State, animated: Bool)
Parameters
nextState
State that will be transitioned to.
animated
Indicates whether the outstanding transition will be animated.
-
Notifies the view controller that it has finished transitioning to a new state.
As this method guarantees that a state transition has fully completed, this function is a good place to call
setNeedsTransition(to:animated:)
, or methods that eventually (asynchronously or synchronously) calls that method.Declaration
Swift
open func didTransition(from previousState: State?, animated: Bool)
Parameters
state
State
animated
If true, the state transition was animated
-
Notifies the view controller that a content view controller will appear.
Declaration
Swift
open func childWillAppear(_ child: UIViewController, animated: Bool)
Parameters
viewController
View controller appearing.
animated
Indicates whether the appearance is animated.
-
Notifies the view controller that a content view controller did appear.
This method is well suited as a function to add targets and listeners that should only be present when the provided content view controller is on screen.
Declaration
Swift
open func childDidAppear(_ child: UIViewController, animated: Bool)
Parameters
viewController
View controller appeared.
animated
Indicates whether the apperance was animated.
-
Notifies the view controller that a content view controller will disappear. This method is well suited as a fucntion to remove targets and listerners that should only be present when the content view controller is on screen.
Declaration
Swift
open func childWillDisappear(_ child: UIViewController, animated: Bool)
Parameters
viewController
View controller disappearing.
animated
Indicates whether the disappearance is animated.
-
Notifies the view controller that a content view controller did disappear.
Declaration
Swift
open func childDidDisappear(_ child: UIViewController, animated: Bool)
Parameters
viewController
Content view controller disappearad.
animated
Indicates whether the disappearance was animated.
-
Returns an optional
StateViewControllerTransitionCoordinator
, used to animate transitions between states.If the provided view controller conforms to
StateViewControllerTransitioning
, and this method returns non-nil, the returnedStateViewControllerTransitionCoordinator
is used to animate the state transition of the provided view controller.Declaration
Swift
open func stateTransitionCoordinator( for child: UIViewController) -> StateViewControllerTransitionCoordinator?
Parameters
child
Content view controller.
Return Value
A
StateViewControllerTransitionCoordinator
, ornil
. -
Undocumented
Declaration
Swift
func addStateObserver( _ eventHandler: @escaping StateViewControllerObserver<State>.EventHandler ) -> StateViewControllerObserver<State>
-
Undocumented
Declaration
Swift
func removeStateObserver(_ observerToRemove: StateViewControllerObserver<State>)