@@ -136,6 +136,8 @@ open class StateViewController<State: Equatable>: UIViewController {
136
136
return false // We completely manage forwarding of appearance methods ourselves.
137
137
}
138
138
139
+ fileprivate var observers : [ StateViewControllerObserver < State > ] = [ ]
140
+
139
141
// MARK: - View lifecycle
140
142
141
143
/// :nodoc:
@@ -504,6 +506,12 @@ fileprivate extension StateViewController {
504
506
return false
505
507
}
506
508
509
+ // We may not have made any changes to content view controllers, even though we have changed the state.
510
+ // Therefore, we must be prepare to end the state transition immediately.
511
+ defer {
512
+ endStateTransitionIfNeeded ( animated: animated)
513
+ }
514
+
507
515
// If we're transitioning between states, we need to abort and wait for the current state
508
516
// transition to finish.
509
517
guard isTransitioningBetweenStates == false else {
@@ -513,12 +521,8 @@ fileprivate extension StateViewController {
513
521
514
522
// Invoke callback method, indicating that we will change state
515
523
willTransition ( to: state, animated: animated)
524
+ dispatchStateEvent ( . willTransitionTo( nextState: state, animated: animated) )
516
525
517
- // We may not have made any changes to content view controllers, even though we have changed the state.
518
- // Therefore, we must be prepare to end the state transition immediately.
519
- defer {
520
- endStateTransitionIfNeeded ( animated: animated)
521
- }
522
526
523
527
// Note that we're transitioning from a state
524
528
transitioningFromState = state
@@ -608,6 +612,7 @@ fileprivate extension StateViewController {
608
612
transitioningFromState = nil
609
613
610
614
// Notify that we're finished transitioning
615
+ dispatchStateEvent ( . didTransitionFrom( previousState: fromState, animated: animated) )
611
616
didTransition ( from: fromState, animated: animated)
612
617
613
618
// If we still need another state, let's transition to it immediately.
@@ -731,6 +736,8 @@ fileprivate extension StateViewController {
731
736
732
737
func updateHierarchy( of viewControllers: [ UIViewController ] ) {
733
738
739
+ let previousSubviews = contentViewControllerContainerView. subviews
740
+
734
741
for (index, viewController) in viewControllers. enumerated ( ) {
735
742
viewController. view. translatesAutoresizingMaskIntoConstraints = true
736
743
viewController. view. autoresizingMask = [ . flexibleWidth, . flexibleHeight]
@@ -740,6 +747,14 @@ fileprivate extension StateViewController {
740
747
contentViewControllerContainerView. insertSubview ( viewController. view, at: index)
741
748
}
742
749
750
+ // Only proceed if the previous subviews of the content view controller container view
751
+ // differ from the new subviews
752
+ guard previousSubviews. elementsEqual ( contentViewControllerContainerView. subviews) == false else {
753
+ return
754
+ }
755
+
756
+ dispatchStateEvent ( . didChangeHierarhcy)
757
+
743
758
// Tell everyone we're updating the view hierarchy
744
759
NotificationCenter . default. post ( name: . stateViewControllerDidChangeViewHierarchy, object: self )
745
760
@@ -787,6 +802,7 @@ fileprivate extension StateViewController {
787
802
// If we are, appearance methods will be forwarded at a later time
788
803
if isInAppearanceTransition == false {
789
804
contentViewControllerWillAppear ( viewController, animated: animated)
805
+ dispatchStateEvent ( . contentWillAppear( viewController) )
790
806
viewController. beginAppearanceTransition ( true , animated: animated)
791
807
}
792
808
@@ -802,6 +818,7 @@ fileprivate extension StateViewController {
802
818
// If we're not in an appearance transition, forward appearance methods.
803
819
// If we are, appearance methods will be forwarded at a later time
804
820
if isInAppearanceTransition == false {
821
+ dispatchStateEvent ( . contentDidAppear( viewController) )
805
822
viewController. endAppearanceTransition ( )
806
823
contentViewControllerDidAppear ( viewController, animated: animated)
807
824
}
@@ -822,6 +839,7 @@ fileprivate extension StateViewController {
822
839
// If we are, appearance methods will be forwarded at a later time
823
840
if isInAppearanceTransition == false {
824
841
contentViewControllerWillDisappear ( viewController, animated: animated)
842
+ dispatchStateEvent ( . contentWillDisappear( viewController) )
825
843
viewController. beginAppearanceTransition ( false , animated: animated)
826
844
}
827
845
@@ -840,6 +858,7 @@ fileprivate extension StateViewController {
840
858
// If we're not in an appearance transition, forward appearance methods.
841
859
// If we are, appearance methods will be forwarded at a later time
842
860
if isInAppearanceTransition == false {
861
+ dispatchStateEvent ( . contentDidDisappear( viewController) )
843
862
viewController. endAppearanceTransition ( )
844
863
contentViewControllerDidDisappear ( viewController, animated: animated)
845
864
}
@@ -848,3 +867,32 @@ fileprivate extension StateViewController {
848
867
viewControllersBeingRemoved. remove ( viewController)
849
868
}
850
869
}
870
+
871
+
872
+ public extension StateViewController {
873
+
874
+
875
+ func addStateObserver(
876
+ _ eventHandler: @escaping StateViewControllerObserver < State > . EventHandler
877
+ ) -> StateViewControllerObserver < State > {
878
+
879
+ let observer = StateViewControllerObserver ( stateViewController: self , eventHandler: eventHandler)
880
+
881
+ observers. append ( observer)
882
+
883
+ return observer
884
+ }
885
+
886
+ func removeStateObserver( _ observerToRemove: StateViewControllerObserver < State > ) {
887
+ observers = observers. filter { observer in
888
+ observer != observerToRemove
889
+ }
890
+ }
891
+
892
+ fileprivate func dispatchStateEvent( _ event: StateViewControllerObserver < State > . Event ) {
893
+ for observer in observers {
894
+ observer. invoke ( with: event)
895
+ }
896
+ }
897
+
898
+ }
0 commit comments