Skip to content

Commit 631d353

Browse files
authored
Merge pull request #1 from formbound/release-1.1.0
Release 1.1.0
2 parents 8bc6adc + bfc4c43 commit 631d353

File tree

3 files changed

+106
-5
lines changed

3 files changed

+106
-5
lines changed

Sources/StateViewController.swift

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ open class StateViewController<State: Equatable>: UIViewController {
136136
return false // We completely manage forwarding of appearance methods ourselves.
137137
}
138138

139+
fileprivate var observers: [StateViewControllerObserver<State>] = []
140+
139141
// MARK: - View lifecycle
140142

141143
/// :nodoc:
@@ -504,6 +506,12 @@ fileprivate extension StateViewController {
504506
return false
505507
}
506508

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+
507515
// If we're transitioning between states, we need to abort and wait for the current state
508516
// transition to finish.
509517
guard isTransitioningBetweenStates == false else {
@@ -513,12 +521,8 @@ fileprivate extension StateViewController {
513521

514522
// Invoke callback method, indicating that we will change state
515523
willTransition(to: state, animated: animated)
524+
dispatchStateEvent(.willTransitionTo(nextState: state, animated: animated))
516525

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-
}
522526

523527
// Note that we're transitioning from a state
524528
transitioningFromState = state
@@ -608,6 +612,7 @@ fileprivate extension StateViewController {
608612
transitioningFromState = nil
609613

610614
// Notify that we're finished transitioning
615+
dispatchStateEvent(.didTransitionFrom(previousState: fromState, animated: animated))
611616
didTransition(from: fromState, animated: animated)
612617

613618
// If we still need another state, let's transition to it immediately.
@@ -731,6 +736,8 @@ fileprivate extension StateViewController {
731736

732737
func updateHierarchy(of viewControllers: [UIViewController]) {
733738

739+
let previousSubviews = contentViewControllerContainerView.subviews
740+
734741
for (index, viewController) in viewControllers.enumerated() {
735742
viewController.view.translatesAutoresizingMaskIntoConstraints = true
736743
viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
@@ -740,6 +747,14 @@ fileprivate extension StateViewController {
740747
contentViewControllerContainerView.insertSubview(viewController.view, at: index)
741748
}
742749

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+
743758
// Tell everyone we're updating the view hierarchy
744759
NotificationCenter.default.post(name: .stateViewControllerDidChangeViewHierarchy, object: self)
745760

@@ -787,6 +802,7 @@ fileprivate extension StateViewController {
787802
// If we are, appearance methods will be forwarded at a later time
788803
if isInAppearanceTransition == false {
789804
contentViewControllerWillAppear(viewController, animated: animated)
805+
dispatchStateEvent(.contentWillAppear(viewController))
790806
viewController.beginAppearanceTransition(true, animated: animated)
791807
}
792808

@@ -802,6 +818,7 @@ fileprivate extension StateViewController {
802818
// If we're not in an appearance transition, forward appearance methods.
803819
// If we are, appearance methods will be forwarded at a later time
804820
if isInAppearanceTransition == false {
821+
dispatchStateEvent(.contentDidAppear(viewController))
805822
viewController.endAppearanceTransition()
806823
contentViewControllerDidAppear(viewController, animated: animated)
807824
}
@@ -822,6 +839,7 @@ fileprivate extension StateViewController {
822839
// If we are, appearance methods will be forwarded at a later time
823840
if isInAppearanceTransition == false {
824841
contentViewControllerWillDisappear(viewController, animated: animated)
842+
dispatchStateEvent(.contentWillDisappear(viewController))
825843
viewController.beginAppearanceTransition(false, animated: animated)
826844
}
827845

@@ -840,6 +858,7 @@ fileprivate extension StateViewController {
840858
// If we're not in an appearance transition, forward appearance methods.
841859
// If we are, appearance methods will be forwarded at a later time
842860
if isInAppearanceTransition == false {
861+
dispatchStateEvent(.contentDidDisappear(viewController))
843862
viewController.endAppearanceTransition()
844863
contentViewControllerDidDisappear(viewController, animated: animated)
845864
}
@@ -848,3 +867,32 @@ fileprivate extension StateViewController {
848867
viewControllersBeingRemoved.remove(viewController)
849868
}
850869
}
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+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
public protocol AnyStateViewControllerObserver : AnyObject {
3+
func remove()
4+
}
5+
6+
public class StateViewControllerObserver<T: Equatable>: AnyStateViewControllerObserver {
7+
8+
public typealias EventHandler = (StateViewControllerObserver<T>.Event) -> Void
9+
10+
public enum Event {
11+
case willTransitionTo(nextState: T, animated: Bool)
12+
case didTransitionFrom(previousState: T?, animated: Bool)
13+
14+
case didChangeHierarhcy
15+
16+
case contentWillAppear(UIViewController)
17+
case contentDidAppear(UIViewController)
18+
19+
case contentWillDisappear(UIViewController)
20+
case contentDidDisappear(UIViewController)
21+
}
22+
23+
private weak var stateViewController: StateViewController<T>?
24+
25+
private let eventHandler: EventHandler
26+
27+
internal init(stateViewController: StateViewController<T>, eventHandler: @escaping EventHandler) {
28+
self.eventHandler = eventHandler
29+
self.stateViewController = stateViewController
30+
}
31+
32+
public func remove() {
33+
stateViewController?.removeStateObserver(self)
34+
}
35+
36+
internal func invoke(with event: Event) {
37+
eventHandler(event)
38+
}
39+
}
40+
41+
extension StateViewControllerObserver: Equatable {
42+
43+
public static func == (lhs: StateViewControllerObserver, rhs: StateViewControllerObserver) -> Bool {
44+
return lhs === rhs
45+
}
46+
}
47+
48+
49+

StateViewController.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
6325167D224E221A00235B46 /* StateViewControllerObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6325167C224E221A00235B46 /* StateViewControllerObserver.swift */; };
1011
B76AA3C6212052FA00020277 /* StateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B76AA3C4212052FA00020277 /* StateViewController.swift */; };
1112
B76AA3C7212052FA00020277 /* StateViewControllerTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B76AA3C5212052FA00020277 /* StateViewControllerTransitionCoordinator.swift */; };
1213
B76AA3CB2120531600020277 /* StateViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = B76AA3C92120531600020277 /* StateViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
1314
B76AA3DA212053D800020277 /* StateViewControllerTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = B76AA3D9212053D800020277 /* StateViewControllerTransitioning.swift */; };
1415
/* End PBXBuildFile section */
1516

1617
/* Begin PBXFileReference section */
18+
6325167C224E221A00235B46 /* StateViewControllerObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateViewControllerObserver.swift; sourceTree = "<group>"; };
1719
B76AA3B7212052B500020277 /* StateViewController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = StateViewController.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1820
B76AA3C4212052FA00020277 /* StateViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateViewController.swift; sourceTree = "<group>"; };
1921
B76AA3C5212052FA00020277 /* StateViewControllerTransitionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateViewControllerTransitionCoordinator.swift; sourceTree = "<group>"; };
@@ -63,6 +65,7 @@
6365
isa = PBXGroup;
6466
children = (
6567
B76AA3C4212052FA00020277 /* StateViewController.swift */,
68+
6325167C224E221A00235B46 /* StateViewControllerObserver.swift */,
6669
B76AA3C5212052FA00020277 /* StateViewControllerTransitionCoordinator.swift */,
6770
B76AA3D9212053D800020277 /* StateViewControllerTransitioning.swift */,
6871
);
@@ -165,6 +168,7 @@
165168
isa = PBXSourcesBuildPhase;
166169
buildActionMask = 2147483647;
167170
files = (
171+
6325167D224E221A00235B46 /* StateViewControllerObserver.swift in Sources */,
168172
B76AA3C6212052FA00020277 /* StateViewController.swift in Sources */,
169173
B76AA3C7212052FA00020277 /* StateViewControllerTransitionCoordinator.swift in Sources */,
170174
B76AA3DA212053D800020277 /* StateViewControllerTransitioning.swift in Sources */,

0 commit comments

Comments
 (0)