├── CRNativeRouterDemo
├── CRNativeRouter.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcuserdata
│ │ │ └── yixing.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── project.pbxproj
├── CRNativeRouter
│ ├── NativeRouterGroup.plist
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── ViewController4.swift
│ ├── ViewController2.swift
│ ├── ViewController3.swift
│ ├── Info.plist
│ ├── NativeRouter.plist
│ ├── ViewController.swift
│ ├── ViewController5.swift
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── AppDelegate.swift
│ └── CRNativeRouter.swift
└── CRNativeRouterTests
│ ├── Info.plist
│ └── CRNativeRouterTests.swift
├── LICENSE
├── README.md
└── CRNativeRouter
└── CRNativeRouter.swift
/CRNativeRouterDemo/CRNativeRouter.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter.xcodeproj/project.xcworkspace/xcuserdata/yixing.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CrashRain/CRNativeRouter/HEAD/CRNativeRouterDemo/CRNativeRouter.xcodeproj/project.xcworkspace/xcuserdata/yixing.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/NativeRouterGroup.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NativeRouter
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouterTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | }
43 | ],
44 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 CrashRain
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouterTests/CRNativeRouterTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CRNativeRouterTests.swift
3 | // CRNativeRouterTests
4 | //
5 | // Created by CrashRain on 16/7/1.
6 | // Copyright © 2016年 CrashRain. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import CRNativeRouter
11 |
12 | class CRNativeRouterTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/ViewController4.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController4.swift
3 | // CRNativeRouter
4 | //
5 | // Created by 易行 on 16/7/30.
6 | // Copyright © 2016年 Demeijia. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController4: UIViewController {
12 |
13 | fileprivate var test = 0
14 | fileprivate var value = 0
15 |
16 | @IBOutlet weak var testLabel: UILabel!
17 | @IBOutlet weak var valueLabel: UILabel!
18 |
19 | override func viewDidLoad() {
20 | super.viewDidLoad()
21 |
22 | // Do any additional setup after loading the view.
23 |
24 | testLabel.text = "test=\(test)"
25 | valueLabel.text = "value=\(value)"
26 | }
27 |
28 | override func didReceiveMemoryWarning() {
29 | super.didReceiveMemoryWarning()
30 | // Dispose of any resources that can be recreated.
31 | }
32 |
33 | @IBAction func dismiss(_ sender: UIBarButtonItem) {
34 | self.dismiss(animated: true, completion: nil)
35 | }
36 |
37 | }
38 |
39 | extension ViewController4: CRNativeRouterProtocol {
40 | func getParametersFromRouter(_ parameter: [String : Any]) {
41 | test = parameter["test"] as! Int
42 | value = parameter["value"] as! Int
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/ViewController2.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController2.swift
3 | // CRNativeRouter
4 | //
5 | // Created by CrashRain on 16/7/5.
6 | // Copyright © 2016年 CrashRain. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController2: UIViewController {
12 |
13 | fileprivate var temp = 0
14 | fileprivate var test = 0
15 | fileprivate var url = ""
16 |
17 | @IBOutlet weak var tempLabel: UILabel!
18 | @IBOutlet weak var testLabel: UILabel!
19 | @IBOutlet weak var urlLabel: UILabel!
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | // Do any additional setup after loading the view.
25 |
26 | tempLabel.text = "temp=\(temp)"
27 | testLabel.text = "test=\(test)"
28 | urlLabel.text = "URL: \(url)"
29 | }
30 |
31 | override func didReceiveMemoryWarning() {
32 | super.didReceiveMemoryWarning()
33 | // Dispose of any resources that can be recreated.
34 | }
35 | }
36 |
37 | extension ViewController2: CRNativeRouterProtocol {
38 | func getParametersFromRouter(_ parameter: [String : Any]) {
39 | temp = parameter["temp"] as! Int
40 | test = parameter["test"] as! Int
41 | url = parameter["url"] as! String
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/ViewController3.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController3.swift
3 | // CRNativeRouter
4 | //
5 | // Created by CrashRain on 16/7/21.
6 | // Copyright © 2016年 CrashRain. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController3: UIViewController {
12 |
13 | fileprivate var test = 0
14 | fileprivate var temp = 0
15 | fileprivate var url = ""
16 |
17 | @IBOutlet weak var testLabel: UILabel!
18 | @IBOutlet weak var tempLabel: UILabel!
19 | @IBOutlet weak var urlLabel: UILabel!
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | // Do any additional setup after loading the view.
25 |
26 | testLabel.text = "test=\(test)"
27 | tempLabel.text = "temp=\(temp)"
28 | urlLabel.text = "URL: \(url)"
29 | }
30 |
31 | override func didReceiveMemoryWarning() {
32 | super.didReceiveMemoryWarning()
33 | // Dispose of any resources that can be recreated.
34 | }
35 | }
36 |
37 | extension ViewController3: CRNativeRouterDelegate {
38 | func getParameters(from router: CRNativeRouter, parameters: CRNativeRouterParam) {
39 | test = parameters.test as? Int ?? 0
40 | temp = parameters.temp as? Int ?? 0
41 | url = parameters.url as? String ?? ""
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0.0
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/NativeRouter.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | identifier
7 | ViewController5
8 | parameters
9 |
10 | temp
11 | test
12 | url
13 |
14 | storyboard
15 | Main
16 | type
17 | ViewController5
18 | name
19 | vc5.md
20 |
21 |
22 | parameters
23 |
24 | value
25 | test
26 |
27 | identifier
28 | ViewController4
29 | storyboard
30 | Main
31 | type
32 | ViewController4
33 | name
34 | vc4.md
35 |
36 |
37 | parameters
38 |
39 | url
40 | test
41 | temp
42 |
43 | identifier
44 | ViewController3
45 | storyboard
46 | Main
47 | type
48 | ViewController3
49 | name
50 | vc3.md
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CRNativeRouter
4 | //
5 | // Created by CrashRain on 16/7/1.
6 | // Copyright © 2016年 CrashRain. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | // Do any additional setup after loading the view, typically from a nib.
16 | }
17 |
18 | override func didReceiveMemoryWarning() {
19 | super.didReceiveMemoryWarning()
20 | // Dispose of any resources that can be recreated.
21 | }
22 |
23 | @IBAction func jump(_ sender: UIButton) {
24 | CRNativeRouter.shared.show("Medical://vc2.md?temp=1", parameters: ["test": 1, "url": "http://xxxx.com?id=1&fdfd=2&fdg=3"])
25 | }
26 |
27 | @IBAction func jumpToView3(_ sender: UIButton) {
28 | CRNativeRouter.shared.show("Medical://vc3.md?temp=3", parameters: ["test": 2, "url": "http://yyyy.com?id=1&haha=2&hello=3"])
29 | }
30 |
31 | @IBAction func jumpToView4(_ sender: UIButton) {
32 | CRNativeRouter.shared.present("Medical://vc4.md?test=1&value=3")
33 | }
34 |
35 | @IBAction func jumpToView5(_ sender: UIButton) {
36 | CRNativeRouter.shared.present("Medical://vc5.md?test=1&temp=2", parameters: ["url": "www.crashrain.com"], inNavigation: true)
37 | }
38 | }
39 |
40 | extension ViewController: CRNativeRouterProtocol {
41 | func getParametersFromRouter(_ parameter: [String : Any]) {
42 |
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/ViewController5.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController5.swift
3 | // CRNativeRouter
4 | //
5 | // Created by 易行 on 2017/3/17.
6 | // Copyright © 2017年 Demeijia. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController5: UIViewController {
12 |
13 | @IBOutlet weak var urlLabel: UILabel!
14 | @IBOutlet weak var tempLabel: UILabel!
15 | @IBOutlet weak var testLabel: UILabel!
16 |
17 | var url = ""
18 | var temp = 0
19 | var test = 0
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | // Do any additional setup after loading the view.
25 | urlLabel.text = "url=\(url)"
26 | testLabel.text = "test=\(test)"
27 | tempLabel.text = "temp=\(temp)"
28 | }
29 |
30 | override func didReceiveMemoryWarning() {
31 | super.didReceiveMemoryWarning()
32 | // Dispose of any resources that can be recreated.
33 | }
34 |
35 | @IBAction func testNavigation(_ sender: UIButton) {
36 | let viewController = UIViewController()
37 | viewController.view.backgroundColor = UIColor.green
38 |
39 | navigationController?.pushViewController(viewController, animated: true)
40 | }
41 |
42 | @IBAction func dismiss(_ sender: UIBarButtonItem) {
43 | navigationController?.dismiss(animated: true, completion: nil)
44 | }
45 | }
46 |
47 | extension ViewController5: CRNativeRouterDelegate {
48 | func getParameters(from router: CRNativeRouter, parameters: CRNativeRouterParam) {
49 | url = parameters.url as? String ?? ""
50 | temp = parameters.temp as? Int ?? 0
51 | test = parameters.test as? Int ?? 0
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CRNativeRouter
4 | //
5 | // Created by CrashRain on 16/7/1.
6 | // Copyright © 2016年 CrashRain. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 |
20 | CRNativeRouter.shared.setURLModifyFormat("^(Medical://)(\\w+\\.md)(\\?(([a-zA-Z]+\\w*=\\w+)(&[a-zA-Z]+\\w*=\\w+)*)|([a-zA-Z]+\\w*=\\w+))?$")
21 |
22 | CRNativeRouter.shared.registerGroupModules(fromConfiguration: "NativeRouterGroup")
23 |
24 | CRNativeRouter.shared.registerModule("vc.md", type: ViewController.self, storyboard: "Main", identifier: "ViewController", parameters: nil)
25 | CRNativeRouter.shared.registerModule("vc2.md", type: ViewController2.self, storyboard: "Main", identifier: "ViewController2", parameters: ["temp", "test", "url"])
26 |
27 | return true
28 | }
29 |
30 | func applicationWillResignActive(_ application: UIApplication) {
31 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
32 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
33 | }
34 |
35 | func applicationDidEnterBackground(_ application: UIApplication) {
36 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
38 | }
39 |
40 | func applicationWillEnterForeground(_ application: UIApplication) {
41 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
42 | }
43 |
44 | func applicationDidBecomeActive(_ application: UIApplication) {
45 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
46 | }
47 |
48 | func applicationWillTerminate(_ application: UIApplication) {
49 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
50 | }
51 |
52 |
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CRNativeRouter
2 | 统跳协议的实现,主要用来降低模块间的耦合度,通过一个公共的组件来切换视图显示和传递数据,支持代码生成、XIB、StoryBoard界面的管理。
3 |
4 | [TOC]
5 |
6 | ##使用方法
7 | ###1、设置统跳协议需要辨识的路径正则表达式
8 | Router需要一个Internal URL来辨识需要显示的界面和要传递的参数,并且需要对传入的URL判断其合法性。在使用前需要先传入一个URL通配的正则表达式给Router。正则表达式可以用如下的模板:
9 |
10 | ```
11 | ^(Module://)(\\w+\\.md)(\\?(([a-zA-Z]+\\w*=\\w+)(&[a-zA-Z]+\\w*=\\w+)*)|([a-zA-Z]+\\w*=\\w+))?$
12 | ```
13 |
14 | ###2、注册ViewController到Router
15 | 之后需要注册已有的或者需要显示的ViewController到Router,注册针对实现有三种方法,分别对应代码生成界面、Xib或者Nib、StoryBoard。
16 |
17 | 1. 代码生成界面注册API
18 |
19 | `registerNewModule(_ name: String, type: AnyClass, parameters: [String]?)`
20 |
21 | 通过这个函数传入该ViewController对应的Module名称、类型以及需要传入的参数名称。
22 |
23 | 2. Xib或者Nib注册API
24 |
25 | `registerNewModule(_ name: String, type: AnyClass, nib: String, parameters: [String]?)`
26 |
27 | 通过这个函数传入该ViewController的Module名称、类型、对应的nib名称以及需要传入的参数名称。
28 |
29 | 3. StoryBoard注册API
30 |
31 | `registerNewModule(_ name: String, type: AnyClass, storyboard: String, identifier: String, parameters: [String]?)`
32 |
33 | 通过这个函数传入该ViewController的Module名称、类型、对应的storyboard名称和其中的identifier字符串,以及需要传入的参数名称。
34 |
35 | ####团队协作
36 |
37 | 考虑到开发过程中会多人团队协作,如果单纯用代码进行注册会碰到git容易冲突的情况,因此引入用plist文件来整体管理每名成员的模块并进行批量注册。
38 |
39 | 1. 指定全局管理开发成员plist文件名称的plist文件
40 |
41 | 这个plist文件管理项目中需要注册的子文件,只有文件名称在这个plist文件中才会被Router进行注册。该plist文件内容为一个数组即可,示例内容如下:
42 |
43 | ```plist
44 |
45 |
46 |
47 |
48 | NativeRouter
49 |
50 |
51 | ```
52 |
53 | 2. 每名成员生成自己的plist文件,将模块添加到该文件中,并将文件名称注册到全局plist文件
54 |
55 | 项目中的每位成员需要生成自己的plist文件,并且将自己开发的模块注册到该plist文件中。plist文件首先需要指定一个名为Modules的array类型数组,之后将每个ViewController注册到文件中。name和type是必须指定的字段,parameters为可选字段,若没有则默认不需要参数。StoryBoard组件额外需要storyboard和identifier字段,nib组件额外需要nib字段。示例内容如下:
56 |
57 | ```plist
58 |
59 |
60 |
61 |
62 | Modules
63 |
64 |
65 | parameters
66 |
67 | value
68 | test
69 |
70 | identifier
71 | ViewController4
72 | storyboard
73 | Main
74 | type
75 | ViewController4
76 | name
77 | vc4.md
78 |
79 |
80 | parameters
81 |
82 | url
83 | test
84 | temp
85 |
86 | identifier
87 | ViewController3
88 | storyboard
89 | Main
90 | type
91 | ViewController3
92 | name
93 | vc3.md
94 |
95 |
96 |
97 |
98 | ```
99 | 3. 调用API进行注册
100 | 之后调用如下API,传入全局plist文件名进行注册即可:
101 | `registerModulesFromDeveloperGroupConfiguration(_ filename: String)`
102 |
103 | ###3、界面跳转
104 |
105 | 之后界面切换调用API即可,支持navigation的Show、ShowDetail、Popup三种方式,另外支持Modally显示方式,具体查看API名称即可。
106 |
107 | 同时也支持是否传入当前navigation的选项,如果不传入则会递归查找当前显示的navigation,建议传入减少性能开销。
108 |
109 | ###4、统跳原理
110 |
111 | 欢迎访问我的私人Blog [CRNativeRouter原理](http://crashrain.com/?p=259)
112 |
113 |
114 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 9D53F4671D4093FC004FE794 /* NativeRouter.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9D53F4661D4093FC004FE794 /* NativeRouter.plist */; };
11 | 9D53F4691D4099FF004FE794 /* ViewController3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D53F4681D4099FF004FE794 /* ViewController3.swift */; };
12 | 9D53F46B1D40A090004FE794 /* NativeRouterGroup.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9D53F46A1D40A090004FE794 /* NativeRouterGroup.plist */; };
13 | 9D8B5DA21D4C72D10059E6C2 /* ViewController4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D8B5DA11D4C72D10059E6C2 /* ViewController4.swift */; };
14 | 9D9E14641D2B7F30006459EA /* ViewController2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D9E14631D2B7F30006459EA /* ViewController2.swift */; };
15 | 9DA76A021D26421B00D21910 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DA76A011D26421B00D21910 /* AppDelegate.swift */; };
16 | 9DA76A041D26421B00D21910 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DA76A031D26421B00D21910 /* ViewController.swift */; };
17 | 9DA76A071D26421B00D21910 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9DA76A051D26421B00D21910 /* Main.storyboard */; };
18 | 9DA76A091D26421B00D21910 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9DA76A081D26421B00D21910 /* Assets.xcassets */; };
19 | 9DA76A0C1D26421B00D21910 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9DA76A0A1D26421B00D21910 /* LaunchScreen.storyboard */; };
20 | 9DA76A171D26421B00D21910 /* CRNativeRouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DA76A161D26421B00D21910 /* CRNativeRouterTests.swift */; };
21 | 9DA76A221D26430000D21910 /* CRNativeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DA76A211D26430000D21910 /* CRNativeRouter.swift */; };
22 | 9DF8534E1E7BAF0400A170BB /* ViewController5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF8534D1E7BAF0400A170BB /* ViewController5.swift */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXContainerItemProxy section */
26 | 9DA76A131D26421B00D21910 /* PBXContainerItemProxy */ = {
27 | isa = PBXContainerItemProxy;
28 | containerPortal = 9DA769F61D26421A00D21910 /* Project object */;
29 | proxyType = 1;
30 | remoteGlobalIDString = 9DA769FD1D26421A00D21910;
31 | remoteInfo = CRNativeRouter;
32 | };
33 | /* End PBXContainerItemProxy section */
34 |
35 | /* Begin PBXFileReference section */
36 | 9D53F4661D4093FC004FE794 /* NativeRouter.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = NativeRouter.plist; sourceTree = ""; };
37 | 9D53F4681D4099FF004FE794 /* ViewController3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController3.swift; sourceTree = ""; };
38 | 9D53F46A1D40A090004FE794 /* NativeRouterGroup.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = NativeRouterGroup.plist; sourceTree = ""; };
39 | 9D8B5DA11D4C72D10059E6C2 /* ViewController4.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController4.swift; sourceTree = ""; };
40 | 9D9E14631D2B7F30006459EA /* ViewController2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController2.swift; sourceTree = ""; };
41 | 9DA769FE1D26421A00D21910 /* CRNativeRouter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CRNativeRouter.app; sourceTree = BUILT_PRODUCTS_DIR; };
42 | 9DA76A011D26421B00D21910 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
43 | 9DA76A031D26421B00D21910 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
44 | 9DA76A061D26421B00D21910 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
45 | 9DA76A081D26421B00D21910 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
46 | 9DA76A0B1D26421B00D21910 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
47 | 9DA76A0D1D26421B00D21910 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
48 | 9DA76A121D26421B00D21910 /* CRNativeRouterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CRNativeRouterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
49 | 9DA76A161D26421B00D21910 /* CRNativeRouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CRNativeRouterTests.swift; sourceTree = ""; };
50 | 9DA76A181D26421B00D21910 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
51 | 9DA76A211D26430000D21910 /* CRNativeRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CRNativeRouter.swift; sourceTree = ""; };
52 | 9DF8534D1E7BAF0400A170BB /* ViewController5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController5.swift; sourceTree = ""; };
53 | /* End PBXFileReference section */
54 |
55 | /* Begin PBXFrameworksBuildPhase section */
56 | 9DA769FB1D26421A00D21910 /* Frameworks */ = {
57 | isa = PBXFrameworksBuildPhase;
58 | buildActionMask = 2147483647;
59 | files = (
60 | );
61 | runOnlyForDeploymentPostprocessing = 0;
62 | };
63 | 9DA76A0F1D26421B00D21910 /* Frameworks */ = {
64 | isa = PBXFrameworksBuildPhase;
65 | buildActionMask = 2147483647;
66 | files = (
67 | );
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | /* End PBXFrameworksBuildPhase section */
71 |
72 | /* Begin PBXGroup section */
73 | 9DA769F51D26421A00D21910 = {
74 | isa = PBXGroup;
75 | children = (
76 | 9DA76A001D26421B00D21910 /* CRNativeRouter */,
77 | 9DA76A151D26421B00D21910 /* CRNativeRouterTests */,
78 | 9DA769FF1D26421A00D21910 /* Products */,
79 | );
80 | sourceTree = "";
81 | };
82 | 9DA769FF1D26421A00D21910 /* Products */ = {
83 | isa = PBXGroup;
84 | children = (
85 | 9DA769FE1D26421A00D21910 /* CRNativeRouter.app */,
86 | 9DA76A121D26421B00D21910 /* CRNativeRouterTests.xctest */,
87 | );
88 | name = Products;
89 | sourceTree = "";
90 | };
91 | 9DA76A001D26421B00D21910 /* CRNativeRouter */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 9DA76A011D26421B00D21910 /* AppDelegate.swift */,
95 | 9DA76A211D26430000D21910 /* CRNativeRouter.swift */,
96 | 9DA76A031D26421B00D21910 /* ViewController.swift */,
97 | 9D9E14631D2B7F30006459EA /* ViewController2.swift */,
98 | 9D53F4681D4099FF004FE794 /* ViewController3.swift */,
99 | 9D8B5DA11D4C72D10059E6C2 /* ViewController4.swift */,
100 | 9DF8534D1E7BAF0400A170BB /* ViewController5.swift */,
101 | 9DA76A051D26421B00D21910 /* Main.storyboard */,
102 | 9DA76A081D26421B00D21910 /* Assets.xcassets */,
103 | 9DA76A0A1D26421B00D21910 /* LaunchScreen.storyboard */,
104 | 9DA76A0D1D26421B00D21910 /* Info.plist */,
105 | 9D53F4661D4093FC004FE794 /* NativeRouter.plist */,
106 | 9D53F46A1D40A090004FE794 /* NativeRouterGroup.plist */,
107 | );
108 | path = CRNativeRouter;
109 | sourceTree = "";
110 | };
111 | 9DA76A151D26421B00D21910 /* CRNativeRouterTests */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 9DA76A161D26421B00D21910 /* CRNativeRouterTests.swift */,
115 | 9DA76A181D26421B00D21910 /* Info.plist */,
116 | );
117 | path = CRNativeRouterTests;
118 | sourceTree = "";
119 | };
120 | /* End PBXGroup section */
121 |
122 | /* Begin PBXNativeTarget section */
123 | 9DA769FD1D26421A00D21910 /* CRNativeRouter */ = {
124 | isa = PBXNativeTarget;
125 | buildConfigurationList = 9DA76A1B1D26421B00D21910 /* Build configuration list for PBXNativeTarget "CRNativeRouter" */;
126 | buildPhases = (
127 | 9DA769FA1D26421A00D21910 /* Sources */,
128 | 9DA769FB1D26421A00D21910 /* Frameworks */,
129 | 9DA769FC1D26421A00D21910 /* Resources */,
130 | );
131 | buildRules = (
132 | );
133 | dependencies = (
134 | );
135 | name = CRNativeRouter;
136 | productName = CRNativeRouter;
137 | productReference = 9DA769FE1D26421A00D21910 /* CRNativeRouter.app */;
138 | productType = "com.apple.product-type.application";
139 | };
140 | 9DA76A111D26421B00D21910 /* CRNativeRouterTests */ = {
141 | isa = PBXNativeTarget;
142 | buildConfigurationList = 9DA76A1E1D26421B00D21910 /* Build configuration list for PBXNativeTarget "CRNativeRouterTests" */;
143 | buildPhases = (
144 | 9DA76A0E1D26421B00D21910 /* Sources */,
145 | 9DA76A0F1D26421B00D21910 /* Frameworks */,
146 | 9DA76A101D26421B00D21910 /* Resources */,
147 | );
148 | buildRules = (
149 | );
150 | dependencies = (
151 | 9DA76A141D26421B00D21910 /* PBXTargetDependency */,
152 | );
153 | name = CRNativeRouterTests;
154 | productName = CRNativeRouterTests;
155 | productReference = 9DA76A121D26421B00D21910 /* CRNativeRouterTests.xctest */;
156 | productType = "com.apple.product-type.bundle.unit-test";
157 | };
158 | /* End PBXNativeTarget section */
159 |
160 | /* Begin PBXProject section */
161 | 9DA769F61D26421A00D21910 /* Project object */ = {
162 | isa = PBXProject;
163 | attributes = {
164 | LastSwiftUpdateCheck = 0730;
165 | LastUpgradeCheck = 1420;
166 | ORGANIZATIONNAME = Demeijia;
167 | TargetAttributes = {
168 | 9DA769FD1D26421A00D21910 = {
169 | CreatedOnToolsVersion = 7.3.1;
170 | DevelopmentTeam = 755CA3W3VN;
171 | LastSwiftMigration = 0820;
172 | };
173 | 9DA76A111D26421B00D21910 = {
174 | CreatedOnToolsVersion = 7.3.1;
175 | LastSwiftMigration = 0820;
176 | TestTargetID = 9DA769FD1D26421A00D21910;
177 | };
178 | };
179 | };
180 | buildConfigurationList = 9DA769F91D26421A00D21910 /* Build configuration list for PBXProject "CRNativeRouter" */;
181 | compatibilityVersion = "Xcode 3.2";
182 | developmentRegion = en;
183 | hasScannedForEncodings = 0;
184 | knownRegions = (
185 | en,
186 | Base,
187 | );
188 | mainGroup = 9DA769F51D26421A00D21910;
189 | productRefGroup = 9DA769FF1D26421A00D21910 /* Products */;
190 | projectDirPath = "";
191 | projectRoot = "";
192 | targets = (
193 | 9DA769FD1D26421A00D21910 /* CRNativeRouter */,
194 | 9DA76A111D26421B00D21910 /* CRNativeRouterTests */,
195 | );
196 | };
197 | /* End PBXProject section */
198 |
199 | /* Begin PBXResourcesBuildPhase section */
200 | 9DA769FC1D26421A00D21910 /* Resources */ = {
201 | isa = PBXResourcesBuildPhase;
202 | buildActionMask = 2147483647;
203 | files = (
204 | 9D53F4671D4093FC004FE794 /* NativeRouter.plist in Resources */,
205 | 9DA76A0C1D26421B00D21910 /* LaunchScreen.storyboard in Resources */,
206 | 9DA76A091D26421B00D21910 /* Assets.xcassets in Resources */,
207 | 9D53F46B1D40A090004FE794 /* NativeRouterGroup.plist in Resources */,
208 | 9DA76A071D26421B00D21910 /* Main.storyboard in Resources */,
209 | );
210 | runOnlyForDeploymentPostprocessing = 0;
211 | };
212 | 9DA76A101D26421B00D21910 /* Resources */ = {
213 | isa = PBXResourcesBuildPhase;
214 | buildActionMask = 2147483647;
215 | files = (
216 | );
217 | runOnlyForDeploymentPostprocessing = 0;
218 | };
219 | /* End PBXResourcesBuildPhase section */
220 |
221 | /* Begin PBXSourcesBuildPhase section */
222 | 9DA769FA1D26421A00D21910 /* Sources */ = {
223 | isa = PBXSourcesBuildPhase;
224 | buildActionMask = 2147483647;
225 | files = (
226 | 9D53F4691D4099FF004FE794 /* ViewController3.swift in Sources */,
227 | 9D8B5DA21D4C72D10059E6C2 /* ViewController4.swift in Sources */,
228 | 9DA76A041D26421B00D21910 /* ViewController.swift in Sources */,
229 | 9D9E14641D2B7F30006459EA /* ViewController2.swift in Sources */,
230 | 9DA76A221D26430000D21910 /* CRNativeRouter.swift in Sources */,
231 | 9DA76A021D26421B00D21910 /* AppDelegate.swift in Sources */,
232 | 9DF8534E1E7BAF0400A170BB /* ViewController5.swift in Sources */,
233 | );
234 | runOnlyForDeploymentPostprocessing = 0;
235 | };
236 | 9DA76A0E1D26421B00D21910 /* Sources */ = {
237 | isa = PBXSourcesBuildPhase;
238 | buildActionMask = 2147483647;
239 | files = (
240 | 9DA76A171D26421B00D21910 /* CRNativeRouterTests.swift in Sources */,
241 | );
242 | runOnlyForDeploymentPostprocessing = 0;
243 | };
244 | /* End PBXSourcesBuildPhase section */
245 |
246 | /* Begin PBXTargetDependency section */
247 | 9DA76A141D26421B00D21910 /* PBXTargetDependency */ = {
248 | isa = PBXTargetDependency;
249 | target = 9DA769FD1D26421A00D21910 /* CRNativeRouter */;
250 | targetProxy = 9DA76A131D26421B00D21910 /* PBXContainerItemProxy */;
251 | };
252 | /* End PBXTargetDependency section */
253 |
254 | /* Begin PBXVariantGroup section */
255 | 9DA76A051D26421B00D21910 /* Main.storyboard */ = {
256 | isa = PBXVariantGroup;
257 | children = (
258 | 9DA76A061D26421B00D21910 /* Base */,
259 | );
260 | name = Main.storyboard;
261 | sourceTree = "";
262 | };
263 | 9DA76A0A1D26421B00D21910 /* LaunchScreen.storyboard */ = {
264 | isa = PBXVariantGroup;
265 | children = (
266 | 9DA76A0B1D26421B00D21910 /* Base */,
267 | );
268 | name = LaunchScreen.storyboard;
269 | sourceTree = "";
270 | };
271 | /* End PBXVariantGroup section */
272 |
273 | /* Begin XCBuildConfiguration section */
274 | 9DA76A191D26421B00D21910 /* Debug */ = {
275 | isa = XCBuildConfiguration;
276 | buildSettings = {
277 | ALWAYS_SEARCH_USER_PATHS = NO;
278 | CLANG_ANALYZER_NONNULL = YES;
279 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
280 | CLANG_CXX_LIBRARY = "libc++";
281 | CLANG_ENABLE_MODULES = YES;
282 | CLANG_ENABLE_OBJC_ARC = YES;
283 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
284 | CLANG_WARN_BOOL_CONVERSION = YES;
285 | CLANG_WARN_COMMA = YES;
286 | CLANG_WARN_CONSTANT_CONVERSION = YES;
287 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
288 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
289 | CLANG_WARN_EMPTY_BODY = YES;
290 | CLANG_WARN_ENUM_CONVERSION = YES;
291 | CLANG_WARN_INFINITE_RECURSION = YES;
292 | CLANG_WARN_INT_CONVERSION = YES;
293 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
294 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
295 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
297 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
298 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
299 | CLANG_WARN_STRICT_PROTOTYPES = YES;
300 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
301 | CLANG_WARN_UNREACHABLE_CODE = YES;
302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
303 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
304 | COPY_PHASE_STRIP = NO;
305 | DEBUG_INFORMATION_FORMAT = dwarf;
306 | ENABLE_STRICT_OBJC_MSGSEND = YES;
307 | ENABLE_TESTABILITY = YES;
308 | GCC_C_LANGUAGE_STANDARD = gnu99;
309 | GCC_DYNAMIC_NO_PIC = NO;
310 | GCC_NO_COMMON_BLOCKS = YES;
311 | GCC_OPTIMIZATION_LEVEL = 0;
312 | GCC_PREPROCESSOR_DEFINITIONS = (
313 | "DEBUG=1",
314 | "$(inherited)",
315 | );
316 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
317 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
318 | GCC_WARN_UNDECLARED_SELECTOR = YES;
319 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
320 | GCC_WARN_UNUSED_FUNCTION = YES;
321 | GCC_WARN_UNUSED_VARIABLE = YES;
322 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
323 | MTL_ENABLE_DEBUG_INFO = YES;
324 | ONLY_ACTIVE_ARCH = YES;
325 | SDKROOT = iphoneos;
326 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
327 | };
328 | name = Debug;
329 | };
330 | 9DA76A1A1D26421B00D21910 /* Release */ = {
331 | isa = XCBuildConfiguration;
332 | buildSettings = {
333 | ALWAYS_SEARCH_USER_PATHS = NO;
334 | CLANG_ANALYZER_NONNULL = YES;
335 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
336 | CLANG_CXX_LIBRARY = "libc++";
337 | CLANG_ENABLE_MODULES = YES;
338 | CLANG_ENABLE_OBJC_ARC = YES;
339 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
340 | CLANG_WARN_BOOL_CONVERSION = YES;
341 | CLANG_WARN_COMMA = YES;
342 | CLANG_WARN_CONSTANT_CONVERSION = YES;
343 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
344 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
345 | CLANG_WARN_EMPTY_BODY = YES;
346 | CLANG_WARN_ENUM_CONVERSION = YES;
347 | CLANG_WARN_INFINITE_RECURSION = YES;
348 | CLANG_WARN_INT_CONVERSION = YES;
349 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
350 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
351 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
352 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
353 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
354 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
355 | CLANG_WARN_STRICT_PROTOTYPES = YES;
356 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
357 | CLANG_WARN_UNREACHABLE_CODE = YES;
358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
359 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
360 | COPY_PHASE_STRIP = NO;
361 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
362 | ENABLE_NS_ASSERTIONS = NO;
363 | ENABLE_STRICT_OBJC_MSGSEND = YES;
364 | GCC_C_LANGUAGE_STANDARD = gnu99;
365 | GCC_NO_COMMON_BLOCKS = YES;
366 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
367 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
368 | GCC_WARN_UNDECLARED_SELECTOR = YES;
369 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
370 | GCC_WARN_UNUSED_FUNCTION = YES;
371 | GCC_WARN_UNUSED_VARIABLE = YES;
372 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
373 | MTL_ENABLE_DEBUG_INFO = NO;
374 | SDKROOT = iphoneos;
375 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
376 | VALIDATE_PRODUCT = YES;
377 | };
378 | name = Release;
379 | };
380 | 9DA76A1C1D26421B00D21910 /* Debug */ = {
381 | isa = XCBuildConfiguration;
382 | buildSettings = {
383 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
384 | DEVELOPMENT_TEAM = 755CA3W3VN;
385 | INFOPLIST_FILE = CRNativeRouter/Info.plist;
386 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
387 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
388 | PRODUCT_BUNDLE_IDENTIFIER = com.Demeijia.CRNativeRouter;
389 | PRODUCT_NAME = "$(TARGET_NAME)";
390 | SWIFT_VERSION = 5.0;
391 | };
392 | name = Debug;
393 | };
394 | 9DA76A1D1D26421B00D21910 /* Release */ = {
395 | isa = XCBuildConfiguration;
396 | buildSettings = {
397 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
398 | DEVELOPMENT_TEAM = 755CA3W3VN;
399 | INFOPLIST_FILE = CRNativeRouter/Info.plist;
400 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
401 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
402 | PRODUCT_BUNDLE_IDENTIFIER = com.Demeijia.CRNativeRouter;
403 | PRODUCT_NAME = "$(TARGET_NAME)";
404 | SWIFT_VERSION = 5.0;
405 | };
406 | name = Release;
407 | };
408 | 9DA76A1F1D26421B00D21910 /* Debug */ = {
409 | isa = XCBuildConfiguration;
410 | buildSettings = {
411 | BUNDLE_LOADER = "$(TEST_HOST)";
412 | INFOPLIST_FILE = CRNativeRouterTests/Info.plist;
413 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
414 | PRODUCT_BUNDLE_IDENTIFIER = com.Demeijia.CRNativeRouterTests;
415 | PRODUCT_NAME = "$(TARGET_NAME)";
416 | SWIFT_VERSION = 5.0;
417 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CRNativeRouter.app/CRNativeRouter";
418 | };
419 | name = Debug;
420 | };
421 | 9DA76A201D26421B00D21910 /* Release */ = {
422 | isa = XCBuildConfiguration;
423 | buildSettings = {
424 | BUNDLE_LOADER = "$(TEST_HOST)";
425 | INFOPLIST_FILE = CRNativeRouterTests/Info.plist;
426 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
427 | PRODUCT_BUNDLE_IDENTIFIER = com.Demeijia.CRNativeRouterTests;
428 | PRODUCT_NAME = "$(TARGET_NAME)";
429 | SWIFT_VERSION = 5.0;
430 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CRNativeRouter.app/CRNativeRouter";
431 | };
432 | name = Release;
433 | };
434 | /* End XCBuildConfiguration section */
435 |
436 | /* Begin XCConfigurationList section */
437 | 9DA769F91D26421A00D21910 /* Build configuration list for PBXProject "CRNativeRouter" */ = {
438 | isa = XCConfigurationList;
439 | buildConfigurations = (
440 | 9DA76A191D26421B00D21910 /* Debug */,
441 | 9DA76A1A1D26421B00D21910 /* Release */,
442 | );
443 | defaultConfigurationIsVisible = 0;
444 | defaultConfigurationName = Release;
445 | };
446 | 9DA76A1B1D26421B00D21910 /* Build configuration list for PBXNativeTarget "CRNativeRouter" */ = {
447 | isa = XCConfigurationList;
448 | buildConfigurations = (
449 | 9DA76A1C1D26421B00D21910 /* Debug */,
450 | 9DA76A1D1D26421B00D21910 /* Release */,
451 | );
452 | defaultConfigurationIsVisible = 0;
453 | defaultConfigurationName = Release;
454 | };
455 | 9DA76A1E1D26421B00D21910 /* Build configuration list for PBXNativeTarget "CRNativeRouterTests" */ = {
456 | isa = XCConfigurationList;
457 | buildConfigurations = (
458 | 9DA76A1F1D26421B00D21910 /* Debug */,
459 | 9DA76A201D26421B00D21910 /* Release */,
460 | );
461 | defaultConfigurationIsVisible = 0;
462 | defaultConfigurationName = Release;
463 | };
464 | /* End XCConfigurationList section */
465 | };
466 | rootObject = 9DA769F61D26421A00D21910 /* Project object */;
467 | }
468 |
--------------------------------------------------------------------------------
/CRNativeRouter/CRNativeRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CRNativeRouter.swift
3 | // CRNativeRouter
4 | //
5 | // Created by CrashRain on 16/7/1.
6 | // Copyright © 2016年 CrashRain. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | private func ~= (lhs: String, rhs: String) -> Bool {
12 | if let result = ((try? NSRegularExpression(pattern: rhs, options: .caseInsensitive).firstMatch(in: lhs, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSRange(location: 0, length: lhs.count))) as NSTextCheckingResult??) {
13 | return result != nil
14 | }
15 |
16 | return false
17 | }
18 |
19 | @objc public class CRNativeRouterPresentOptions: NSObject {
20 | var presentationStyle = UIModalPresentationStyle.overCurrentContext
21 | var transitionStyle = UIModalTransitionStyle.coverVertical
22 | }
23 |
24 | @dynamicMemberLookup
25 | public struct CRNativeRouterParamT where Key: Hashable & ExpressibleByStringLiteral {
26 | var wrappedValue: [Key: Value] = [:]
27 |
28 | init(dict: [Key: Value]) {
29 | wrappedValue = dict
30 | }
31 |
32 | subscript(key: Key) -> Value? {
33 | get {
34 | return wrappedValue[key]
35 | }
36 | set {
37 | wrappedValue[key] = newValue
38 | }
39 | }
40 |
41 | subscript(dynamicMember key: Key) -> Value? {
42 | return wrappedValue[key]
43 | }
44 | }
45 |
46 | public typealias CRNativeRouterParam = CRNativeRouterParamT
47 |
48 | public protocol CRNativeRouterProtocol: NSObjectProtocol {
49 | func getParametersFromRouter(_ parameter: [String: Any])
50 | }
51 |
52 | public protocol CRNativeRouterDelegate {
53 | func getParameters(from router: CRNativeRouter, parameters: CRNativeRouterParam)
54 | }
55 |
56 | public class CRNativeRouter: NSObject {
57 |
58 | // 视图控制器类型枚举
59 | private enum CRNativeRouterViewControllerType {
60 | case normal(type: AnyClass)
61 | case nib(type: AnyClass, name: String)
62 | case storyboard(type: AnyClass, name: String, identifier: String)
63 | }
64 |
65 | // 视图显示方式
66 | private enum CRNativeRouterViewPresentType {
67 | case show
68 | case showDetail
69 | case presentModally
70 | case presentAsPopover
71 | }
72 |
73 | private enum CRNativeRouterKey: String {
74 | case module = "CRNativeRouterModuleKey"
75 | case parameters = "CRNativeRouterParametersKey"
76 | }
77 |
78 | // 映射关系
79 | private var mapClass: [String: CRNativeRouterViewControllerType] = [:]
80 | private var mapParameters: [String: [String]] = [:]
81 |
82 | // 预设的URL匹配正则表达式
83 | private var regularFormat = "^(Module://)(\\w+\\.md)(\\?(([a-zA-Z]+\\w*=\\w+)(&[a-zA-Z]+\\w*=\\w+)*)|([a-zA-Z]+\\w*=\\w+))?$"
84 |
85 | // 单例
86 | public static let shared = CRNativeRouter()
87 |
88 | @available(iOS, deprecated, message: "Use shared instead")
89 | @objc public class func sharedInstance() -> CRNativeRouter! {
90 | return shared
91 | }
92 |
93 | /**
94 | 分离URL中的模块名称和参数队列
95 |
96 | - parameter url: URL
97 |
98 | - returns: 分离后的字典数据
99 | */
100 | private func divideComponentsFromUrl(_ url: String) -> [CRNativeRouterKey: String] {
101 | var compResult: [CRNativeRouterKey: String] = [:]
102 |
103 | do {
104 | // 分离模块名称
105 | var regularExpression = try NSRegularExpression(pattern: "://\\w+\\.md", options: [])
106 | var components = regularExpression.matches(in: url, options: .reportCompletion, range: NSMakeRange(0, url.count))
107 |
108 | if components.count > 0 {
109 | let tempRange = components[0].range
110 | compResult[.module] = String(url[url.index(url.startIndex, offsetBy: tempRange.location + 3) ..< url.index(url.startIndex, offsetBy: tempRange.location + tempRange.length)])
111 | }
112 |
113 | // 分离参数
114 | regularExpression = try NSRegularExpression(pattern: "\\?[\\w|&|=]*$", options: [])
115 | components = regularExpression.matches(in: url, options: .reportCompletion, range: NSMakeRange(0, url.count))
116 |
117 | if components.count > 0 {
118 | let tempRange = components[0].range
119 | compResult[.parameters] = String(url[url.index(url.startIndex, offsetBy: tempRange.location + 1) ..< url.index(url.startIndex, offsetBy: tempRange.location + tempRange.length)])
120 | }
121 | } catch {
122 | // exception catched
123 | }
124 |
125 | return compResult
126 | }
127 |
128 | /**
129 | 通过module名称映射到对应的视图控制器
130 |
131 | - parameter module: module名称
132 |
133 | - returns: 对应的视图控制器
134 | */
135 | private func reflectViewController(_ module: String) -> UIViewController? {
136 | guard let type = mapClass[module] else { return nil }
137 |
138 | var viewController: UIViewController? = nil
139 |
140 | switch type {
141 | case .normal(let vcType):
142 | viewController = (vcType as! UIViewController.Type).init()
143 | case .nib(let vcType, let nib):
144 | viewController = (vcType as! UIViewController.Type).init(nibName: nib, bundle: nil)
145 | case .storyboard(_, let name, let identifier):
146 | viewController = UIStoryboard(name: name, bundle: nil).instantiateViewController(withIdentifier: identifier)
147 | }
148 |
149 | return viewController
150 | }
151 |
152 | /**
153 | 视图控制器参数校验
154 |
155 | - parameter module: module名称
156 | - parameter parameter: URL中的参数队列字符串
157 |
158 | - returns: 校验结果
159 | */
160 | private func viewControllerParametersCheck(_ module: String, parameter: String, paramDict: [String: Any]? = nil) -> Bool {
161 | guard let requiredList = mapParameters[module], requiredList.count > 0 else { return true }
162 |
163 | let components = parameter.components(separatedBy: "&")
164 | var params = Set()
165 |
166 | components.filter { $0.count > 0 }.forEach { params.insert($0.components(separatedBy: "=")[0]) }
167 |
168 | if let additionalParams = paramDict {
169 | params.formUnion(Set(additionalParams.keys))
170 | }
171 |
172 | return Set(requiredList).intersection(params).count == requiredList.count
173 | }
174 |
175 | /**
176 | 生成视图控制器参数对应的字典数据
177 |
178 | - parameter parameter: 参数队列字符串
179 |
180 | - returns: 参数字典数据
181 | */
182 | private func viewControllerParameterGenerate(_ parameter: String, paramDict: [String: Any]? = nil) -> [String: Any] {
183 | guard parameter != "" else { return paramDict ?? [:] }
184 |
185 | let components = parameter.components(separatedBy: "&")
186 | var params: [String: Any] = [:]
187 |
188 | components.forEach { item in
189 | let refs = item.components(separatedBy: "=")
190 |
191 | if let intValue = Int(refs[1]), "\(intValue)" == refs[1] {
192 | params[refs[0]] = intValue
193 | } else if let doubleValue = Double(refs[1]), "\(doubleValue)" == refs[1] {
194 | params[refs[0]] = doubleValue
195 | } else {
196 | params[refs[0]] = refs[1]
197 | }
198 | }
199 |
200 | if let additionalParam = paramDict {
201 | params.merge(additionalParam) { (_, new) -> Any in new }
202 | }
203 |
204 | return params
205 | }
206 |
207 | /**
208 | 返回URL对应的视图控制器,并且完成对应的参数初始化
209 |
210 | - parameter url: URL
211 |
212 | - returns: 视图控制器
213 | */
214 | private func configureModule(_ url: String, parameters: [String: Any]? = nil) -> UIViewController? {
215 | guard url ~= regularFormat else { return nil }
216 |
217 | let components = divideComponentsFromUrl(url)
218 | guard let module = components[.module] else { return nil }
219 | let parameterStr = components[.parameters] ?? ""
220 |
221 | guard viewControllerParametersCheck(module, parameter: parameterStr, paramDict: parameters), let viewController = reflectViewController(module) else { return nil }
222 |
223 | if let p = mapParameters[module], p.count > 0 && !(viewController is CRNativeRouterProtocol || viewController is CRNativeRouterDelegate) {
224 | return nil
225 | }
226 |
227 | let paramDict = viewControllerParameterGenerate(parameterStr, paramDict: parameters)
228 | if viewController is CRNativeRouterDelegate {
229 | (viewController as! CRNativeRouterDelegate).getParameters(from: self, parameters: CRNativeRouterParam(dict: paramDict))
230 | } else if viewController is CRNativeRouterProtocol {
231 | (viewController as! CRNativeRouterProtocol).getParametersFromRouter(paramDict)
232 | }
233 |
234 | return viewController
235 | }
236 |
237 | /**
238 | 获取当前显示的视图控制器
239 |
240 | - returns: 当前显示的视图控制器
241 | */
242 | public func currentViewController() -> UIViewController? {
243 | guard let rootViewController = UIApplication.shared.keyWindow?.rootViewController else { return nil }
244 |
245 | return recursionTopViewController(rootViewController)
246 | }
247 |
248 | /**
249 | 递归查找显示的视图控制器
250 |
251 | - parameter rootViewController: 开始查找的视图控制器结点
252 |
253 | - returns: 视图控制器
254 | */
255 | private func recursionTopViewController(_ rootViewController: UIViewController) -> UIViewController {
256 | if let navigationController = rootViewController as? UINavigationController, let topViewController = navigationController.topViewController {
257 | return recursionTopViewController(topViewController)
258 | } else if let tabBarController = rootViewController as? UITabBarController {
259 | if let viewControllers = tabBarController.viewControllers {
260 | return recursionTopViewController(viewControllers[tabBarController.selectedIndex])
261 | } else {
262 | return tabBarController
263 | }
264 | }
265 |
266 | guard let presentedViewController = rootViewController.presentedViewController else { return rootViewController }
267 |
268 | return recursionTopViewController(presentedViewController)
269 | }
270 |
271 | // MARK: API
272 |
273 | /**
274 | 设置统跳URL格式,以正则表达式表示
275 | 内部使用固定格式正则,暂不提供该接口
276 |
277 | - parameter format: URL格式(正则表达式)
278 | */
279 | public func setURLModifyFormat(_ format: String) {
280 | regularFormat = format
281 | }
282 |
283 | /**
284 | 注册新的视图控制器
285 |
286 | - parameter name: 视图控制器名称
287 | - parameter type: 视图控制器类型
288 | - parameter parameters: 对应的参数名称列表
289 |
290 | - returns: 注册结果
291 | */
292 | @discardableResult
293 | public func registerModule(_ name: String, type: AnyClass, parameters: [String]?) -> Bool {
294 | if type is UIViewController.Type {
295 | mapClass[name] = .normal(type: type)
296 | mapParameters[name] = parameters ?? []
297 |
298 | return true
299 | }
300 |
301 | return false
302 | }
303 |
304 | /**
305 | 注册新的nib视图控制器
306 |
307 | - parameter name: 视图控制器名称
308 | - parameter type: 视图控制器类型
309 | - parameter nib: nib名称
310 | - parameter parameters: 对应的参数名称列表
311 |
312 | - returns: 注册结果
313 | */
314 | @discardableResult
315 | public func registerModule(_ name: String, type: AnyClass, nib: String, parameters: [String]?) -> Bool {
316 | if type is UIViewController.Type {
317 | mapClass[name] = .nib(type: type, name: nib)
318 | mapParameters[name] = parameters ?? []
319 |
320 | return true
321 | }
322 |
323 | return false
324 | }
325 |
326 | /**
327 | 注册新的Storyboard视图控制器
328 |
329 | - parameter name: 视图控制器名称
330 | - parameter type: 视图控制器类型
331 | - parameter storyboard: storyboard名称
332 | - parameter identifier: identifier标识
333 | - parameter parameters: 对应的参数名称列表
334 |
335 | - returns: 注册结果
336 | */
337 | @discardableResult
338 | public func registerModule(_ name: String, type: AnyClass, storyboard: String, identifier: String, parameters: [String]?) -> Bool {
339 | if type is UIViewController.Type {
340 | mapClass[name] = .storyboard(type: type, name: storyboard, identifier: identifier)
341 | mapParameters[name] = parameters ?? []
342 |
343 | return true
344 | }
345 |
346 | return false
347 | }
348 |
349 | /**
350 | 从plist文件注册视图控制器
351 |
352 | - parameter filename: plist文件名称
353 | */
354 | public func registerModules(fromConfiguration configuration: String) {
355 | guard let plistPath = Bundle.main.path(forResource: configuration, ofType: "plist") else { return }
356 |
357 | var modules: [[String: Any]]?
358 | if let dict = NSDictionary(contentsOfFile: plistPath) {
359 | modules = dict["Modules"] as? [[String:Any]]
360 | } else if let array = NSArray(contentsOfFile: plistPath), let arr = array as? [[String: Any]] {
361 | modules = arr
362 | }
363 |
364 | guard let modules, !modules.isEmpty else { return }
365 |
366 | modules.forEach { module in
367 | guard let name = module["name"] as? String else { return }
368 | guard let type = module["type"] as? String else { return }
369 |
370 | guard let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String else { return }
371 | guard let className = NSClassFromString(namespace + "." + type) ?? NSClassFromString(type) else { return }
372 |
373 | let parameters = module["parameters"] as? [String]
374 |
375 | if let storyboard = module["storyboard"] as? String { // storyboard
376 | guard let identifier = module["identifier"] as? String else { return }
377 |
378 | _ = registerModule(name, type: className, storyboard: storyboard, identifier: identifier, parameters: parameters)
379 | } else if let nib = module["nib"] as? String {
380 | _ = registerModule(name, type: className, nib: nib, parameters: parameters)
381 | } else {
382 | _ = registerModule(name, type: className, parameters: parameters)
383 | }
384 | }
385 | }
386 |
387 | /**
388 | 从plist总文件中获取各个分plist文件,并注册视图控制器
389 |
390 | - parameter filename: plist文件名称
391 | */
392 | public func registerGroupModules(fromConfiguration configuration: String) {
393 | guard let plistPath = Bundle.main.path(forResource: configuration, ofType: "plist") else { return }
394 | guard let groupArray = NSArray(contentsOfFile: plistPath) as? [String] else { return }
395 |
396 | groupArray.forEach { registerModules(fromConfiguration: $0) }
397 | }
398 |
399 | /**
400 | Navigation controller show a new view controller
401 |
402 | - parameter url: URL
403 | - parameter navigationController: navigation controller
404 | - parameter delegate: navigation controller delegate
405 | */
406 | @discardableResult
407 | private func showViewController(_ url: String, parameters: [String: Any]? = nil, pushTo navigation: UINavigationController? = nil, delegate: UINavigationControllerDelegate? = nil, type: CRNativeRouterViewPresentType = .show) -> UIViewController? {
408 | guard let viewController = configureModule(url, parameters: parameters) else { return nil }
409 | guard let navigationController = navigation ?? currentViewController()?.navigationController else { return nil }
410 |
411 | navigationController.delegate = delegate
412 | if type == .showDetail {
413 | navigationController.showDetailViewController(viewController, sender: self)
414 | } else {
415 | navigationController.show(viewController, sender: self)
416 | }
417 |
418 | return viewController
419 | }
420 |
421 | @discardableResult
422 | @objc public func present(_ url: String, parameters: [String: Any]? = nil, from current: UIViewController? = nil, inNavigation: Bool = false, params: CRNativeRouterPresentOptions = .init()) -> UIViewController? {
423 | guard let viewController = configureModule(url, parameters: parameters) else { return nil }
424 | guard let from = current ?? currentViewController() else { return nil }
425 |
426 | let newViewController = inNavigation ? (viewController.navigationController ?? UINavigationController(rootViewController: viewController)) : viewController
427 | newViewController.modalPresentationStyle = params.presentationStyle
428 | newViewController.modalTransitionStyle = params.transitionStyle
429 | from.present(newViewController, animated: true, completion: nil)
430 |
431 | return viewController
432 | }
433 |
434 | @discardableResult
435 | @objc public func popover(_ url: String, parameters: [String: Any]? = nil, from current: UIViewController? = nil, sourceRect: CGRect) -> UIViewController? {
436 | guard let viewController = configureModule(url, parameters: parameters) else { return nil }
437 | guard let from = current ?? currentViewController() else { return nil }
438 | guard let popoverController = from.popoverPresentationController else { return nil }
439 |
440 | viewController.navigationController?.modalPresentationStyle = .popover
441 | viewController.modalPresentationStyle = .popover
442 |
443 | popoverController.sourceView = from.view
444 | popoverController.sourceRect = sourceRect
445 | from.present(viewController.navigationController ?? viewController, animated: true, completion: nil)
446 |
447 | return viewController
448 | }
449 |
450 | @discardableResult
451 | @objc public func show(_ url: String, parameters: [String: Any]? = nil, navigation: UINavigationController? = nil, delegate: UINavigationControllerDelegate? = nil) -> UIViewController? {
452 | return showViewController(url, parameters: parameters, pushTo: navigation, delegate: delegate)
453 | }
454 |
455 | @discardableResult
456 | @objc public func showDetail(_ url: String, parameters: [String: Any]? = nil, navigation: UINavigationController? = nil, delegate: UINavigationControllerDelegate? = nil) -> UIViewController? {
457 | return showViewController(url, parameters: parameters, pushTo: navigation, delegate: delegate, type: .showDetail)
458 | }
459 |
460 | /**
461 | Navigation controller show a new view controller
462 |
463 | - parameter url: URL
464 | - parameter navigationController: navigation controller
465 | */
466 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
467 | @discardableResult
468 | @objc public func navigationControllerShowViewController(_ url: String, navigationController: UINavigationController?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
469 | return show(url, navigation: navigationController, delegate: delegate)
470 | }
471 |
472 | /**
473 | Navigation controller show a new view controller
474 |
475 | - parameter url: URL
476 | - parameter parameters: additional parameters
477 | - parameter navigationController: navigation controller
478 | */
479 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
480 | @discardableResult
481 | @objc public func navigationControllerShowViewController(_ url: String, parameters: [String: Any], navigationController: UINavigationController, delegate: UINavigationControllerDelegate?) -> UIViewController? {
482 | return show(url, parameters: parameters, navigation: navigationController, delegate: delegate)
483 | }
484 |
485 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
486 | @discardableResult
487 | @objc public func navigationControllerShowViewController(_ url: String, parameters: [String: Any], navigationController: UINavigationController?) -> UIViewController? {
488 | return show(url, parameters: parameters, navigation: navigationController)
489 | }
490 |
491 | /**
492 | Navigation controller show a new view controller
493 |
494 | - parameter url: URL
495 | - parameter parameters: additional parameters
496 | */
497 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
498 | @discardableResult
499 | @objc public func showViewController(_ url: String, parameters: [String: Any]?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
500 | return show(url, parameters: parameters, delegate: delegate)
501 | }
502 |
503 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
504 | @discardableResult
505 | @objc public func showViewController(_ url: String, parameters: [String: Any]?) -> UIViewController? {
506 | return show(url, parameters: parameters)
507 | }
508 |
509 | /**
510 | Navigation controller show detail a new view controller
511 |
512 | - parameter url: URL
513 | - parameter navigationController: navigation controller
514 | */
515 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
516 | @discardableResult
517 | @objc public func navigationControllerShowDetailViewController(_ url: String, navigationController: UINavigationController?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
518 | return showDetail(url, navigation: navigationController, delegate: delegate)
519 | }
520 |
521 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
522 | @discardableResult
523 | @objc public func navigationControllerShowDetailViewController(_ url: String, navigationController: UINavigationController?) -> UIViewController? {
524 | return showDetail(url, navigation: navigationController)
525 | }
526 |
527 | /**
528 | Navigation controller show detail a new view controller
529 |
530 | - parameter url: URL
531 | - parameter parameters: additional parameters
532 | - parameter navigationController: navigation controller
533 | */
534 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
535 | @discardableResult
536 | @objc public func navigationControllerShowDetailViewController(_ url: String, parameters: [String: Any], navigationController: UINavigationController?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
537 | return showDetail(url, parameters: parameters, navigation: navigationController, delegate: delegate)
538 | }
539 |
540 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
541 | @discardableResult
542 | @objc public func navigationControllerShowDetailViewController(_ url: String, parameters: [String: Any], navigationController: UINavigationController?) -> UIViewController? {
543 | return showDetail(url, parameters: parameters, navigation: navigationController)
544 | }
545 |
546 | /**
547 | Navigation controller show detail a new view controller
548 |
549 | - parameter url: URL
550 | - parameter parameters: additional parameters
551 | */
552 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
553 | @discardableResult
554 | @objc public func showDetailViewController(_ url: String, parameters: [String: Any]?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
555 | return showDetail(url, parameters: parameters, delegate: delegate)
556 | }
557 |
558 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
559 | @discardableResult
560 | @objc public func showDetailViewController(_ url: String, parameters: [String: Any]? = nil) -> UIViewController? {
561 | return showDetail(url, parameters: parameters)
562 | }
563 |
564 | /**
565 | Show a view controller modally
566 |
567 | - parameter url: URL
568 | - parameter viewController: view controller where new one show from
569 | */
570 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
571 | @discardableResult
572 | @objc public func showModallyViewController(_ url: String, fromViewController viewController: UIViewController) -> UIViewController? {
573 | return present(url, from: viewController)
574 | }
575 |
576 |
577 | /// Show a view controller within navigation modally
578 | ///
579 | /// - Parameters:
580 | /// - url: URL
581 | /// - viewController: view controller where new one show from
582 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
583 | @discardableResult
584 | @objc public func showModallyViewControllerInNavigation(_ url: String, fromViewController viewController: UIViewController) -> UIViewController? {
585 | return present(url, from: viewController, inNavigation: true)
586 | }
587 |
588 | /**
589 | Show a view controller modally
590 |
591 | - parameter url: URL
592 | - parameter viewController: view controller where new one show from
593 | - parameter parameters: additional parameters
594 | */
595 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
596 | @discardableResult
597 | @objc public func showModallyViewController(_ url: String, fromViewController viewController: UIViewController, parameters: [String: Any]) -> UIViewController? {
598 | return present(url, parameters: parameters, from: viewController)
599 | }
600 |
601 |
602 | /// Show a view controller within navigation modally
603 | ///
604 | /// - Parameters:
605 | /// - url: URL
606 | /// - viewController: view controller where new one show from
607 | /// - parameters: additional parameters
608 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
609 | @discardableResult
610 | @objc public func showModallyViewControllerInNavigation(_ url: String, fromViewController viewController: UIViewController, parameters: [String: Any]) -> UIViewController? {
611 | return present(url, parameters: parameters, from: viewController, inNavigation: true)
612 | }
613 |
614 | /**
615 | Show a view controller modally
616 |
617 | - parameter url: URL
618 | - parameter parameters: additional parameters
619 | */
620 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
621 | @discardableResult
622 | @objc public func showModallyViewController(_ url: String, parameters: [String: Any]? = nil) -> UIViewController? {
623 | return present(url, parameters: parameters)
624 | }
625 |
626 | /// Show a view controller within navigation modally
627 | ///
628 | /// - Parameters:
629 | /// - url: URL
630 | /// - parameters: additional parameters
631 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
632 | @discardableResult
633 | @objc public func showModallyViewControllerInNavigation(_ url: String, parameters: [String: Any]? = nil) -> UIViewController? {
634 | return present(url, parameters: parameters, inNavigation: true)
635 | }
636 |
637 | /**
638 | Pop over a new view controller
639 |
640 | - parameter url: URL
641 | - parameter viewController: view controller where new one show from
642 | - parameter sourceRect: source area rect
643 | */
644 | @available(iOS, deprecated: 8.0, message: "API renamed, use popover instead")
645 | @discardableResult
646 | @objc public func popoverViewController(_ url: String, fromViewController viewController: UIViewController, sourceRect: CGRect) -> UIViewController? {
647 | return popover(url, from: viewController, sourceRect: sourceRect)
648 | }
649 |
650 | /**
651 | Pop over a new view controller
652 |
653 | - parameter url: URL
654 | - parameter viewController: view controller where new one show from
655 | - parameter parameters: additional parameters
656 | - parameter sourceRect: source area rect
657 | */
658 | @available(iOS, deprecated: 8.0, message: "API renamed, use popover instead")
659 | @discardableResult
660 | @objc public func popoverViewController(_ url: String, fromViewController viewController: UIViewController, parameters: [String: Any], sourceRect: CGRect) -> UIViewController? {
661 | return popover(url, parameters: parameters, from: viewController, sourceRect: sourceRect)
662 | }
663 |
664 | /**
665 | Pop over a new view controller
666 |
667 | - parameter url: URL
668 | - parameter sourceRect: source area rect
669 | - parameter parameters: additional parameters
670 | */
671 | @available(iOS, deprecated: 8.0, message: "API renamed, use popover instead")
672 | @discardableResult
673 | @objc public func popoverViewController(_ url: String, sourceRect: CGRect, parameters: [String: Any]? = nil) -> UIViewController? {
674 | return popover(url, parameters: parameters, sourceRect: sourceRect)
675 | }
676 | }
677 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/CRNativeRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CRNativeRouter.swift
3 | // CRNativeRouter
4 | //
5 | // Created by CrashRain on 16/7/1.
6 | // Copyright © 2016年 CrashRain. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | private func ~= (lhs: String, rhs: String) -> Bool {
12 | if let result = ((try? NSRegularExpression(pattern: rhs, options: .caseInsensitive).firstMatch(in: lhs, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSRange(location: 0, length: lhs.count))) as NSTextCheckingResult??) {
13 | return result != nil
14 | }
15 |
16 | return false
17 | }
18 |
19 | @objc public class CRNativeRouterPresentOptions: NSObject {
20 | var presentationStyle = UIModalPresentationStyle.overCurrentContext
21 | var transitionStyle = UIModalTransitionStyle.coverVertical
22 | }
23 |
24 | @dynamicMemberLookup
25 | public struct CRNativeRouterParamT where Key: Hashable & ExpressibleByStringLiteral {
26 | var wrappedValue: [Key: Value] = [:]
27 |
28 | init(dict: [Key: Value]) {
29 | wrappedValue = dict
30 | }
31 |
32 | subscript(key: Key) -> Value? {
33 | get {
34 | return wrappedValue[key]
35 | }
36 | set {
37 | wrappedValue[key] = newValue
38 | }
39 | }
40 |
41 | subscript(dynamicMember key: Key) -> Value? {
42 | return wrappedValue[key]
43 | }
44 | }
45 |
46 | public typealias CRNativeRouterParam = CRNativeRouterParamT
47 |
48 | public protocol CRNativeRouterProtocol: NSObjectProtocol {
49 | func getParametersFromRouter(_ parameter: [String: Any])
50 | }
51 |
52 | public protocol CRNativeRouterDelegate {
53 | func getParameters(from router: CRNativeRouter, parameters: CRNativeRouterParam)
54 | }
55 |
56 | public class CRNativeRouter: NSObject {
57 |
58 | // 视图控制器类型枚举
59 | private enum CRNativeRouterViewControllerType {
60 | case normal(type: AnyClass)
61 | case nib(type: AnyClass, name: String)
62 | case storyboard(type: AnyClass, name: String, identifier: String)
63 | }
64 |
65 | // 视图显示方式
66 | private enum CRNativeRouterViewPresentType {
67 | case show
68 | case showDetail
69 | case presentModally
70 | case presentAsPopover
71 | }
72 |
73 | private enum CRNativeRouterKey: String {
74 | case module = "CRNativeRouterModuleKey"
75 | case parameters = "CRNativeRouterParametersKey"
76 | }
77 |
78 | // 映射关系
79 | private var mapClass: [String: CRNativeRouterViewControllerType] = [:]
80 | private var mapParameters: [String: [String]] = [:]
81 |
82 | // 预设的URL匹配正则表达式
83 | private var regularFormat = "^(Module://)(\\w+\\.md)(\\?(([a-zA-Z]+\\w*=\\w+)(&[a-zA-Z]+\\w*=\\w+)*)|([a-zA-Z]+\\w*=\\w+))?$"
84 |
85 | // 单例
86 | public static let shared = CRNativeRouter()
87 |
88 | @available(iOS, deprecated, message: "Use shared instead")
89 | @objc public class func sharedInstance() -> CRNativeRouter! {
90 | return shared
91 | }
92 |
93 | /**
94 | 分离URL中的模块名称和参数队列
95 |
96 | - parameter url: URL
97 |
98 | - returns: 分离后的字典数据
99 | */
100 | private func divideComponentsFromUrl(_ url: String) -> [CRNativeRouterKey: String] {
101 | var compResult: [CRNativeRouterKey: String] = [:]
102 |
103 | do {
104 | // 分离模块名称
105 | var regularExpression = try NSRegularExpression(pattern: "://\\w+\\.md", options: [])
106 | var components = regularExpression.matches(in: url, options: .reportCompletion, range: NSMakeRange(0, url.count))
107 |
108 | if components.count > 0 {
109 | let tempRange = components[0].range
110 | compResult[.module] = String(url[url.index(url.startIndex, offsetBy: tempRange.location + 3) ..< url.index(url.startIndex, offsetBy: tempRange.location + tempRange.length)])
111 | }
112 |
113 | // 分离参数
114 | regularExpression = try NSRegularExpression(pattern: "\\?[\\w|&|=]*$", options: [])
115 | components = regularExpression.matches(in: url, options: .reportCompletion, range: NSMakeRange(0, url.count))
116 |
117 | if components.count > 0 {
118 | let tempRange = components[0].range
119 | compResult[.parameters] = String(url[url.index(url.startIndex, offsetBy: tempRange.location + 1) ..< url.index(url.startIndex, offsetBy: tempRange.location + tempRange.length)])
120 | }
121 | } catch {
122 | // exception catched
123 | }
124 |
125 | return compResult
126 | }
127 |
128 | /**
129 | 通过module名称映射到对应的视图控制器
130 |
131 | - parameter module: module名称
132 |
133 | - returns: 对应的视图控制器
134 | */
135 | private func reflectViewController(_ module: String) -> UIViewController? {
136 | guard let type = mapClass[module] else { return nil }
137 |
138 | var viewController: UIViewController? = nil
139 |
140 | switch type {
141 | case .normal(let vcType):
142 | viewController = (vcType as! UIViewController.Type).init()
143 | case .nib(let vcType, let nib):
144 | viewController = (vcType as! UIViewController.Type).init(nibName: nib, bundle: nil)
145 | case .storyboard(_, let name, let identifier):
146 | viewController = UIStoryboard(name: name, bundle: nil).instantiateViewController(withIdentifier: identifier)
147 | }
148 |
149 | return viewController
150 | }
151 |
152 | /**
153 | 视图控制器参数校验
154 |
155 | - parameter module: module名称
156 | - parameter parameter: URL中的参数队列字符串
157 |
158 | - returns: 校验结果
159 | */
160 | private func viewControllerParametersCheck(_ module: String, parameter: String, paramDict: [String: Any]? = nil) -> Bool {
161 | guard let requiredList = mapParameters[module], requiredList.count > 0 else { return true }
162 |
163 | let components = parameter.components(separatedBy: "&")
164 | var params = Set()
165 |
166 | components.filter { $0.count > 0 }.forEach { params.insert($0.components(separatedBy: "=")[0]) }
167 |
168 | if let additionalParams = paramDict {
169 | params.formUnion(Set(additionalParams.keys))
170 | }
171 |
172 | return Set(requiredList).intersection(params).count == requiredList.count
173 | }
174 |
175 | /**
176 | 生成视图控制器参数对应的字典数据
177 |
178 | - parameter parameter: 参数队列字符串
179 |
180 | - returns: 参数字典数据
181 | */
182 | private func viewControllerParameterGenerate(_ parameter: String, paramDict: [String: Any]? = nil) -> [String: Any] {
183 | guard parameter != "" else { return paramDict ?? [:] }
184 |
185 | let components = parameter.components(separatedBy: "&")
186 | var params: [String: Any] = [:]
187 |
188 | components.forEach { item in
189 | let refs = item.components(separatedBy: "=")
190 |
191 | if let intValue = Int(refs[1]), "\(intValue)" == refs[1] {
192 | params[refs[0]] = intValue
193 | } else if let doubleValue = Double(refs[1]), "\(doubleValue)" == refs[1] {
194 | params[refs[0]] = doubleValue
195 | } else {
196 | params[refs[0]] = refs[1]
197 | }
198 | }
199 |
200 | if let additionalParam = paramDict {
201 | params.merge(additionalParam) { (_, new) -> Any in new }
202 | }
203 |
204 | return params
205 | }
206 |
207 | /**
208 | 返回URL对应的视图控制器,并且完成对应的参数初始化
209 |
210 | - parameter url: URL
211 |
212 | - returns: 视图控制器
213 | */
214 | private func configureModule(_ url: String, parameters: [String: Any]? = nil) -> UIViewController? {
215 | guard url ~= regularFormat else { return nil }
216 |
217 | let components = divideComponentsFromUrl(url)
218 | guard let module = components[.module] else { return nil }
219 | let parameterStr = components[.parameters] ?? ""
220 |
221 | guard viewControllerParametersCheck(module, parameter: parameterStr, paramDict: parameters), let viewController = reflectViewController(module) else { return nil }
222 |
223 | if let p = mapParameters[module], p.count > 0 && !(viewController is CRNativeRouterProtocol || viewController is CRNativeRouterDelegate) {
224 | return nil
225 | }
226 |
227 | let paramDict = viewControllerParameterGenerate(parameterStr, paramDict: parameters)
228 | if viewController is CRNativeRouterDelegate {
229 | (viewController as! CRNativeRouterDelegate).getParameters(from: self, parameters: CRNativeRouterParam(dict: paramDict))
230 | } else if viewController is CRNativeRouterProtocol {
231 | (viewController as! CRNativeRouterProtocol).getParametersFromRouter(paramDict)
232 | }
233 |
234 | return viewController
235 | }
236 |
237 | /**
238 | 获取当前显示的视图控制器
239 |
240 | - returns: 当前显示的视图控制器
241 | */
242 | public func currentViewController() -> UIViewController? {
243 | guard let rootViewController = UIApplication.shared.keyWindow?.rootViewController else { return nil }
244 |
245 | return recursionTopViewController(rootViewController)
246 | }
247 |
248 | /**
249 | 递归查找显示的视图控制器
250 |
251 | - parameter rootViewController: 开始查找的视图控制器结点
252 |
253 | - returns: 视图控制器
254 | */
255 | private func recursionTopViewController(_ rootViewController: UIViewController) -> UIViewController {
256 | if let navigationController = rootViewController as? UINavigationController, let topViewController = navigationController.topViewController {
257 | return recursionTopViewController(topViewController)
258 | } else if let tabBarController = rootViewController as? UITabBarController {
259 | if let viewControllers = tabBarController.viewControllers {
260 | return recursionTopViewController(viewControllers[tabBarController.selectedIndex])
261 | } else {
262 | return tabBarController
263 | }
264 | }
265 |
266 | guard let presentedViewController = rootViewController.presentedViewController else { return rootViewController }
267 |
268 | return recursionTopViewController(presentedViewController)
269 | }
270 |
271 | // MARK: API
272 |
273 | /**
274 | 设置统跳URL格式,以正则表达式表示
275 | 内部使用固定格式正则,暂不提供该接口
276 |
277 | - parameter format: URL格式(正则表达式)
278 | */
279 | public func setURLModifyFormat(_ format: String) {
280 | regularFormat = format
281 | }
282 |
283 | /**
284 | 注册新的视图控制器
285 |
286 | - parameter name: 视图控制器名称
287 | - parameter type: 视图控制器类型
288 | - parameter parameters: 对应的参数名称列表
289 |
290 | - returns: 注册结果
291 | */
292 | @discardableResult
293 | public func registerModule(_ name: String, type: AnyClass, parameters: [String]?) -> Bool {
294 | if type is UIViewController.Type {
295 | mapClass[name] = .normal(type: type)
296 | mapParameters[name] = parameters ?? []
297 |
298 | return true
299 | }
300 |
301 | return false
302 | }
303 |
304 | /**
305 | 注册新的nib视图控制器
306 |
307 | - parameter name: 视图控制器名称
308 | - parameter type: 视图控制器类型
309 | - parameter nib: nib名称
310 | - parameter parameters: 对应的参数名称列表
311 |
312 | - returns: 注册结果
313 | */
314 | @discardableResult
315 | public func registerModule(_ name: String, type: AnyClass, nib: String, parameters: [String]?) -> Bool {
316 | if type is UIViewController.Type {
317 | mapClass[name] = .nib(type: type, name: nib)
318 | mapParameters[name] = parameters ?? []
319 |
320 | return true
321 | }
322 |
323 | return false
324 | }
325 |
326 | /**
327 | 注册新的Storyboard视图控制器
328 |
329 | - parameter name: 视图控制器名称
330 | - parameter type: 视图控制器类型
331 | - parameter storyboard: storyboard名称
332 | - parameter identifier: identifier标识
333 | - parameter parameters: 对应的参数名称列表
334 |
335 | - returns: 注册结果
336 | */
337 | @discardableResult
338 | public func registerModule(_ name: String, type: AnyClass, storyboard: String, identifier: String, parameters: [String]?) -> Bool {
339 | if type is UIViewController.Type {
340 | mapClass[name] = .storyboard(type: type, name: storyboard, identifier: identifier)
341 | mapParameters[name] = parameters ?? []
342 |
343 | return true
344 | }
345 |
346 | return false
347 | }
348 |
349 | /**
350 | 从plist文件注册视图控制器
351 |
352 | - parameter filename: plist文件名称
353 | */
354 | public func registerModules(fromConfiguration configuration: String) {
355 | guard let plistPath = Bundle.main.path(forResource: configuration, ofType: "plist") else { return }
356 |
357 | var modules: [[String: Any]]?
358 | if let dict = NSDictionary(contentsOfFile: plistPath) {
359 | modules = dict["Modules"] as? [[String:Any]]
360 | } else if let array = NSArray(contentsOfFile: plistPath), let arr = array as? [[String: Any]] {
361 | modules = arr
362 | }
363 |
364 | guard let modules, !modules.isEmpty else { return }
365 |
366 | modules.forEach { module in
367 | guard let name = module["name"] as? String else { return }
368 | guard let type = module["type"] as? String else { return }
369 |
370 | guard let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String else { return }
371 | guard let className = NSClassFromString(namespace + "." + type) ?? NSClassFromString(type) else { return }
372 |
373 | let parameters = module["parameters"] as? [String]
374 |
375 | if let storyboard = module["storyboard"] as? String { // storyboard
376 | guard let identifier = module["identifier"] as? String else { return }
377 |
378 | _ = registerModule(name, type: className, storyboard: storyboard, identifier: identifier, parameters: parameters)
379 | } else if let nib = module["nib"] as? String {
380 | _ = registerModule(name, type: className, nib: nib, parameters: parameters)
381 | } else {
382 | _ = registerModule(name, type: className, parameters: parameters)
383 | }
384 | }
385 | }
386 |
387 | /**
388 | 从plist总文件中获取各个分plist文件,并注册视图控制器
389 |
390 | - parameter filename: plist文件名称
391 | */
392 | public func registerGroupModules(fromConfiguration configuration: String) {
393 | guard let plistPath = Bundle.main.path(forResource: configuration, ofType: "plist") else { return }
394 | guard let groupArray = NSArray(contentsOfFile: plistPath) as? [String] else { return }
395 |
396 | groupArray.forEach { registerModules(fromConfiguration: $0) }
397 | }
398 |
399 | /**
400 | Navigation controller show a new view controller
401 |
402 | - parameter url: URL
403 | - parameter navigationController: navigation controller
404 | - parameter delegate: navigation controller delegate
405 | */
406 | @discardableResult
407 | private func showViewController(_ url: String, parameters: [String: Any]? = nil, pushTo navigation: UINavigationController? = nil, delegate: UINavigationControllerDelegate? = nil, type: CRNativeRouterViewPresentType = .show) -> UIViewController? {
408 | guard let viewController = configureModule(url, parameters: parameters) else { return nil }
409 | guard let navigationController = navigation ?? currentViewController()?.navigationController else { return nil }
410 |
411 | navigationController.delegate = delegate
412 | if type == .showDetail {
413 | navigationController.showDetailViewController(viewController, sender: self)
414 | } else {
415 | navigationController.show(viewController, sender: self)
416 | }
417 |
418 | return viewController
419 | }
420 |
421 | @discardableResult
422 | @objc public func present(_ url: String, parameters: [String: Any]? = nil, from current: UIViewController? = nil, inNavigation: Bool = false, params: CRNativeRouterPresentOptions = .init()) -> UIViewController? {
423 | guard let viewController = configureModule(url, parameters: parameters) else { return nil }
424 | guard let from = current ?? currentViewController() else { return nil }
425 |
426 | let newViewController = inNavigation ? (viewController.navigationController ?? UINavigationController(rootViewController: viewController)) : viewController
427 | newViewController.modalPresentationStyle = params.presentationStyle
428 | newViewController.modalTransitionStyle = params.transitionStyle
429 | from.present(newViewController, animated: true, completion: nil)
430 |
431 | return viewController
432 | }
433 |
434 | @discardableResult
435 | @objc public func popover(_ url: String, parameters: [String: Any]? = nil, from current: UIViewController? = nil, sourceRect: CGRect) -> UIViewController? {
436 | guard let viewController = configureModule(url, parameters: parameters) else { return nil }
437 | guard let from = current ?? currentViewController() else { return nil }
438 | guard let popoverController = from.popoverPresentationController else { return nil }
439 |
440 | viewController.navigationController?.modalPresentationStyle = .popover
441 | viewController.modalPresentationStyle = .popover
442 |
443 | popoverController.sourceView = from.view
444 | popoverController.sourceRect = sourceRect
445 | from.present(viewController.navigationController ?? viewController, animated: true, completion: nil)
446 |
447 | return viewController
448 | }
449 |
450 | @discardableResult
451 | @objc public func show(_ url: String, parameters: [String: Any]? = nil, navigation: UINavigationController? = nil, delegate: UINavigationControllerDelegate? = nil) -> UIViewController? {
452 | return showViewController(url, parameters: parameters, pushTo: navigation, delegate: delegate)
453 | }
454 |
455 | @discardableResult
456 | @objc public func showDetail(_ url: String, parameters: [String: Any]? = nil, navigation: UINavigationController? = nil, delegate: UINavigationControllerDelegate? = nil) -> UIViewController? {
457 | return showViewController(url, parameters: parameters, pushTo: navigation, delegate: delegate, type: .showDetail)
458 | }
459 |
460 | /**
461 | Navigation controller show a new view controller
462 |
463 | - parameter url: URL
464 | - parameter navigationController: navigation controller
465 | */
466 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
467 | @discardableResult
468 | @objc public func navigationControllerShowViewController(_ url: String, navigationController: UINavigationController?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
469 | return show(url, navigation: navigationController, delegate: delegate)
470 | }
471 |
472 | /**
473 | Navigation controller show a new view controller
474 |
475 | - parameter url: URL
476 | - parameter parameters: additional parameters
477 | - parameter navigationController: navigation controller
478 | */
479 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
480 | @discardableResult
481 | @objc public func navigationControllerShowViewController(_ url: String, parameters: [String: Any], navigationController: UINavigationController, delegate: UINavigationControllerDelegate?) -> UIViewController? {
482 | return show(url, parameters: parameters, navigation: navigationController, delegate: delegate)
483 | }
484 |
485 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
486 | @discardableResult
487 | @objc public func navigationControllerShowViewController(_ url: String, parameters: [String: Any], navigationController: UINavigationController?) -> UIViewController? {
488 | return show(url, parameters: parameters, navigation: navigationController)
489 | }
490 |
491 | /**
492 | Navigation controller show a new view controller
493 |
494 | - parameter url: URL
495 | - parameter parameters: additional parameters
496 | */
497 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
498 | @discardableResult
499 | @objc public func showViewController(_ url: String, parameters: [String: Any]?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
500 | return show(url, parameters: parameters, delegate: delegate)
501 | }
502 |
503 | @available(iOS, deprecated: 8.0, message: "API renamed, use show instead")
504 | @discardableResult
505 | @objc public func showViewController(_ url: String, parameters: [String: Any]?) -> UIViewController? {
506 | return show(url, parameters: parameters)
507 | }
508 |
509 | /**
510 | Navigation controller show detail a new view controller
511 |
512 | - parameter url: URL
513 | - parameter navigationController: navigation controller
514 | */
515 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
516 | @discardableResult
517 | @objc public func navigationControllerShowDetailViewController(_ url: String, navigationController: UINavigationController?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
518 | return showDetail(url, navigation: navigationController, delegate: delegate)
519 | }
520 |
521 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
522 | @discardableResult
523 | @objc public func navigationControllerShowDetailViewController(_ url: String, navigationController: UINavigationController?) -> UIViewController? {
524 | return showDetail(url, navigation: navigationController)
525 | }
526 |
527 | /**
528 | Navigation controller show detail a new view controller
529 |
530 | - parameter url: URL
531 | - parameter parameters: additional parameters
532 | - parameter navigationController: navigation controller
533 | */
534 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
535 | @discardableResult
536 | @objc public func navigationControllerShowDetailViewController(_ url: String, parameters: [String: Any], navigationController: UINavigationController?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
537 | return showDetail(url, parameters: parameters, navigation: navigationController, delegate: delegate)
538 | }
539 |
540 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
541 | @discardableResult
542 | @objc public func navigationControllerShowDetailViewController(_ url: String, parameters: [String: Any], navigationController: UINavigationController?) -> UIViewController? {
543 | return showDetail(url, parameters: parameters, navigation: navigationController)
544 | }
545 |
546 | /**
547 | Navigation controller show detail a new view controller
548 |
549 | - parameter url: URL
550 | - parameter parameters: additional parameters
551 | */
552 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
553 | @discardableResult
554 | @objc public func showDetailViewController(_ url: String, parameters: [String: Any]?, delegate: UINavigationControllerDelegate?) -> UIViewController? {
555 | return showDetail(url, parameters: parameters, delegate: delegate)
556 | }
557 |
558 | @available(iOS, deprecated: 8.0, message: "API renamed, use showDetail instead")
559 | @discardableResult
560 | @objc public func showDetailViewController(_ url: String, parameters: [String: Any]? = nil) -> UIViewController? {
561 | return showDetail(url, parameters: parameters)
562 | }
563 |
564 | /**
565 | Show a view controller modally
566 |
567 | - parameter url: URL
568 | - parameter viewController: view controller where new one show from
569 | */
570 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
571 | @discardableResult
572 | @objc public func showModallyViewController(_ url: String, fromViewController viewController: UIViewController) -> UIViewController? {
573 | return present(url, from: viewController)
574 | }
575 |
576 |
577 | /// Show a view controller within navigation modally
578 | ///
579 | /// - Parameters:
580 | /// - url: URL
581 | /// - viewController: view controller where new one show from
582 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
583 | @discardableResult
584 | @objc public func showModallyViewControllerInNavigation(_ url: String, fromViewController viewController: UIViewController) -> UIViewController? {
585 | return present(url, from: viewController, inNavigation: true)
586 | }
587 |
588 | /**
589 | Show a view controller modally
590 |
591 | - parameter url: URL
592 | - parameter viewController: view controller where new one show from
593 | - parameter parameters: additional parameters
594 | */
595 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
596 | @discardableResult
597 | @objc public func showModallyViewController(_ url: String, fromViewController viewController: UIViewController, parameters: [String: Any]) -> UIViewController? {
598 | return present(url, parameters: parameters, from: viewController)
599 | }
600 |
601 |
602 | /// Show a view controller within navigation modally
603 | ///
604 | /// - Parameters:
605 | /// - url: URL
606 | /// - viewController: view controller where new one show from
607 | /// - parameters: additional parameters
608 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
609 | @discardableResult
610 | @objc public func showModallyViewControllerInNavigation(_ url: String, fromViewController viewController: UIViewController, parameters: [String: Any]) -> UIViewController? {
611 | return present(url, parameters: parameters, from: viewController, inNavigation: true)
612 | }
613 |
614 | /**
615 | Show a view controller modally
616 |
617 | - parameter url: URL
618 | - parameter parameters: additional parameters
619 | */
620 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
621 | @discardableResult
622 | @objc public func showModallyViewController(_ url: String, parameters: [String: Any]? = nil) -> UIViewController? {
623 | return present(url, parameters: parameters)
624 | }
625 |
626 | /// Show a view controller within navigation modally
627 | ///
628 | /// - Parameters:
629 | /// - url: URL
630 | /// - parameters: additional parameters
631 | @available(iOS, deprecated: 8.0, message: "API renamed, use present instead")
632 | @discardableResult
633 | @objc public func showModallyViewControllerInNavigation(_ url: String, parameters: [String: Any]? = nil) -> UIViewController? {
634 | return present(url, parameters: parameters, inNavigation: true)
635 | }
636 |
637 | /**
638 | Pop over a new view controller
639 |
640 | - parameter url: URL
641 | - parameter viewController: view controller where new one show from
642 | - parameter sourceRect: source area rect
643 | */
644 | @available(iOS, deprecated: 8.0, message: "API renamed, use popover instead")
645 | @discardableResult
646 | @objc public func popoverViewController(_ url: String, fromViewController viewController: UIViewController, sourceRect: CGRect) -> UIViewController? {
647 | return popover(url, from: viewController, sourceRect: sourceRect)
648 | }
649 |
650 | /**
651 | Pop over a new view controller
652 |
653 | - parameter url: URL
654 | - parameter viewController: view controller where new one show from
655 | - parameter parameters: additional parameters
656 | - parameter sourceRect: source area rect
657 | */
658 | @available(iOS, deprecated: 8.0, message: "API renamed, use popover instead")
659 | @discardableResult
660 | @objc public func popoverViewController(_ url: String, fromViewController viewController: UIViewController, parameters: [String: Any], sourceRect: CGRect) -> UIViewController? {
661 | return popover(url, parameters: parameters, from: viewController, sourceRect: sourceRect)
662 | }
663 |
664 | /**
665 | Pop over a new view controller
666 |
667 | - parameter url: URL
668 | - parameter sourceRect: source area rect
669 | - parameter parameters: additional parameters
670 | */
671 | @available(iOS, deprecated: 8.0, message: "API renamed, use popover instead")
672 | @discardableResult
673 | @objc public func popoverViewController(_ url: String, sourceRect: CGRect, parameters: [String: Any]? = nil) -> UIViewController? {
674 | return popover(url, parameters: parameters, sourceRect: sourceRect)
675 | }
676 | }
677 |
--------------------------------------------------------------------------------
/CRNativeRouterDemo/CRNativeRouter/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
37 |
44 |
51 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
103 |
109 |
115 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
169 |
175 |
181 |
187 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
247 |
253 |
259 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
326 |
332 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
--------------------------------------------------------------------------------