@@ -59,7 +59,7 @@ public class MRNavigator {
59
59
}
60
60
61
61
public func navigate( to route: MRRoute ,
62
- from viewController: UIViewController ,
62
+ from viewController: UIViewController ? = nil ,
63
63
navigationType: NavigationType = . push,
64
64
animated: Bool = true ,
65
65
completion: ( ( ) -> Void ) ? = nil ) {
@@ -82,8 +82,8 @@ public class MRNavigator {
82
82
return false
83
83
}
84
84
85
- if let rootVC = UIApplication . shared . windows . first ( where : { $0 . isKeyWindow } ) ? . rootViewController {
86
- navigate ( to: route, from: rootVC )
85
+ if let topvc = topViewController ( ) {
86
+ navigate ( to: route, from: topvc )
87
87
return true
88
88
}
89
89
return false
@@ -121,7 +121,7 @@ public class MRNavigator {
121
121
}
122
122
123
123
private func handleResult( _ result: RouteResult ,
124
- from viewController: UIViewController ,
124
+ from viewController: UIViewController ? ,
125
125
navigationType: NavigationType ,
126
126
animated: Bool ,
127
127
completion: ( ( ) -> Void ) ? ) {
@@ -143,21 +143,31 @@ public class MRNavigator {
143
143
}
144
144
145
145
private func perform( navigation type: NavigationType ,
146
- from: UIViewController ,
146
+ from: UIViewController ? ,
147
147
to: UIViewController ,
148
148
animated: Bool ,
149
149
completion: ( ( ) -> Void ) ? ) {
150
+ var temp = ( from != nil ) ? from : topViewController ( )
151
+ guard let base = temp else {
152
+ completion ? ( )
153
+ return
154
+ }
150
155
switch type {
151
156
case . push:
152
- from. navigationController? . pushViewController ( to, animated: animated)
153
- completion ? ( )
157
+ if let navigationController = base. navigationController {
158
+ navigationController. pushViewController ( to, animated: animated)
159
+ completion ? ( )
160
+ } else {
161
+ let nav = UINavigationController ( rootViewController: to)
162
+ base. present ( nav, animated: animated, completion: completion)
163
+ }
154
164
case . present:
155
- from . present ( to, animated: animated, completion: completion)
165
+ base . present ( to, animated: animated, completion: completion)
156
166
case . modal:
157
167
to. modalPresentationStyle = . fullScreen
158
- from . present ( to, animated: animated, completion: completion)
168
+ base . present ( to, animated: animated, completion: completion)
159
169
case . replace:
160
- guard let navigationController = from . navigationController else {
170
+ guard let navigationController = base . navigationController else {
161
171
completion ? ( )
162
172
return
163
173
}
@@ -167,7 +177,7 @@ public class MRNavigator {
167
177
navigationController. setViewControllers ( viewControllers, animated: animated)
168
178
completion ? ( )
169
179
case . custom( let handler) :
170
- handler ( from , to)
180
+ handler ( base , to)
171
181
completion ? ( )
172
182
}
173
183
}
@@ -185,3 +195,70 @@ public class MRNavigator {
185
195
metadata: [ " params " : route. params, " result " : String ( describing: result) ] )
186
196
}
187
197
}
198
+
199
+
200
+ public extension MRNavigator {
201
+
202
+ /// Get the top most view controller from the base view controller; default param is UIWindow's rootViewController
203
+ func topViewController( _ from: UIViewController ? = nil ) -> UIViewController ? {
204
+ var base = ( from != nil ) ? from : compatibleKeyWindow? . rootViewController
205
+ if let nav = base as? UINavigationController {
206
+ return topViewController ( nav. visibleViewController)
207
+ }
208
+ if let tab = base as? UITabBarController {
209
+ if let selected = tab. selectedViewController {
210
+ return topViewController ( selected)
211
+ }
212
+ }
213
+ if let presented = base? . presentedViewController {
214
+ return topViewController ( presented)
215
+ }
216
+ return base
217
+ }
218
+
219
+ var compatibleKeyWindow : UIWindow ? {
220
+ if #available( iOS 13 , * ) {
221
+ return UIApplication . shared. connectedScenes
222
+ . compactMap { $0 as? UIWindowScene }
223
+ . flatMap { $0. windows }
224
+ . first { $0. isKeyWindow }
225
+ } else {
226
+ return UIApplication . shared. keyWindow
227
+ }
228
+ }
229
+ /// 重置应用到根视图控制器
230
+ func resetToRootViewController( _ animated: Bool = false ) {
231
+ guard let window = compatibleKeyWindow,
232
+ let rootViewController = window. rootViewController else {
233
+ return
234
+ }
235
+
236
+ // 1. 先处理当前显示的模态视图
237
+ if let presentedVC = rootViewController. presentedViewController {
238
+ presentedVC. dismiss ( animated: false ) {
239
+ self . resetToRootHelper ( rootViewController, animated: animated)
240
+ }
241
+ } else {
242
+ resetToRootHelper ( rootViewController, animated: animated)
243
+ }
244
+ }
245
+
246
+ private func resetToRootHelper( _ rootViewController: UIViewController , animated: Bool ) {
247
+ // 2. 处理 UINavigationController
248
+ if let navigationController = rootViewController as? UINavigationController {
249
+ navigationController. popToRootViewController ( animated: animated)
250
+ }
251
+
252
+ // 3. 处理 UITabBarController
253
+ if let tabBarController = rootViewController as? UITabBarController {
254
+ // 重置所有 tab 的导航栈
255
+ tabBarController. viewControllers? . forEach { viewController in
256
+ if let navigationController = viewController as? UINavigationController {
257
+ navigationController. popToRootViewController ( animated: animated)
258
+ }
259
+ }
260
+ // 切换到第一个 tab
261
+ tabBarController. selectedIndex = 0
262
+ }
263
+ }
264
+ }
0 commit comments