@@ -14,7 +14,6 @@ public class UIPilot<T: Equatable>: ObservableObject {
14
14
var onPush : ( ( T ) -> Void ) ?
15
15
var onPopLast : ( ( Int , Bool ) -> Void ) ?
16
16
17
-
18
17
public init ( initial: T ? = nil , debug: Bool = false ) {
19
18
logger = debug ? DebugLog ( ) : EmptyLog ( )
20
19
logger. log ( " UIPilot - Pilot Initialized. " )
@@ -68,46 +67,62 @@ public class UIPilot<T: Equatable>: ObservableObject {
68
67
logger. log ( " UIPilot - \( popped) route popped by system " )
69
68
}
70
69
}
71
-
72
70
}
73
71
74
72
public struct UIPilotHost < T: Equatable , Screen: View > : View {
75
-
76
- @ObservedObject
77
- var pilot : UIPilot < T >
73
+
74
+ @StateObject
75
+ var navigationStyle = NavigationStyle ( )
76
+
77
+ let pilot : UIPilot < T >
78
78
@ViewBuilder
79
- var routeMap : ( T ) -> Screen
79
+ let routeMap : ( T ) -> Screen
80
80
81
81
public init ( _ pilot: UIPilot < T > , @ViewBuilder _ routeMap: @escaping ( T ) -> Screen ) {
82
82
self . pilot = pilot
83
83
self . routeMap = routeMap
84
84
}
85
85
86
86
public var body : some View {
87
- NavigationControllerHost ( uipilot: pilot, routeMap: routeMap)
88
- . environmentObject ( pilot)
87
+ NavigationControllerHost (
88
+ navTitle: navigationStyle. title,
89
+ navHidden: navigationStyle. isHidden,
90
+ uipilot: pilot,
91
+ routeMap: routeMap
92
+ )
93
+ . environmentObject ( pilot)
94
+ . environment ( \. uipNavigationStyle, navigationStyle)
95
+
89
96
}
90
97
}
91
98
92
99
struct NavigationControllerHost < T: Equatable , Screen: View > : UIViewControllerRepresentable {
100
+
101
+ let navTitle : String
102
+ let navHidden : Bool
103
+
93
104
let uipilot : UIPilot < T >
105
+
94
106
@ViewBuilder
95
- let routeMap : ( T ) -> Screen
107
+ var routeMap : ( T ) -> Screen
96
108
97
109
func makeUIViewController( context: Context ) -> UINavigationController {
98
110
let navigation = PopAwareUINavigationController ( )
111
+
99
112
navigation. popHandler = {
100
113
uipilot. onSystemPop ( )
101
114
}
102
115
103
116
for path in uipilot. routes {
104
117
navigation. pushViewController (
105
- UIHostingController ( rootView: routeMap ( path) ) , animated: false )
118
+ UIHostingController ( rootView: routeMap ( path) ) , animated: true
119
+ )
106
120
}
107
121
108
122
uipilot. onPush = { route in
109
123
navigation. pushViewController (
110
- UIHostingController ( rootView: routeMap ( route) ) , animated: true )
124
+ UIHostingController ( rootView: routeMap ( route) ) , animated: true
125
+ )
111
126
}
112
127
113
128
uipilot. onPopLast = { numToPop, animated in
@@ -118,25 +133,172 @@ struct NavigationControllerHost<T: Equatable, Screen: View>: UIViewControllerRep
118
133
navigation. popToViewController ( popTo, animated: animated)
119
134
}
120
135
}
121
-
136
+
122
137
return navigation
123
138
}
124
139
125
- func updateUIViewController( _ uiViewController: UINavigationController , context: Context ) {
126
-
140
+ func updateUIViewController( _ navigation: UINavigationController , context: Context ) {
141
+ navigation. topViewController? . navigationItem. title = navTitle
142
+ navigation. navigationBar. isHidden = navHidden
143
+ }
144
+
145
+ static func dismantleUIViewController( _ navigation: UINavigationController , coordinator: ( ) ) {
146
+ navigation. viewControllers = [ ]
147
+ ( navigation as! PopAwareUINavigationController ) . popHandler = nil
127
148
}
128
149
129
150
typealias UIViewControllerType = UINavigationController
130
151
}
131
152
132
- class PopAwareUINavigationController : UINavigationController
153
+ class PopAwareUINavigationController : UINavigationController , UINavigationControllerDelegate
133
154
{
134
155
var popHandler : ( ( ) -> Void ) ?
156
+
157
+ var popGestureBeganController : UIViewController ?
158
+
159
+ override func viewDidLoad( ) {
160
+ super. viewDidLoad ( )
161
+ self . delegate = self
162
+ }
163
+
164
+ func navigationController( _ navigationController: UINavigationController , willShow viewController: UIViewController , animated: Bool ) {
165
+
166
+ if let coordinator = viewController. transitionCoordinator {
167
+ coordinator. notifyWhenInteractionChanges { [ weak self] ( context) in
168
+ if !context. isCancelled {
169
+ self ? . popHandler ? ( )
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+
177
+ extension View {
178
+ public func uipNavigationBarHidden( _ hidden: Bool ) -> some View {
179
+ return modifier ( NavHiddenModifier ( isHidden: hidden) )
180
+ }
181
+
182
+ public func uipNavigationTitle( _ title: String ) -> some View {
183
+ return modifier ( NavTitleModifier ( title: title) )
184
+ }
185
+
186
+ }
187
+
188
+ private struct NavigationTitleKey : EnvironmentKey {
189
+ static let defaultValue : Binding < String > = . constant( " " )
190
+ }
191
+
192
+ private struct NavigationHiddenKey : EnvironmentKey {
193
+ static let defaultValue : Binding < Bool > = . constant( false )
194
+ }
195
+
196
+ private struct NavigationStyleKey : EnvironmentKey {
197
+ static let defaultValue : NavigationStyle = NavigationStyle ( )
198
+ }
135
199
136
- override func popViewController( animated: Bool ) -> UIViewController ?
137
- {
138
- popHandler ? ( )
139
- return super. popViewController ( animated: animated)
200
+
201
+ extension EnvironmentValues {
202
+
203
+ var uipNavigationStyle : NavigationStyle {
204
+ get { self [ NavigationStyleKey . self] }
205
+ set {
206
+ self [ NavigationStyleKey . self] = newValue
207
+ }
208
+ }
209
+
210
+ var upNavigationHidden : Binding < Bool > {
211
+ get { self [ NavigationHiddenKey . self] }
212
+ set {
213
+ self [ NavigationHiddenKey . self] = newValue
214
+ }
215
+ }
216
+
217
+ var upNavigationTitle : Binding < String > {
218
+ get { self [ NavigationTitleKey . self] }
219
+ set {
220
+ self [ NavigationTitleKey . self] = newValue
221
+ }
222
+ }
223
+ }
224
+
225
+ class NavigationStyle : ObservableObject {
226
+ @Published
227
+ var isHidden = false
228
+ var isHiddenOwner : String = " "
229
+
230
+ @Published
231
+ var title = " "
232
+ var titleOwner : String = " "
233
+ }
234
+
235
+ struct NavTitleModifier : ViewModifier {
236
+ let title : String
237
+
238
+ @State var id = UUID ( ) . uuidString
239
+ @State var initialValue : String = " "
240
+
241
+ @Environment ( \. uipNavigationStyle) var navStyle
242
+
243
+ init ( title: String ) {
244
+ self . title = title
245
+ }
246
+
247
+ func body( content: Content ) -> some View {
248
+
249
+ // In case where title change after onAppear
250
+ if navStyle. titleOwner == id && navStyle. title != title {
251
+ DispatchQueue . main. async {
252
+ navStyle. title = title
253
+ }
254
+ }
255
+
256
+ return content
257
+ . onAppear {
258
+ initialValue = navStyle. title
259
+
260
+ navStyle. title = title
261
+ navStyle. titleOwner = id
262
+ }
263
+ . onDisappear {
264
+ if navStyle. titleOwner == id {
265
+ navStyle. title = initialValue
266
+ navStyle. titleOwner = " "
267
+ }
268
+ }
269
+ }
270
+ }
271
+
272
+ struct NavHiddenModifier : ViewModifier {
273
+ let isHidden : Bool
274
+
275
+ @State var id = UUID ( ) . uuidString
276
+ @State var initialValue : Bool = false
277
+
278
+ @Environment ( \. uipNavigationStyle) var navStyle
279
+
280
+ func body( content: Content ) -> some View {
281
+
282
+ // In case where isHidden change after onAppear
283
+ if navStyle. isHiddenOwner == id && navStyle. isHidden != isHidden {
284
+ DispatchQueue . main. async {
285
+ navStyle. isHidden = isHidden
286
+ }
287
+ }
288
+
289
+ return content
290
+ . onAppear {
291
+ initialValue = navStyle. isHidden
292
+
293
+ navStyle. isHidden = isHidden
294
+ navStyle. isHiddenOwner = id
295
+ }
296
+ . onDisappear {
297
+ if navStyle. isHiddenOwner == id {
298
+ navStyle. isHidden = initialValue
299
+ navStyle. isHiddenOwner = " "
300
+ }
301
+ }
140
302
}
141
303
}
142
304
0 commit comments