├── .gitignore ├── JCNavigator.podspec ├── JCNavigator ├── JCModuleMap.h ├── JCModuleMap.m ├── JCNavigator.h └── JCNavigator.m ├── JCNavigatorDemo ├── JCNavigatorDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── JCNavigatorDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── icon_back.imageset │ │ ├── Contents.json │ │ ├── icon_back@2x.png │ │ └── icon_back@3x.png │ └── icon_close.imageset │ │ ├── Contents.json │ │ ├── icon_close@2x.png │ │ └── icon_close@3x.png │ ├── Base.lproj │ └── LaunchScreen.storyboard │ ├── Info.plist │ ├── JCNavigationController.h │ ├── JCNavigationController.m │ ├── JCRootModuleMap.h │ ├── JCRootModuleMap.m │ ├── JCRootView.h │ ├── JCRootView.m │ ├── JCTabBarController.h │ ├── JCTabBarController.m │ ├── TestModule │ ├── JCTestModuleMap.h │ ├── JCTestModuleMap.m │ └── Private │ │ ├── JCContentDetailViewController.h │ │ ├── JCContentDetailViewController.m │ │ ├── JCFirstLevelViewController.h │ │ ├── JCFirstLevelViewController.m │ │ ├── JCSecondLevelViewController.h │ │ ├── JCSecondLevelViewController.m │ │ ├── JCTestClass.h │ │ ├── JCTestClass.m │ │ ├── JCTestHeader.h │ │ ├── JCTestView.h │ │ ├── JCTestView.m │ │ ├── JCThirdLevelViewController.h │ │ └── JCThirdLevelViewController.m │ ├── UIViewController+JCNavigationItem.h │ ├── UIViewController+JCNavigationItem.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /JCNavigator.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'JCNavigator' 3 | s.version = '1.0.2' 4 | s.summary = 'A decoupled navigator framework of jumping between modules or apps for iOS development.' 5 | s.homepage = 'https://github.com/imjoych/JCNavigator' 6 | s.author = { 'ChenJianjun' => 'imjoych@gmail.com' } 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.source = { :git => 'https://github.com/imjoych/JCNavigator.git', :tag => s.version.to_s } 9 | s.source_files = 'JCNavigator/*.{h,m}' 10 | s.requires_arc = true 11 | 12 | s.ios.deployment_target = '9.0' 13 | 14 | end 15 | 16 | -------------------------------------------------------------------------------- /JCNavigator/JCModuleMap.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCModuleMap.h 3 | // JCNavigator 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol JCModuleMap 12 | 13 | /** 14 | * Returns view controller class for map key which implemented in classesForMapKeys: method. 15 | */ 16 | - (Class)viewControllerClassForMapKey:(NSString *)mapKey; 17 | 18 | /** 19 | * Returns view controller class for URL which lastPathComponent relating to the map key. 20 | */ 21 | - (Class)viewControllerClassForURL:(NSURL *)URL; 22 | 23 | @end 24 | 25 | /** 26 | * Class of module map for pages jump. 27 | */ 28 | @interface JCModuleMap : NSObject 29 | 30 | #pragma mark - Implemented by subclass 31 | 32 | /** 33 | * Returns module map key prefix, default JC. 34 | */ 35 | - (NSString *)mapKeyPrefix; 36 | 37 | /** 38 | * Returns dictionary with the map of the view controllers classes for mapKeys. 39 | */ 40 | - (NSDictionary *)classesForMapKeys; 41 | 42 | /** 43 | * Presented or not settings while opening the view controller through URL, default NO. 44 | */ 45 | - (BOOL)presentedForClass:(Class)viewControllerClass; 46 | 47 | /** 48 | * Animated or not settings while opening the view controller through URL, default YES. 49 | */ 50 | - (BOOL)animatedForClass:(Class)viewControllerClass; 51 | 52 | /** 53 | * Returns array of reuse view controller classes. 54 | * @note The view controllers of classes will be reused if existed in window, otherwise new view controllers will be created. 55 | */ 56 | - (NSArray *)reuseViewControllerClasses; 57 | 58 | /** 59 | * Returns dictionary with properties map of URL query for the view controllers classes. 60 | * @note If all of the URL query items names are equal to the properties names, there is no need to implement this method. 61 | */ 62 | - (NSDictionary *)propertiesMapOfURLQueryForClasses; 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /JCNavigator/JCModuleMap.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCModuleMap.m 3 | // JCNavigator 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCModuleMap.h" 10 | 11 | @interface JCModuleMap () { 12 | NSDictionary *_lowercaseStringMaps; 13 | } 14 | 15 | @end 16 | 17 | @implementation JCModuleMap 18 | 19 | - (NSString *)mapKeyPrefix 20 | { 21 | return @"JC"; 22 | } 23 | 24 | - (NSDictionary *)classesForMapKeys 25 | { 26 | return nil; 27 | } 28 | 29 | - (BOOL)presentedForClass:(Class)viewControllerClass 30 | { 31 | return NO; 32 | } 33 | 34 | - (BOOL)animatedForClass:(Class)viewControllerClass 35 | { 36 | return YES; 37 | } 38 | 39 | - (NSArray *)reuseViewControllerClasses 40 | { 41 | return nil; 42 | } 43 | 44 | - (NSDictionary *)propertiesMapOfURLQueryForClasses 45 | { 46 | return nil; 47 | } 48 | 49 | #pragma mark - Private 50 | 51 | - (NSDictionary *)lowercaseStringMaps 52 | { 53 | NSDictionary *classesForMapKeys = [self classesForMapKeys]; 54 | if (!classesForMapKeys || classesForMapKeys.count < 1) { 55 | return nil; 56 | } 57 | if (!_lowercaseStringMaps) { 58 | NSMutableDictionary *maps = [@{} mutableCopy]; 59 | [classesForMapKeys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 60 | maps[[key lowercaseString]] = obj; 61 | }]; 62 | _lowercaseStringMaps = [maps copy]; 63 | } 64 | return _lowercaseStringMaps; 65 | } 66 | 67 | #pragma mark - JCModuleMap protocol 68 | 69 | - (Class)viewControllerClassForMapKey:(NSString *)mapKey 70 | { 71 | if (![mapKey isKindOfClass:[NSString class]]) { 72 | return nil; 73 | } 74 | NSString *lowercaseKey = [mapKey lowercaseString]; 75 | NSDictionary *maps = [self lowercaseStringMaps]; 76 | if ([maps.allKeys containsObject:lowercaseKey]) { 77 | return maps[lowercaseKey]; 78 | } 79 | return nil; 80 | } 81 | 82 | - (Class)viewControllerClassForURL:(NSURL *)URL 83 | { 84 | if (![URL isKindOfClass:[NSURL class]]) { 85 | return nil; 86 | } 87 | NSString *mapKey = [NSString stringWithFormat:@"%@_%@", [self mapKeyPrefix], [URL lastPathComponent]]; 88 | return [self viewControllerClassForMapKey:mapKey]; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /JCNavigator/JCNavigator.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCNavigator.h 3 | // JCNavigator 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class JCModuleMap; 13 | 14 | /** 15 | * Values for properties will be setted for will opened view controller with this block. 16 | */ 17 | typedef NSDictionary *(^JCNavigatorPropertiesBlock)(void); 18 | 19 | /** 20 | * Class of navigation manager. 21 | */ 22 | @interface JCNavigator : NSObject 23 | 24 | /** 25 | * Singleton instance of JCNavigator. 26 | */ 27 | + (instancetype)sharedNavigator; 28 | 29 | /** 30 | * Add customized NSURL scheme with host list which can be used for pages jump with open URL methods. 31 | * @param scheme customized NSURL scheme. 32 | * @param hostList customized list of NSURL host. 33 | */ 34 | - (void)addURLScheme:(NSString *)scheme hostList:(NSArray *)hostList; 35 | 36 | /** 37 | * Add several modules of maps which subclassing from JCModuleMap. 38 | */ 39 | - (void)addModuleMap:(JCModuleMap *)moduleMap; 40 | 41 | /** 42 | * Set the root view controller for the window. 43 | */ 44 | - (void)setRootViewController:(UIViewController *)rootViewController; 45 | 46 | /** 47 | * Set class of navigation controller which subclassing from UINavigationController. 48 | */ 49 | - (void)setNavigationControllerClass:(Class)navigationControllerClass; 50 | 51 | /** 52 | * The root navigation controller for the window. 53 | */ 54 | - (UINavigationController *)rootNavigationController; 55 | 56 | /** 57 | * The top view controller on the stack. 58 | */ 59 | - (UIViewController *)topViewController; 60 | 61 | /** 62 | * Current visible view controller. 63 | * @note Return modal view controller if it exists. Otherwise the top view controller. 64 | */ 65 | - (UIViewController *)visibleViewController; 66 | 67 | 68 | #pragma mark - Open URL operation 69 | 70 | /** 71 | * Open URL methods. 72 | * @note URL string format 'scheme://host/path?paramA=xxx¶mB=yyy' 73 | */ 74 | - (BOOL)openURLString:(NSString *)URLString; 75 | - (BOOL)openURL:(NSURL *)URL; 76 | - (void)openURL:(NSURL *)URL options:(NSDictionary *)options completionHandler:(void (^)(BOOL success))completion; 77 | 78 | #pragma mark - Open with module map key operation 79 | 80 | /** 81 | * Open with module map key methods. 82 | * @note Params presented defaults NO, animated defaults YES. 83 | */ 84 | - (void)openWithMapKey:(NSString *)mapKey; 85 | - (void)openWithMapKey:(NSString *)mapKey propertiesBlock:(JCNavigatorPropertiesBlock)block; 86 | - (void)openWithMapKey:(NSString *)mapKey propertiesBlock:(JCNavigatorPropertiesBlock)block presented:(BOOL)presented animated:(BOOL)animated; 87 | 88 | #pragma mark - Open with view controller 89 | 90 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; 91 | - (void)presentViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion; 92 | 93 | #pragma mark - Pop view controller operation 94 | 95 | - (void)popViewControllerAnimated:(BOOL)animated; 96 | - (void)popToRootViewControllerAnimated:(BOOL)animated; 97 | - (void)popToViewController:(UIViewController *)viewController animated:(BOOL)animated; 98 | 99 | #pragma mark - Dismiss view controller operation 100 | 101 | - (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion; 102 | 103 | - (void)dismissAndPopToRootViewControllerCompletion:(void (^)(void))completion; 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /JCNavigator/JCNavigator.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCNavigator.m 3 | // JCNavigator 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCNavigator.h" 10 | #import "JCModuleMap.h" 11 | 12 | @interface JCNavigator () { 13 | NSMutableSet *_moduleMaps; 14 | NSMutableDictionary *_hostListForScheme; 15 | Class _navigationControllerClass; 16 | UIViewController *_windowRootVC; 17 | UIViewController *_rootViewController; 18 | UINavigationController *_rootNavigationController; 19 | } 20 | 21 | @end 22 | 23 | @implementation JCNavigator 24 | 25 | - (instancetype)init 26 | { 27 | if (self = [super init]) { 28 | _moduleMaps = [NSMutableSet set]; 29 | _hostListForScheme = [NSMutableDictionary dictionary]; 30 | _navigationControllerClass = [UINavigationController class]; 31 | } 32 | return self; 33 | } 34 | 35 | #pragma mark - Public 36 | 37 | + (instancetype)sharedNavigator 38 | { 39 | static JCNavigator *sharedNavigator = nil; 40 | static dispatch_once_t onceToken; 41 | dispatch_once(&onceToken, ^{ 42 | sharedNavigator = [[self alloc] init]; 43 | }); 44 | return sharedNavigator; 45 | } 46 | 47 | - (void)addURLScheme:(NSString *)scheme hostList:(NSArray *)hostList 48 | { 49 | NSMutableArray *lowercaseHostList = [NSMutableArray arrayWithCapacity:hostList.count]; 50 | for (NSString *host in hostList) { 51 | [lowercaseHostList addObject:[host lowercaseString]]; 52 | } 53 | _hostListForScheme[[scheme lowercaseString]] = [lowercaseHostList copy]; 54 | } 55 | 56 | - (void)addModuleMap:(JCModuleMap *)moduleMap 57 | { 58 | [_moduleMaps addObject:moduleMap]; 59 | } 60 | 61 | - (void)setRootViewController:(UIViewController *)rootViewController 62 | { 63 | NSParameterAssert([rootViewController isKindOfClass:[UIViewController class]]); 64 | _windowRootVC = rootViewController; 65 | if ([rootViewController isKindOfClass:[UINavigationController class]]) { 66 | _rootNavigationController = (UINavigationController *)rootViewController; 67 | } else { 68 | _rootNavigationController = rootViewController.navigationController; 69 | } 70 | if (!_rootNavigationController) { 71 | NSArray *viewControllers = [self childViewControllers:rootViewController]; 72 | if (viewControllers.count == 0) { 73 | _rootNavigationController = [[_navigationControllerClass alloc] initWithRootViewController:rootViewController]; 74 | _windowRootVC = _rootNavigationController; 75 | } 76 | } 77 | if (_rootNavigationController) { 78 | _rootViewController = _rootNavigationController.viewControllers.firstObject; 79 | } 80 | [UIApplication sharedApplication].delegate.window.rootViewController = _windowRootVC; 81 | } 82 | 83 | - (void)setNavigationControllerClass:(Class)navigationControllerClass 84 | { 85 | _navigationControllerClass = navigationControllerClass; 86 | } 87 | 88 | - (UINavigationController *)rootNavigationController 89 | { 90 | return _rootNavigationController ?: [self childNavigationController]; 91 | } 92 | 93 | - (UIViewController *)topViewController 94 | { 95 | return [self rootNavigationController].topViewController; 96 | } 97 | 98 | - (UIViewController *)visibleViewController 99 | { 100 | return [self rootNavigationController].visibleViewController; 101 | } 102 | 103 | #pragma mark - Container View Controllers 104 | 105 | - (NSArray *)childViewControllers:(UIViewController *)viewController 106 | { 107 | if ([viewController isKindOfClass:[UITabBarController class]] 108 | || [viewController isKindOfClass:[UISplitViewController class]] 109 | || [viewController isKindOfClass:[UIPageViewController class]]) { 110 | return viewController.childViewControllers; 111 | } 112 | return nil; 113 | } 114 | 115 | - (UINavigationController *)childNavigationController 116 | { 117 | if ([_windowRootVC isKindOfClass:[UITabBarController class]]) { 118 | UITabBarController *tabBarController = (UITabBarController *)_windowRootVC; 119 | UINavigationController *navigationController = [self navigationControllerOfVC:tabBarController.selectedViewController]; 120 | if (navigationController) { 121 | _rootViewController = navigationController.viewControllers.firstObject; 122 | return navigationController; 123 | } 124 | } 125 | 126 | NSArray *viewControllers = [self childViewControllers:_windowRootVC]; 127 | for (UIViewController *vc in viewControllers) { 128 | UINavigationController *navigationController = [self navigationControllerOfVC:vc]; 129 | if (navigationController) { 130 | _rootViewController = navigationController.viewControllers.firstObject; 131 | return navigationController; 132 | } 133 | } 134 | return nil; 135 | } 136 | 137 | - (UINavigationController *)navigationControllerOfVC:(UIViewController *)vc 138 | { 139 | if ([vc isKindOfClass:[UINavigationController class]]) { 140 | return (UINavigationController *)vc; 141 | } else if (vc.navigationController) { 142 | return vc.navigationController; 143 | } 144 | return nil; 145 | } 146 | 147 | #pragma mark - Open URL operation 148 | 149 | - (BOOL)openURLString:(NSString *)URLString 150 | { 151 | if (![URLString isKindOfClass:[NSString class]]) { 152 | return NO; 153 | } 154 | return [self openURL:[NSURL URLWithString:URLString]]; 155 | } 156 | 157 | - (BOOL)openURL:(NSURL *)URL 158 | { 159 | __block BOOL opened = NO; 160 | [self openURL:URL options:nil completionHandler:^(BOOL success) { 161 | opened = success; 162 | }]; 163 | return opened; 164 | } 165 | 166 | - (void)openURL:(NSURL *)URL options:(NSDictionary *)options completionHandler:(void (^)(BOOL))completion 167 | { 168 | if (![URL isKindOfClass:[NSURL class]] || !URL.scheme) { // Invalid URL. 169 | if (completion) { 170 | completion(NO); 171 | } 172 | return; 173 | } 174 | 175 | NSString *lowercaseScheme = [URL.scheme lowercaseString]; 176 | if (![_hostListForScheme.allKeys containsObject:lowercaseScheme] || !URL.host) { // URL scheme not found or URL host not exist. 177 | [self openSpecifiedURL:URL options:options completionHandler:^(BOOL success) { 178 | if (completion) { 179 | completion(success); 180 | } 181 | }]; 182 | return; 183 | } 184 | 185 | NSArray *hostList = _hostListForScheme[lowercaseScheme]; 186 | NSString *lowercaseHost = [URL.host lowercaseString]; 187 | if (![hostList containsObject:lowercaseHost]) { // URL host not found. 188 | [self openSpecifiedURL:URL options:options completionHandler:^(BOOL success) { 189 | if (completion) { 190 | completion(success); 191 | } 192 | }]; 193 | return; 194 | } 195 | 196 | JCModuleMap *moduleMap = [self moduleMapForURL:URL]; 197 | if (!moduleMap) { // The corresponding moduleMap not found. 198 | [self openSpecifiedURL:URL options:options completionHandler:^(BOOL success) { 199 | if (completion) { 200 | completion(success); 201 | } 202 | }]; 203 | return; 204 | } 205 | 206 | Class viewControllerClass = [moduleMap viewControllerClassForURL:URL]; 207 | NSDictionary *parameters = [self parseURLQuery:URL.query]; 208 | BOOL presented = [moduleMap presentedForClass:viewControllerClass]; 209 | BOOL animated = [moduleMap animatedForClass:viewControllerClass]; 210 | [self openViewControllerWithClass:viewControllerClass 211 | moduleMap:moduleMap 212 | params:parameters 213 | presented:presented 214 | animated:animated]; 215 | if (completion) { 216 | completion(YES); 217 | } 218 | } 219 | 220 | - (void)openSpecifiedURL:(NSURL *)URL options:(NSDictionary *)options completionHandler:(void (^)(BOOL success))completion 221 | { 222 | if (@available(iOS 10.0, *)) { 223 | /// If options is a valid dictionary, use the async method of openURL, otherwise use the sync method. 224 | if ([options isKindOfClass:[NSDictionary class]]) { 225 | [[UIApplication sharedApplication] openURL:URL options:options completionHandler:completion]; 226 | return; 227 | } 228 | } 229 | BOOL success = NO; 230 | if ([[UIApplication sharedApplication] canOpenURL:URL]) { 231 | success = [[UIApplication sharedApplication] openURL:URL]; 232 | } 233 | if (completion) { 234 | completion(success); 235 | } 236 | } 237 | 238 | - (NSDictionary *)parseURLQuery:(NSString *)query 239 | { 240 | if (![query isKindOfClass:[NSString class]] || query.length < 1) { 241 | return nil; 242 | } 243 | NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; 244 | NSArray *pairs = [query componentsSeparatedByString:@"&"]; 245 | for (NSString *pair in pairs) { 246 | NSArray *keyValue = [pair componentsSeparatedByString:@"="]; 247 | if (keyValue.count == 2) { 248 | NSString *key = [keyValue[0] stringByRemovingPercentEncoding]; 249 | NSString *value = [keyValue[1] stringByRemovingPercentEncoding]; 250 | parameters[key] = value; 251 | } 252 | } 253 | return parameters; 254 | } 255 | 256 | #pragma mark - Open with module map key operation 257 | 258 | - (void)openWithMapKey:(NSString *)mapKey 259 | { 260 | [self openWithMapKey:mapKey propertiesBlock:nil]; 261 | } 262 | 263 | - (void)openWithMapKey:(NSString *)mapKey propertiesBlock:(JCNavigatorPropertiesBlock)block 264 | { 265 | [self openWithMapKey:mapKey propertiesBlock:block presented:NO animated:YES]; 266 | } 267 | 268 | - (void)openWithMapKey:(NSString *)mapKey propertiesBlock:(JCNavigatorPropertiesBlock)block presented:(BOOL)presented animated:(BOOL)animated 269 | { 270 | NSParameterAssert([mapKey isKindOfClass:[NSString class]]); 271 | JCModuleMap *moduleMap = [self moduleMapForMapKey:mapKey]; 272 | if (!moduleMap) { 273 | NSAssert(moduleMap, @"The subclass of JCModuleMap for %@ not found! Please create it and implement the classesForMapKeys: method, next add it's instance to JCNavigator with addModuleMap: method.", mapKey); 274 | return; 275 | } 276 | Class viewControllerClass = [moduleMap viewControllerClassForMapKey:mapKey]; 277 | NSDictionary *parameters = block ? block(): nil; 278 | [self openViewControllerWithClass:viewControllerClass 279 | moduleMap:moduleMap 280 | params:parameters 281 | presented:presented 282 | animated:animated]; 283 | } 284 | 285 | #pragma mark - Open with view controller 286 | 287 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated 288 | { 289 | [[self visibleViewController].navigationController pushViewController:viewController animated:animated]; 290 | } 291 | 292 | - (void)presentViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion 293 | { 294 | UIViewController *vc = [self visibleViewController]; 295 | vc = vc.parentViewController ?: vc; 296 | [vc presentViewController:viewController animated:animated completion:completion]; 297 | } 298 | 299 | #pragma mark - Pop view controller operation 300 | 301 | - (void)popViewControllerAnimated:(BOOL)animated 302 | { 303 | [[self visibleViewController].navigationController popViewControllerAnimated:animated]; 304 | } 305 | 306 | - (void)popToRootViewControllerAnimated:(BOOL)animated 307 | { 308 | [[self visibleViewController].navigationController popToRootViewControllerAnimated:animated]; 309 | } 310 | 311 | - (void)popToViewController:(UIViewController *)viewController animated:(BOOL)animated 312 | { 313 | [[self visibleViewController].navigationController popToViewController:viewController animated:animated]; 314 | } 315 | 316 | #pragma mark - Dismiss view controller operation 317 | 318 | - (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion 319 | { 320 | [[self visibleViewController] dismissViewControllerAnimated:animated completion:^{ 321 | if (completion) { 322 | completion(); 323 | } 324 | }]; 325 | } 326 | 327 | - (void)dismissAndPopToRootViewControllerCompletion:(void (^)(void))completion 328 | { 329 | [self openPreviousVCOfWillOpenedVC:_rootViewController completion:^(BOOL success) { 330 | if (completion) { 331 | completion(); 332 | } 333 | }]; 334 | } 335 | 336 | #pragma mark - Open view controller 337 | 338 | - (void)openViewControllerWithClass:(Class)viewControllerClass moduleMap:(JCModuleMap *)moduleMap params:(NSDictionary *)params presented:(BOOL)presented animated:(BOOL)animated 339 | { 340 | NSParameterAssert([viewControllerClass isSubclassOfClass:[UIViewController class]]); 341 | if ([[moduleMap reuseViewControllerClasses] containsObject:viewControllerClass]) { 342 | UIViewController *viewController = [self existedViewControllerForClass:viewControllerClass]; 343 | if (viewController) { 344 | [self setViewController:viewController moduleMap:moduleMap params:params]; 345 | [self openPreviousVCOfWillOpenedVC:viewController completion:^(BOOL success) { 346 | if (success) { 347 | [self openViewController:viewController presented:presented animated:animated]; 348 | } 349 | }]; 350 | return; 351 | } 352 | } 353 | 354 | UIViewController *viewController = [[viewControllerClass alloc] init]; 355 | [self setViewController:viewController moduleMap:moduleMap params:params]; 356 | [self openViewController:viewController presented:presented animated:animated]; 357 | } 358 | 359 | - (UIViewController *)existedViewControllerForClass:(Class)class 360 | { 361 | return [self existedViewControllerForClass:class navigationController:[self rootNavigationController]]; 362 | } 363 | 364 | - (UIViewController *)existedViewControllerForClass:(Class)class navigationController:(UINavigationController *)navigationController 365 | { 366 | if (!class || !navigationController) { 367 | return nil; 368 | } 369 | UIViewController *viewController = nil; 370 | for (UIViewController *vc in navigationController.viewControllers) { 371 | if ([vc isMemberOfClass:class]) { 372 | viewController = vc; 373 | break; 374 | } else if (vc.presentedViewController && [vc.presentedViewController isKindOfClass:[UINavigationController class]]) { 375 | viewController = [self existedViewControllerForClass:class navigationController:(UINavigationController *)vc.presentedViewController]; 376 | if (viewController) { 377 | break; 378 | } 379 | } 380 | } 381 | return viewController; 382 | } 383 | 384 | - (void)setViewController:(UIViewController *)viewController moduleMap:(JCModuleMap *)moduleMap params:(NSDictionary *)params 385 | { 386 | if (![params isKindOfClass:[NSDictionary class]] || params.count < 1) { 387 | return; 388 | } 389 | NSDictionary *mapForClasses = [moduleMap propertiesMapOfURLQueryForClasses]; 390 | NSDictionary *propertiesMap = mapForClasses[NSStringFromClass([viewController class])]; 391 | [params enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { 392 | NSString *propertyName = nil; 393 | if (propertiesMap) { 394 | propertyName = propertiesMap[key]; 395 | } 396 | if (!propertyName) { 397 | propertyName = key; 398 | } 399 | NSString *propertySetterName = [NSString stringWithFormat:@"set%@%@:", [[propertyName substringToIndex:1] uppercaseString], [propertyName substringFromIndex:1]]; 400 | 401 | if (![obj isKindOfClass:[NSNull class]] 402 | && [viewController respondsToSelector:NSSelectorFromString(propertyName)] 403 | && [viewController respondsToSelector:NSSelectorFromString(propertySetterName)]) { 404 | [viewController setValue:obj forKey:propertyName]; 405 | } 406 | }]; 407 | } 408 | 409 | /// Pop to the previous view controller of viewController, 410 | /// or dismiss to the previous navigation level of viewController. 411 | - (void)openPreviousVCOfWillOpenedVC:(UIViewController *)viewController completion:(void(^)(BOOL success))completion 412 | { 413 | if (!viewController) { 414 | if (completion) { 415 | completion(NO); 416 | } 417 | return; 418 | } 419 | UIViewController *visibleViewController = [self visibleViewController]; 420 | NSArray *viewControllers = visibleViewController.navigationController.viewControllers; 421 | if ([viewControllers containsObject:viewController]) { 422 | NSUInteger vcIndex = [viewControllers indexOfObject:viewController]; 423 | if (vcIndex > 0) { 424 | // pop to the previous view controller of viewController. 425 | [self popToViewController:viewControllers[vcIndex - 1] animated:NO]; 426 | if (completion) { 427 | completion(YES); 428 | } 429 | return; 430 | } else if (viewController != _rootViewController) { 431 | // dismiss to the previous navigation level of viewController. 432 | [self dismissViewControllerAnimated:NO completion:^{ 433 | if (completion) { 434 | completion(YES); 435 | } 436 | }]; 437 | return; 438 | } 439 | if (viewControllers.count > 1 && viewController == _rootViewController) { 440 | [self popToRootViewControllerAnimated:NO]; 441 | } 442 | if (completion) { 443 | completion(NO); 444 | } 445 | return; 446 | } 447 | if (visibleViewController != _rootViewController) { 448 | // dismiss to the previous navigation level to find viewController. 449 | [self dismissViewControllerAnimated:NO completion:^{ 450 | [self openPreviousVCOfWillOpenedVC:viewController completion:completion]; 451 | }]; 452 | } else { 453 | if (completion) { 454 | completion(NO); 455 | } 456 | } 457 | } 458 | 459 | /// Method of opening view controller. 460 | - (void)openViewController:(UIViewController *)viewController presented:(BOOL)presented animated:(BOOL)animated 461 | { 462 | if (presented) { 463 | UINavigationController *navigationController = [[_navigationControllerClass alloc] initWithRootViewController:viewController]; 464 | [self presentViewController:navigationController animated:animated completion:nil]; 465 | return; 466 | } 467 | [self pushViewController:viewController animated:animated]; 468 | } 469 | 470 | #pragma mark - ModuleMap 471 | 472 | - (JCModuleMap *)moduleMapForMapKey:(NSString *)mapKey 473 | { 474 | __block JCModuleMap *moduleMap = nil; 475 | [_moduleMaps enumerateObjectsUsingBlock:^(JCModuleMap *map, BOOL *stop) { 476 | if ([map viewControllerClassForMapKey:mapKey]) { 477 | moduleMap = map; 478 | *stop = YES; 479 | } 480 | }]; 481 | return moduleMap; 482 | } 483 | 484 | - (JCModuleMap *)moduleMapForURL:(NSURL *)URL 485 | { 486 | __block JCModuleMap *moduleMap = nil; 487 | [_moduleMaps enumerateObjectsUsingBlock:^(JCModuleMap *map, BOOL *stop) { 488 | if ([map viewControllerClassForURL:URL]) { 489 | moduleMap = map; 490 | *stop = YES; 491 | } 492 | }]; 493 | return moduleMap; 494 | } 495 | 496 | @end 497 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 23F2F50E2081919B00FC3ED4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F2F50D2081919B00FC3ED4 /* AppDelegate.m */; }; 11 | 23F2F5112081919B00FC3ED4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F2F5102081919B00FC3ED4 /* ViewController.m */; }; 12 | 23F2F5262081921700FC3ED4 /* JCNavigator.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F2F5252081921700FC3ED4 /* JCNavigator.m */; }; 13 | 23F2F52C2081A8BD00FC3ED4 /* JCModuleMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F2F52B2081A8BD00FC3ED4 /* JCModuleMap.m */; }; 14 | 23F2F5432081B33800FC3ED4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F2F53A2081B33800FC3ED4 /* main.m */; }; 15 | 23F2F5462081B33800FC3ED4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23F2F5402081B33800FC3ED4 /* Assets.xcassets */; }; 16 | 23F2F54C2081B35400FC3ED4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 23F2F5492081B35400FC3ED4 /* LaunchScreen.storyboard */; }; 17 | B6625DB520AC1C9B00A7D910 /* JCRootModuleMap.m in Sources */ = {isa = PBXBuildFile; fileRef = B6625DB420AC1C9A00A7D910 /* JCRootModuleMap.m */; }; 18 | B6625DDA20AC1F6D00A7D910 /* UIViewController+JCNavigationItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B6625DD620AC1F6C00A7D910 /* UIViewController+JCNavigationItem.m */; }; 19 | B6625DDB20AC1F6D00A7D910 /* JCNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6625DD920AC1F6D00A7D910 /* JCNavigationController.m */; }; 20 | B69230AF20AD631900802608 /* JCRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = B69230AE20AD631800802608 /* JCRootView.m */; }; 21 | B6C5EF5020BD3A8E00625506 /* JCTestView.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5EF4420BD3A8D00625506 /* JCTestView.m */; }; 22 | B6C5EF5120BD3A8E00625506 /* JCSecondLevelViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5EF4720BD3A8D00625506 /* JCSecondLevelViewController.m */; }; 23 | B6C5EF5220BD3A8E00625506 /* JCContentDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5EF4B20BD3A8E00625506 /* JCContentDetailViewController.m */; }; 24 | B6C5EF5320BD3A8E00625506 /* JCTestClass.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5EF4D20BD3A8E00625506 /* JCTestClass.m */; }; 25 | B6C5EF5420BD3A8E00625506 /* JCFirstLevelViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5EF4E20BD3A8E00625506 /* JCFirstLevelViewController.m */; }; 26 | B6C5EF5520BD3A8E00625506 /* JCThirdLevelViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5EF4F20BD3A8E00625506 /* JCThirdLevelViewController.m */; }; 27 | B6D9C9D820CE0EEF006443DF /* JCTestModuleMap.m in Sources */ = {isa = PBXBuildFile; fileRef = B6D9C9D620CE0EEE006443DF /* JCTestModuleMap.m */; }; 28 | B6E2873C25F8647A009DE419 /* JCTabBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E2873B25F8647A009DE419 /* JCTabBarController.m */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 23F2F5092081919B00FC3ED4 /* JCNavigatorDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JCNavigatorDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 23F2F50C2081919B00FC3ED4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 34 | 23F2F50D2081919B00FC3ED4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 35 | 23F2F50F2081919B00FC3ED4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 36 | 23F2F5102081919B00FC3ED4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 37 | 23F2F5242081921700FC3ED4 /* JCNavigator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JCNavigator.h; sourceTree = ""; }; 38 | 23F2F5252081921700FC3ED4 /* JCNavigator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JCNavigator.m; sourceTree = ""; }; 39 | 23F2F52A2081A8BD00FC3ED4 /* JCModuleMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JCModuleMap.h; sourceTree = ""; }; 40 | 23F2F52B2081A8BD00FC3ED4 /* JCModuleMap.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JCModuleMap.m; sourceTree = ""; }; 41 | 23F2F5392081B33800FC3ED4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | 23F2F53A2081B33800FC3ED4 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 43 | 23F2F5402081B33800FC3ED4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 44 | 23F2F54A2081B35400FC3ED4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 45 | B6625DB220AC1C9A00A7D910 /* JCRootModuleMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCRootModuleMap.h; sourceTree = ""; }; 46 | B6625DB420AC1C9A00A7D910 /* JCRootModuleMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JCRootModuleMap.m; sourceTree = ""; }; 47 | B6625DD620AC1F6C00A7D910 /* UIViewController+JCNavigationItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+JCNavigationItem.m"; sourceTree = ""; }; 48 | B6625DD720AC1F6C00A7D910 /* JCNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCNavigationController.h; sourceTree = ""; }; 49 | B6625DD820AC1F6C00A7D910 /* UIViewController+JCNavigationItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+JCNavigationItem.h"; sourceTree = ""; }; 50 | B6625DD920AC1F6D00A7D910 /* JCNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JCNavigationController.m; sourceTree = ""; }; 51 | B69230AD20AD631800802608 /* JCRootView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JCRootView.h; sourceTree = ""; }; 52 | B69230AE20AD631800802608 /* JCRootView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JCRootView.m; sourceTree = ""; }; 53 | B6C5EF4320BD3A8D00625506 /* JCFirstLevelViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCFirstLevelViewController.h; sourceTree = ""; }; 54 | B6C5EF4420BD3A8D00625506 /* JCTestView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JCTestView.m; sourceTree = ""; }; 55 | B6C5EF4520BD3A8D00625506 /* JCContentDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCContentDetailViewController.h; sourceTree = ""; }; 56 | B6C5EF4620BD3A8D00625506 /* JCTestHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCTestHeader.h; sourceTree = ""; }; 57 | B6C5EF4720BD3A8D00625506 /* JCSecondLevelViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JCSecondLevelViewController.m; sourceTree = ""; }; 58 | B6C5EF4820BD3A8D00625506 /* JCTestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCTestClass.h; sourceTree = ""; }; 59 | B6C5EF4920BD3A8E00625506 /* JCTestView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCTestView.h; sourceTree = ""; }; 60 | B6C5EF4A20BD3A8E00625506 /* JCSecondLevelViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCSecondLevelViewController.h; sourceTree = ""; }; 61 | B6C5EF4B20BD3A8E00625506 /* JCContentDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JCContentDetailViewController.m; sourceTree = ""; }; 62 | B6C5EF4C20BD3A8E00625506 /* JCThirdLevelViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCThirdLevelViewController.h; sourceTree = ""; }; 63 | B6C5EF4D20BD3A8E00625506 /* JCTestClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JCTestClass.m; sourceTree = ""; }; 64 | B6C5EF4E20BD3A8E00625506 /* JCFirstLevelViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JCFirstLevelViewController.m; sourceTree = ""; }; 65 | B6C5EF4F20BD3A8E00625506 /* JCThirdLevelViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JCThirdLevelViewController.m; sourceTree = ""; }; 66 | B6D9C9D620CE0EEE006443DF /* JCTestModuleMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JCTestModuleMap.m; sourceTree = ""; }; 67 | B6D9C9D720CE0EEF006443DF /* JCTestModuleMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCTestModuleMap.h; sourceTree = ""; }; 68 | B6E2873A25F8647A009DE419 /* JCTabBarController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JCTabBarController.h; sourceTree = ""; }; 69 | B6E2873B25F8647A009DE419 /* JCTabBarController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JCTabBarController.m; sourceTree = ""; }; 70 | /* End PBXFileReference section */ 71 | 72 | /* Begin PBXFrameworksBuildPhase section */ 73 | 23F2F5062081919B00FC3ED4 /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXFrameworksBuildPhase section */ 81 | 82 | /* Begin PBXGroup section */ 83 | 23F2F5002081919B00FC3ED4 = { 84 | isa = PBXGroup; 85 | children = ( 86 | 23F2F522208191B500FC3ED4 /* JCNavigator */, 87 | 23F2F50B2081919B00FC3ED4 /* JCNavigatorDemo */, 88 | 23F2F50A2081919B00FC3ED4 /* Products */, 89 | ); 90 | sourceTree = ""; 91 | }; 92 | 23F2F50A2081919B00FC3ED4 /* Products */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 23F2F5092081919B00FC3ED4 /* JCNavigatorDemo.app */, 96 | ); 97 | name = Products; 98 | sourceTree = ""; 99 | }; 100 | 23F2F50B2081919B00FC3ED4 /* JCNavigatorDemo */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | B6625D9120AC1C8600A7D910 /* TestModule */, 104 | 23F2F5352081B32900FC3ED4 /* Support Files */, 105 | 23F2F50C2081919B00FC3ED4 /* AppDelegate.h */, 106 | 23F2F50D2081919B00FC3ED4 /* AppDelegate.m */, 107 | 23F2F50F2081919B00FC3ED4 /* ViewController.h */, 108 | 23F2F5102081919B00FC3ED4 /* ViewController.m */, 109 | B69230AD20AD631800802608 /* JCRootView.h */, 110 | B69230AE20AD631800802608 /* JCRootView.m */, 111 | B6625DB220AC1C9A00A7D910 /* JCRootModuleMap.h */, 112 | B6625DB420AC1C9A00A7D910 /* JCRootModuleMap.m */, 113 | B6E2873A25F8647A009DE419 /* JCTabBarController.h */, 114 | B6E2873B25F8647A009DE419 /* JCTabBarController.m */, 115 | B6625DD720AC1F6C00A7D910 /* JCNavigationController.h */, 116 | B6625DD920AC1F6D00A7D910 /* JCNavigationController.m */, 117 | B6625DD820AC1F6C00A7D910 /* UIViewController+JCNavigationItem.h */, 118 | B6625DD620AC1F6C00A7D910 /* UIViewController+JCNavigationItem.m */, 119 | ); 120 | path = JCNavigatorDemo; 121 | sourceTree = ""; 122 | }; 123 | 23F2F522208191B500FC3ED4 /* JCNavigator */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 23F2F5242081921700FC3ED4 /* JCNavigator.h */, 127 | 23F2F5252081921700FC3ED4 /* JCNavigator.m */, 128 | 23F2F52A2081A8BD00FC3ED4 /* JCModuleMap.h */, 129 | 23F2F52B2081A8BD00FC3ED4 /* JCModuleMap.m */, 130 | ); 131 | name = JCNavigator; 132 | path = ../JCNavigator; 133 | sourceTree = ""; 134 | }; 135 | 23F2F5352081B32900FC3ED4 /* Support Files */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 23F2F5402081B33800FC3ED4 /* Assets.xcassets */, 139 | 23F2F5492081B35400FC3ED4 /* LaunchScreen.storyboard */, 140 | 23F2F5392081B33800FC3ED4 /* Info.plist */, 141 | 23F2F53A2081B33800FC3ED4 /* main.m */, 142 | ); 143 | name = "Support Files"; 144 | sourceTree = ""; 145 | }; 146 | B6625D9120AC1C8600A7D910 /* TestModule */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | B6D9C9D720CE0EEF006443DF /* JCTestModuleMap.h */, 150 | B6D9C9D620CE0EEE006443DF /* JCTestModuleMap.m */, 151 | B6C5EF2F20BD3A7500625506 /* Private */, 152 | ); 153 | path = TestModule; 154 | sourceTree = ""; 155 | }; 156 | B6C5EF2F20BD3A7500625506 /* Private */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | B6C5EF4520BD3A8D00625506 /* JCContentDetailViewController.h */, 160 | B6C5EF4B20BD3A8E00625506 /* JCContentDetailViewController.m */, 161 | B6C5EF4320BD3A8D00625506 /* JCFirstLevelViewController.h */, 162 | B6C5EF4E20BD3A8E00625506 /* JCFirstLevelViewController.m */, 163 | B6C5EF4A20BD3A8E00625506 /* JCSecondLevelViewController.h */, 164 | B6C5EF4720BD3A8D00625506 /* JCSecondLevelViewController.m */, 165 | B6C5EF4C20BD3A8E00625506 /* JCThirdLevelViewController.h */, 166 | B6C5EF4F20BD3A8E00625506 /* JCThirdLevelViewController.m */, 167 | B6C5EF4820BD3A8D00625506 /* JCTestClass.h */, 168 | B6C5EF4D20BD3A8E00625506 /* JCTestClass.m */, 169 | B6C5EF4620BD3A8D00625506 /* JCTestHeader.h */, 170 | B6C5EF4920BD3A8E00625506 /* JCTestView.h */, 171 | B6C5EF4420BD3A8D00625506 /* JCTestView.m */, 172 | ); 173 | path = Private; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXGroup section */ 177 | 178 | /* Begin PBXNativeTarget section */ 179 | 23F2F5082081919B00FC3ED4 /* JCNavigatorDemo */ = { 180 | isa = PBXNativeTarget; 181 | buildConfigurationList = 23F2F51F2081919C00FC3ED4 /* Build configuration list for PBXNativeTarget "JCNavigatorDemo" */; 182 | buildPhases = ( 183 | 23F2F5052081919B00FC3ED4 /* Sources */, 184 | 23F2F5062081919B00FC3ED4 /* Frameworks */, 185 | 23F2F5072081919B00FC3ED4 /* Resources */, 186 | ); 187 | buildRules = ( 188 | ); 189 | dependencies = ( 190 | ); 191 | name = JCNavigatorDemo; 192 | productName = JCNavigatorDemo; 193 | productReference = 23F2F5092081919B00FC3ED4 /* JCNavigatorDemo.app */; 194 | productType = "com.apple.product-type.application"; 195 | }; 196 | /* End PBXNativeTarget section */ 197 | 198 | /* Begin PBXProject section */ 199 | 23F2F5012081919B00FC3ED4 /* Project object */ = { 200 | isa = PBXProject; 201 | attributes = { 202 | CLASSPREFIX = JC; 203 | LastUpgradeCheck = 1240; 204 | ORGANIZATIONNAME = "Joych"; 205 | TargetAttributes = { 206 | 23F2F5082081919B00FC3ED4 = { 207 | CreatedOnToolsVersion = 9.3; 208 | }; 209 | }; 210 | }; 211 | buildConfigurationList = 23F2F5042081919B00FC3ED4 /* Build configuration list for PBXProject "JCNavigatorDemo" */; 212 | compatibilityVersion = "Xcode 9.3"; 213 | developmentRegion = en; 214 | hasScannedForEncodings = 0; 215 | knownRegions = ( 216 | en, 217 | Base, 218 | ); 219 | mainGroup = 23F2F5002081919B00FC3ED4; 220 | productRefGroup = 23F2F50A2081919B00FC3ED4 /* Products */; 221 | projectDirPath = ""; 222 | projectRoot = ""; 223 | targets = ( 224 | 23F2F5082081919B00FC3ED4 /* JCNavigatorDemo */, 225 | ); 226 | }; 227 | /* End PBXProject section */ 228 | 229 | /* Begin PBXResourcesBuildPhase section */ 230 | 23F2F5072081919B00FC3ED4 /* Resources */ = { 231 | isa = PBXResourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | 23F2F54C2081B35400FC3ED4 /* LaunchScreen.storyboard in Resources */, 235 | 23F2F5462081B33800FC3ED4 /* Assets.xcassets in Resources */, 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | }; 239 | /* End PBXResourcesBuildPhase section */ 240 | 241 | /* Begin PBXSourcesBuildPhase section */ 242 | 23F2F5052081919B00FC3ED4 /* Sources */ = { 243 | isa = PBXSourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | B6625DDA20AC1F6D00A7D910 /* UIViewController+JCNavigationItem.m in Sources */, 247 | B69230AF20AD631900802608 /* JCRootView.m in Sources */, 248 | B6C5EF5320BD3A8E00625506 /* JCTestClass.m in Sources */, 249 | B6C5EF5420BD3A8E00625506 /* JCFirstLevelViewController.m in Sources */, 250 | B6625DDB20AC1F6D00A7D910 /* JCNavigationController.m in Sources */, 251 | B6C5EF5120BD3A8E00625506 /* JCSecondLevelViewController.m in Sources */, 252 | B6C5EF5020BD3A8E00625506 /* JCTestView.m in Sources */, 253 | B6C5EF5520BD3A8E00625506 /* JCThirdLevelViewController.m in Sources */, 254 | 23F2F5262081921700FC3ED4 /* JCNavigator.m in Sources */, 255 | 23F2F5112081919B00FC3ED4 /* ViewController.m in Sources */, 256 | B6E2873C25F8647A009DE419 /* JCTabBarController.m in Sources */, 257 | B6625DB520AC1C9B00A7D910 /* JCRootModuleMap.m in Sources */, 258 | B6C5EF5220BD3A8E00625506 /* JCContentDetailViewController.m in Sources */, 259 | 23F2F52C2081A8BD00FC3ED4 /* JCModuleMap.m in Sources */, 260 | 23F2F5432081B33800FC3ED4 /* main.m in Sources */, 261 | 23F2F50E2081919B00FC3ED4 /* AppDelegate.m in Sources */, 262 | B6D9C9D820CE0EEF006443DF /* JCTestModuleMap.m in Sources */, 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | }; 266 | /* End PBXSourcesBuildPhase section */ 267 | 268 | /* Begin PBXVariantGroup section */ 269 | 23F2F5492081B35400FC3ED4 /* LaunchScreen.storyboard */ = { 270 | isa = PBXVariantGroup; 271 | children = ( 272 | 23F2F54A2081B35400FC3ED4 /* Base */, 273 | ); 274 | name = LaunchScreen.storyboard; 275 | sourceTree = ""; 276 | }; 277 | /* End PBXVariantGroup section */ 278 | 279 | /* Begin XCBuildConfiguration section */ 280 | 23F2F51D2081919C00FC3ED4 /* Debug */ = { 281 | isa = XCBuildConfiguration; 282 | buildSettings = { 283 | ALWAYS_SEARCH_USER_PATHS = NO; 284 | CLANG_ANALYZER_NONNULL = YES; 285 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 286 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 287 | CLANG_CXX_LIBRARY = "libc++"; 288 | CLANG_ENABLE_MODULES = YES; 289 | CLANG_ENABLE_OBJC_ARC = YES; 290 | CLANG_ENABLE_OBJC_WEAK = YES; 291 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 292 | CLANG_WARN_BOOL_CONVERSION = YES; 293 | CLANG_WARN_COMMA = YES; 294 | CLANG_WARN_CONSTANT_CONVERSION = YES; 295 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 296 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 297 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 298 | CLANG_WARN_EMPTY_BODY = YES; 299 | CLANG_WARN_ENUM_CONVERSION = YES; 300 | CLANG_WARN_INFINITE_RECURSION = YES; 301 | CLANG_WARN_INT_CONVERSION = YES; 302 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 303 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 304 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 305 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 306 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 307 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 308 | CLANG_WARN_STRICT_PROTOTYPES = YES; 309 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 310 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 311 | CLANG_WARN_UNREACHABLE_CODE = YES; 312 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 313 | CODE_SIGN_IDENTITY = "iPhone Developer"; 314 | COPY_PHASE_STRIP = NO; 315 | DEBUG_INFORMATION_FORMAT = dwarf; 316 | ENABLE_STRICT_OBJC_MSGSEND = YES; 317 | ENABLE_TESTABILITY = YES; 318 | GCC_C_LANGUAGE_STANDARD = gnu11; 319 | GCC_DYNAMIC_NO_PIC = NO; 320 | GCC_NO_COMMON_BLOCKS = YES; 321 | GCC_OPTIMIZATION_LEVEL = 0; 322 | GCC_PREPROCESSOR_DEFINITIONS = ( 323 | "DEBUG=1", 324 | "$(inherited)", 325 | ); 326 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 327 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 328 | GCC_WARN_UNDECLARED_SELECTOR = YES; 329 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 330 | GCC_WARN_UNUSED_FUNCTION = YES; 331 | GCC_WARN_UNUSED_VARIABLE = YES; 332 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 333 | MTL_ENABLE_DEBUG_INFO = YES; 334 | ONLY_ACTIVE_ARCH = YES; 335 | SDKROOT = iphoneos; 336 | }; 337 | name = Debug; 338 | }; 339 | 23F2F51E2081919C00FC3ED4 /* Release */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | ALWAYS_SEARCH_USER_PATHS = NO; 343 | CLANG_ANALYZER_NONNULL = YES; 344 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 345 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 346 | CLANG_CXX_LIBRARY = "libc++"; 347 | CLANG_ENABLE_MODULES = YES; 348 | CLANG_ENABLE_OBJC_ARC = YES; 349 | CLANG_ENABLE_OBJC_WEAK = YES; 350 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 351 | CLANG_WARN_BOOL_CONVERSION = YES; 352 | CLANG_WARN_COMMA = YES; 353 | CLANG_WARN_CONSTANT_CONVERSION = YES; 354 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 355 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 356 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 357 | CLANG_WARN_EMPTY_BODY = YES; 358 | CLANG_WARN_ENUM_CONVERSION = YES; 359 | CLANG_WARN_INFINITE_RECURSION = YES; 360 | CLANG_WARN_INT_CONVERSION = YES; 361 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 362 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 363 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 364 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 365 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 366 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 367 | CLANG_WARN_STRICT_PROTOTYPES = YES; 368 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 369 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 370 | CLANG_WARN_UNREACHABLE_CODE = YES; 371 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 372 | CODE_SIGN_IDENTITY = "iPhone Developer"; 373 | COPY_PHASE_STRIP = NO; 374 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 375 | ENABLE_NS_ASSERTIONS = NO; 376 | ENABLE_STRICT_OBJC_MSGSEND = YES; 377 | GCC_C_LANGUAGE_STANDARD = gnu11; 378 | GCC_NO_COMMON_BLOCKS = YES; 379 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 380 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 381 | GCC_WARN_UNDECLARED_SELECTOR = YES; 382 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 383 | GCC_WARN_UNUSED_FUNCTION = YES; 384 | GCC_WARN_UNUSED_VARIABLE = YES; 385 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 386 | MTL_ENABLE_DEBUG_INFO = NO; 387 | SDKROOT = iphoneos; 388 | VALIDATE_PRODUCT = YES; 389 | }; 390 | name = Release; 391 | }; 392 | 23F2F5202081919C00FC3ED4 /* Debug */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 396 | CODE_SIGN_STYLE = Manual; 397 | CURRENT_PROJECT_VERSION = 121; 398 | DEVELOPMENT_TEAM = ""; 399 | INFOPLIST_FILE = JCNavigatorDemo/Info.plist; 400 | LD_RUNPATH_SEARCH_PATHS = ( 401 | "$(inherited)", 402 | "@executable_path/Frameworks", 403 | ); 404 | MARKETING_VERSION = 1.0.2; 405 | PRODUCT_BUNDLE_IDENTIFIER = com.joych.JCNavigatorDemo; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | PROVISIONING_PROFILE_SPECIFIER = ""; 408 | TARGETED_DEVICE_FAMILY = "1,2"; 409 | }; 410 | name = Debug; 411 | }; 412 | 23F2F5212081919C00FC3ED4 /* Release */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 416 | CODE_SIGN_STYLE = Manual; 417 | CURRENT_PROJECT_VERSION = 121; 418 | DEVELOPMENT_TEAM = ""; 419 | INFOPLIST_FILE = JCNavigatorDemo/Info.plist; 420 | LD_RUNPATH_SEARCH_PATHS = ( 421 | "$(inherited)", 422 | "@executable_path/Frameworks", 423 | ); 424 | MARKETING_VERSION = 1.0.2; 425 | PRODUCT_BUNDLE_IDENTIFIER = com.joych.JCNavigatorDemo; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | PROVISIONING_PROFILE_SPECIFIER = ""; 428 | TARGETED_DEVICE_FAMILY = "1,2"; 429 | }; 430 | name = Release; 431 | }; 432 | /* End XCBuildConfiguration section */ 433 | 434 | /* Begin XCConfigurationList section */ 435 | 23F2F5042081919B00FC3ED4 /* Build configuration list for PBXProject "JCNavigatorDemo" */ = { 436 | isa = XCConfigurationList; 437 | buildConfigurations = ( 438 | 23F2F51D2081919C00FC3ED4 /* Debug */, 439 | 23F2F51E2081919C00FC3ED4 /* Release */, 440 | ); 441 | defaultConfigurationIsVisible = 0; 442 | defaultConfigurationName = Release; 443 | }; 444 | 23F2F51F2081919C00FC3ED4 /* Build configuration list for PBXNativeTarget "JCNavigatorDemo" */ = { 445 | isa = XCConfigurationList; 446 | buildConfigurations = ( 447 | 23F2F5202081919C00FC3ED4 /* Debug */, 448 | 23F2F5212081919C00FC3ED4 /* Release */, 449 | ); 450 | defaultConfigurationIsVisible = 0; 451 | defaultConfigurationName = Release; 452 | }; 453 | /* End XCConfigurationList section */ 454 | }; 455 | rootObject = 23F2F5012081919B00FC3ED4 /* Project object */; 456 | } 457 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | #import "JCNavigator.h" 12 | #import "JCNavigationController.h" 13 | #import "JCTabBarController.h" 14 | 15 | @interface AppDelegate () 16 | 17 | @end 18 | 19 | @implementation AppDelegate 20 | 21 | #pragma mark - 22 | 23 | - (void)navigatorConfigs 24 | { 25 | [[JCNavigator sharedNavigator] addURLScheme:@"joych" hostList:@[@"com.joych.JCNavigatorDemo"]]; 26 | [[JCNavigator sharedNavigator] setNavigationControllerClass:[JCNavigationController class]]; 27 | } 28 | 29 | - (void)setNavigationBarAppearance 30 | { 31 | UIColor *tintColor = [UIColor whiteColor]; 32 | [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: tintColor, NSFontAttributeName:[UIFont systemFontOfSize:18]}]; 33 | [[UINavigationBar appearance] setBarTintColor:tintColor]; 34 | [[UINavigationBar appearance] setTintColor:tintColor]; 35 | UIImage *image = [self createImageWithColor:[UIColor darkGrayColor]]; 36 | [[UINavigationBar appearance] setBackgroundImage:image forBarMetrics:UIBarMetricsDefault]; 37 | } 38 | 39 | - (UIImage *)createImageWithColor:(UIColor *)color 40 | { 41 | CGRect rect = CGRectMake(0, 0, 1.f, 1.f); 42 | UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0); 43 | CGContextRef context = UIGraphicsGetCurrentContext(); 44 | CGContextSetFillColorWithColor(context, [color CGColor]); 45 | CGContextFillRect(context, rect); 46 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 47 | UIGraphicsEndImageContext(); 48 | return image; 49 | } 50 | 51 | #pragma mark - UIApplicationDelegate 52 | 53 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 54 | // Override point for customization after application launch. 55 | [self navigatorConfigs]; 56 | [self setNavigationBarAppearance]; 57 | 58 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 59 | self.window.backgroundColor = [UIColor whiteColor]; 60 | // [[JCNavigator sharedNavigator] setRootViewController:[ViewController new]]; 61 | // [[JCNavigator sharedNavigator] setRootViewController:[[JCNavigationController alloc] initWithRootViewController:[ViewController new]]]; 62 | [[JCNavigator sharedNavigator] setRootViewController:[JCTabBarController new]]; 63 | [self.window makeKeyAndVisible]; 64 | return YES; 65 | } 66 | 67 | 68 | - (void)applicationWillResignActive:(UIApplication *)application { 69 | // 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. 70 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 71 | } 72 | 73 | 74 | - (void)applicationDidEnterBackground:(UIApplication *)application { 75 | // 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. 76 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 77 | } 78 | 79 | 80 | - (void)applicationWillEnterForeground:(UIApplication *)application { 81 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 82 | } 83 | 84 | 85 | - (void)applicationDidBecomeActive:(UIApplication *)application { 86 | // 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. 87 | } 88 | 89 | 90 | - (void)applicationWillTerminate:(UIApplication *)application { 91 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 92 | } 93 | 94 | - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options 95 | { 96 | __block BOOL opened = NO; 97 | [[JCNavigator sharedNavigator] openURL:url options:options completionHandler:^(BOOL success) { 98 | opened = success; 99 | }]; 100 | return opened; 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/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 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icon_back@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "icon_back@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_back.imageset/icon_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imjoych/JCNavigator/2642dabc7daf82aa795634a2bd30bd493603e32d/JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_back.imageset/icon_back@2x.png -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_back.imageset/icon_back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imjoych/JCNavigator/2642dabc7daf82aa795634a2bd30bd493603e32d/JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_back.imageset/icon_back@3x.png -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icon_close@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "icon_close@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_close.imageset/icon_close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imjoych/JCNavigator/2642dabc7daf82aa795634a2bd30bd493603e32d/JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_close.imageset/icon_close@2x.png -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_close.imageset/icon_close@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imjoych/JCNavigator/2642dabc7daf82aa795634a2bd30bd493603e32d/JCNavigatorDemo/JCNavigatorDemo/Assets.xcassets/icon_close.imageset/icon_close@3x.png -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/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 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/JCNavigationController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCNavigationController.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JCNavigationController : UINavigationController 12 | 13 | @property (nonatomic) BOOL popGestureDisabled; ///< Pop gesture recognizer is disabled. Default NO. 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/JCNavigationController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCNavigationController.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCNavigationController.h" 10 | #import "UIViewController+JCNavigationItem.h" 11 | 12 | @interface JCNavigationController () 13 | 14 | @end 15 | 16 | @implementation JCNavigationController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view. 21 | self.interactivePopGestureRecognizer.delegate = self; 22 | self.interactivePopGestureRecognizer.enabled = NO; 23 | self.delegate = self; 24 | } 25 | 26 | - (void)didReceiveMemoryWarning { 27 | [super didReceiveMemoryWarning]; 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated 32 | { 33 | if (self.viewControllers.count >= 1) { 34 | viewController.hidesBottomBarWhenPushed = YES; 35 | [viewController jc_setupLeftBackBarButtonItem]; 36 | } 37 | [super pushViewController:viewController animated:animated]; 38 | } 39 | 40 | - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion 41 | { 42 | if ([viewControllerToPresent isKindOfClass:[UINavigationController class]]) { 43 | [[(UINavigationController *)viewControllerToPresent visibleViewController] jc_setupLeftCloseBarButtonItem]; 44 | } 45 | [super presentViewController:viewControllerToPresent animated:flag completion:completion]; 46 | } 47 | 48 | - (BOOL)canPopViewController 49 | { 50 | return self.viewControllers.count > 1; 51 | } 52 | 53 | - (BOOL)popGestureEnabled 54 | { 55 | return !self.popGestureDisabled && [self canPopViewController]; 56 | } 57 | 58 | //- (BOOL)shouldAutorotate 59 | //{ 60 | // return NO; 61 | //} 62 | // 63 | //- (UIInterfaceOrientationMask)supportedInterfaceOrientations 64 | //{ 65 | // return UIInterfaceOrientationMaskPortrait; 66 | //} 67 | 68 | - (UIStatusBarStyle)preferredStatusBarStyle 69 | { 70 | return UIStatusBarStyleLightContent; 71 | } 72 | 73 | #pragma mark - UINavigationControllerDelegate 74 | 75 | - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated 76 | { 77 | self.interactivePopGestureRecognizer.enabled = [self popGestureEnabled]; 78 | } 79 | 80 | #pragma mark - UIGestureRecognizerDelegate 81 | 82 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 83 | { 84 | if (gestureRecognizer == self.interactivePopGestureRecognizer) { 85 | return [self popGestureEnabled]; 86 | } 87 | return YES; 88 | } 89 | 90 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 91 | { 92 | return YES; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/JCRootModuleMap.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCRootModuleMap.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCModuleMap.h" 10 | 11 | FOUNDATION_EXPORT NSString *const JCRootMapKey; 12 | 13 | @interface JCRootModuleMap : JCModuleMap 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/JCRootModuleMap.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCRootModuleMap.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCRootModuleMap.h" 10 | #import "JCNavigator.h" 11 | 12 | NSString *const JCRootMapKey = @"JC_root"; 13 | 14 | @implementation JCRootModuleMap 15 | 16 | + (void)initialize 17 | { 18 | if (self == [JCRootModuleMap self]) { 19 | [[JCNavigator sharedNavigator] addModuleMap:[JCRootModuleMap new]]; 20 | } 21 | } 22 | 23 | - (NSDictionary *)classesForMapKeys 24 | { 25 | return @{JCRootMapKey: NSClassFromString(@"ViewController")}; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/JCRootView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCRootView.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/17. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JCRootView : UIView 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame 14 | pushBlock:(void(^)(void))pushBlock 15 | presentBlock:(void(^)(void))presentBlock; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/JCRootView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCRootView.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/17. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCRootView.h" 10 | 11 | @interface JCRootView () 12 | 13 | @property (nonatomic, strong) UIButton *pushButton; 14 | @property (nonatomic, strong) UIButton *presentButton; 15 | @property (nonatomic, copy) void (^pushBlock)(void); 16 | @property (nonatomic, copy) void (^presentBlock)(void); 17 | 18 | @end 19 | 20 | @implementation JCRootView 21 | 22 | - (instancetype)initWithFrame:(CGRect)frame pushBlock:(void (^)(void))pushBlock presentBlock:(void (^)(void))presentBlock 23 | { 24 | if (self = [super initWithFrame:frame]) { 25 | _pushBlock = [pushBlock copy]; 26 | _presentBlock = [presentBlock copy]; 27 | 28 | _pushButton = [UIButton buttonWithType:UIButtonTypeCustom]; 29 | [_pushButton addTarget:self action:@selector(pushAction:) forControlEvents:UIControlEventTouchUpInside]; 30 | [_pushButton setTitle:@"PushViewController" forState:UIControlStateNormal]; 31 | [_pushButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 32 | _pushButton.layer.borderWidth = 1; 33 | _pushButton.layer.borderColor = [UIColor darkGrayColor].CGColor; 34 | _pushButton.layer.masksToBounds = YES; 35 | [self addSubview:_pushButton]; 36 | 37 | _presentButton = [UIButton buttonWithType:UIButtonTypeCustom]; 38 | [_presentButton addTarget:self action:@selector(presentAction:) forControlEvents:UIControlEventTouchUpInside]; 39 | [_presentButton setTitle:@"PresentViewController" forState:UIControlStateNormal]; 40 | [_presentButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 41 | _presentButton.layer.borderWidth = 1; 42 | _presentButton.layer.borderColor = [UIColor darkGrayColor].CGColor; 43 | _presentButton.layer.masksToBounds = YES; 44 | [self addSubview:_presentButton]; 45 | } 46 | return self; 47 | } 48 | 49 | - (void)layoutSubviews 50 | { 51 | [super layoutSubviews]; 52 | CGFloat width = CGRectGetWidth(self.bounds); 53 | CGFloat height = CGRectGetHeight(self.bounds); 54 | _pushButton.bounds = CGRectMake(0, 0, width - 32, 64); 55 | _pushButton.center = CGPointMake(width / 2, height / 2 - 64); 56 | _presentButton.bounds = _pushButton.bounds; 57 | _presentButton.center = CGPointMake(width / 2, height / 2 + 64); 58 | } 59 | 60 | - (void)pushAction:(id)sender 61 | { 62 | if (self.pushBlock) { 63 | self.pushBlock(); 64 | } 65 | } 66 | 67 | - (void)presentAction:(id)sender 68 | { 69 | if (self.presentBlock) { 70 | self.presentBlock(); 71 | } 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/JCTabBarController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCTabBarController.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2021/3/10. 6 | // Copyright © 2021 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface JCTabBarController : UITabBarController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/JCTabBarController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCTabBarController.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2021/3/10. 6 | // Copyright © 2021 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCTabBarController.h" 10 | #import "JCNavigationController.h" 11 | #import "ViewController.h" 12 | #import "JCFirstLevelViewController.h" 13 | 14 | @interface JCTabBarController () 15 | 16 | @end 17 | 18 | @implementation JCTabBarController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | [self configTabBar]; 24 | } 25 | 26 | - (void)configTabBar 27 | { 28 | ViewController *homepageVC = [[ViewController alloc] init]; 29 | homepageVC.title = @"Homepage"; 30 | JCNavigationController *homepageNavi = [[JCNavigationController alloc] initWithRootViewController:homepageVC]; 31 | 32 | JCFirstLevelViewController *firstLevelVC = [[JCFirstLevelViewController alloc] init]; 33 | firstLevelVC.title = @"FirstLevel"; 34 | JCNavigationController *firstLevelNavi = [[JCNavigationController alloc] initWithRootViewController:firstLevelVC]; 35 | 36 | self.viewControllers = @[homepageNavi, firstLevelNavi]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/JCTestModuleMap.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCTestModuleMap.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCModuleMap.h" 10 | 11 | @interface JCTestModuleMap : JCModuleMap 12 | 13 | + (void)openFirstLevelVCPresented:(BOOL)presented 14 | propertiesDict:(NSDictionary *)propertiesDict; 15 | 16 | + (void)openSecondLevelVCPresented:(BOOL)presented; 17 | 18 | + (void)openThirdLevelVCPresented:(BOOL)presented; 19 | 20 | + (void)openContentDetailVCWithCurrentIndex:(NSString *)currentIndex 21 | testId:(NSString *)testId 22 | testArray:(NSArray *)testArray; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/JCTestModuleMap.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCTestModuleMap.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCTestModuleMap.h" 10 | #import "JCNavigator.h" 11 | 12 | static NSString *const JCFirstLevelMapKey = @"JC_firstLevel"; 13 | static NSString *const JCSecondLevelMapKey = @"JC_secondLevel"; 14 | static NSString *const JCThirdLevelMapKey = @"JC_thirdLevel"; 15 | static NSString *const JCContentDetailMapKey = @"JC_contentDetail"; 16 | 17 | @implementation JCTestModuleMap 18 | 19 | + (void)initialize 20 | { 21 | if (self == [JCTestModuleMap self]) { 22 | [[JCNavigator sharedNavigator] addModuleMap:[JCTestModuleMap new]]; 23 | } 24 | } 25 | 26 | + (void)openFirstLevelVCPresented:(BOOL)presented propertiesDict:(NSDictionary *)propertiesDict 27 | { 28 | if (presented) { 29 | [[JCNavigator sharedNavigator] openWithMapKey:JCFirstLevelMapKey propertiesBlock:^NSDictionary *{ 30 | return propertiesDict; 31 | } presented:YES animated:YES]; 32 | return; 33 | } 34 | [[JCNavigator sharedNavigator] openWithMapKey:JCFirstLevelMapKey propertiesBlock:^NSDictionary *{ 35 | return propertiesDict; 36 | }]; 37 | // [[JCNavigator sharedNavigator] openURL:[NSURL URLWithString:@"joych://com.joych.JCNavigatorDemo/firstlevel"]]; 38 | // [[JCNavigator sharedNavigator] openURLString:@"joych://com.joych.JCNavigatorDemo/firstlevel"]; 39 | } 40 | 41 | + (void)openSecondLevelVCPresented:(BOOL)presented 42 | { 43 | if (presented) { 44 | [[JCNavigator sharedNavigator] openWithMapKey:JCSecondLevelMapKey propertiesBlock:nil presented:YES animated:YES]; 45 | return; 46 | } 47 | [[JCNavigator sharedNavigator] openWithMapKey:JCSecondLevelMapKey]; 48 | // [[JCNavigator sharedNavigator] openURL:[NSURL URLWithString:@"joych://com.joych.JCNavigatorDemo/secondlevel"]]; 49 | // [[JCNavigator sharedNavigator] openURLString:@"joych://com.joych.JCNavigatorDemo/secondlevel"]; 50 | } 51 | 52 | + (void)openThirdLevelVCPresented:(BOOL)presented 53 | { 54 | if (presented) { 55 | [[JCNavigator sharedNavigator] openWithMapKey:JCThirdLevelMapKey propertiesBlock:nil presented:YES animated:YES]; 56 | return; 57 | } 58 | [[JCNavigator sharedNavigator] openWithMapKey:JCThirdLevelMapKey]; 59 | // [[JCNavigator sharedNavigator] openURL:[NSURL URLWithString:@"joych://com.joych.JCNavigatorDemo/thirdlevel"]]; 60 | // [[JCNavigator sharedNavigator] openURLString:@"joych://com.joych.JCNavigatorDemo/thirdlevel"]; 61 | } 62 | 63 | + (void)openContentDetailVCWithCurrentIndex:(NSString *)currentIndex testId:(NSString *)testId testArray:(NSArray *)testArray 64 | { 65 | NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:3]; 66 | if ([currentIndex isKindOfClass:[NSString class]]) { 67 | params[@"currentIndex"] = currentIndex; 68 | } 69 | if ([testId isKindOfClass:[NSString class]]) { 70 | params[@"testId"] = testId; 71 | } 72 | if ([testArray isKindOfClass:[NSArray class]]) { 73 | params[@"testArray"] = testArray; 74 | } 75 | [[JCNavigator sharedNavigator] openWithMapKey:JCContentDetailMapKey propertiesBlock:^NSDictionary *{ 76 | return params; 77 | } presented:YES animated:YES]; 78 | } 79 | 80 | - (NSDictionary *)classesForMapKeys 81 | { 82 | return @{JCFirstLevelMapKey: NSClassFromString(@"JCFirstLevelViewController"), 83 | JCSecondLevelMapKey: NSClassFromString(@"JCSecondLevelViewController"), 84 | JCThirdLevelMapKey: NSClassFromString(@"JCThirdLevelViewController"), 85 | JCContentDetailMapKey: NSClassFromString(@"JCContentDetailViewController"), 86 | }; 87 | } 88 | 89 | - (BOOL)presentedForClass:(Class)viewControllerClass 90 | { 91 | if ([viewControllerClass isEqual:NSClassFromString(@"JCContentDetailViewController")]) { 92 | return YES; 93 | } 94 | return NO; 95 | } 96 | 97 | - (NSArray *)reuseViewControllerClasses 98 | { 99 | return @[NSClassFromString(@"JCFirstLevelViewController")]; 100 | } 101 | 102 | - (NSDictionary *)propertiesMapOfURLQueryForClasses 103 | { 104 | return @{@"JCContentDetailViewController": @{@"pageindex": @"currentIndex"}}; 105 | } 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCContentDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCContentDetailViewController.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol JCContentDetailDelegate 12 | 13 | @property (nonatomic, strong) NSString *currentIndex; 14 | @property (nonatomic, strong) NSString *testId; 15 | @property (nonatomic, strong) NSArray *testArray; 16 | 17 | @end 18 | 19 | @interface JCContentDetailViewController : UIViewController 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCContentDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCContentDetailViewController.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCContentDetailViewController.h" 10 | #import "JCTestHeader.h" 11 | #import "JCTestClass.h" 12 | 13 | @interface JCContentDetailViewController () 14 | 15 | @property (nonatomic, strong) JCTestView *testView; 16 | 17 | @end 18 | 19 | @implementation JCContentDetailViewController 20 | @synthesize currentIndex, testId, testArray; 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | // Do any additional setup after loading the view. 25 | self.title = [NSString stringWithFormat:@"%@_%@", NSStringFromClass([self class]), self.currentIndex]; 26 | self.view.backgroundColor = [UIColor whiteColor]; 27 | 28 | _testView = [[JCTestView alloc] initWithFrame:CGRectZero pushBlock:^{ 29 | if ([self.currentIndex integerValue] == 3) { 30 | [[JCNavigator sharedNavigator] dismissAndPopToRootViewControllerCompletion:nil]; 31 | return; 32 | } 33 | [self openViewControllerPresented:NO]; 34 | } presentBlock:^{ 35 | [self openViewControllerPresented:YES]; 36 | }]; 37 | [_testView setTestObject:[self testObject]]; 38 | [self.view addSubview:_testView]; 39 | } 40 | 41 | - (void)viewWillLayoutSubviews 42 | { 43 | [super viewWillLayoutSubviews]; 44 | self.testView.frame = CGRectMake(0, 64, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 64); 45 | } 46 | 47 | - (void)didReceiveMemoryWarning { 48 | [super didReceiveMemoryWarning]; 49 | // Dispose of any resources that can be recreated. 50 | } 51 | 52 | - (void)openViewControllerPresented:(BOOL)presented 53 | { 54 | switch ([self.currentIndex integerValue]) { 55 | case 0: 56 | [JCTestModuleMap openFirstLevelVCPresented:presented propertiesDict:@{@"comeFrom": @"Root Detail"}]; 57 | break; 58 | case 1: 59 | [JCTestModuleMap openSecondLevelVCPresented:presented]; 60 | break; 61 | case 2: 62 | [JCTestModuleMap openThirdLevelVCPresented:presented]; 63 | break; 64 | case 3: 65 | [JCTestModuleMap openFirstLevelVCPresented:presented propertiesDict:@{@"comeFrom": @"Third Level Detail"}]; 66 | break; 67 | default: 68 | [JCTestModuleMap openFirstLevelVCPresented:presented propertiesDict:@{}]; 69 | break; 70 | } 71 | } 72 | 73 | - (JCTestClass *)testObject 74 | { 75 | JCTestClass *testObject = nil; 76 | if (self.testId || self.testArray) { 77 | testObject = [JCTestClass new]; 78 | testObject.testId = self.testId; 79 | testObject.testArray = self.testArray; 80 | } 81 | return testObject; 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCFirstLevelViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCFirstLevelViewController.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol JCFirstLevelVCDelegate 12 | 13 | @property (nonatomic, strong) NSString *comeFrom; 14 | 15 | @end 16 | 17 | @interface JCFirstLevelViewController : UIViewController 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCFirstLevelViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCFirstLevelViewController.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCFirstLevelViewController.h" 10 | #import "JCTestHeader.h" 11 | #import "JCTestClass.h" 12 | 13 | @interface JCFirstLevelViewController () 14 | 15 | @property (nonatomic, strong) JCTestView *testView; 16 | 17 | @end 18 | 19 | @implementation JCFirstLevelViewController 20 | @synthesize comeFrom = _comeFrom; 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | // Do any additional setup after loading the view. 25 | self.title = NSStringFromClass([self class]); 26 | self.view.backgroundColor = [UIColor whiteColor]; 27 | _testView = [[JCTestView alloc] initWithFrame:CGRectZero pushBlock:^{ 28 | [JCTestModuleMap openSecondLevelVCPresented:NO]; 29 | } presentBlock:^{ 30 | [JCTestModuleMap openContentDetailVCWithCurrentIndex:@"1" testId:nil testArray:nil]; 31 | // [[JCNavigator sharedNavigator] openURLString:@"joych://com.joych.jcnavigatordemo/contentdetail?pageindex=1"]; 32 | }]; 33 | [_testView setTestObject:[self testObject]]; 34 | [self.view addSubview:_testView]; 35 | } 36 | 37 | - (void)viewWillLayoutSubviews 38 | { 39 | [super viewWillLayoutSubviews]; 40 | self.testView.frame = CGRectMake(0, 64, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 64); 41 | } 42 | 43 | - (void)didReceiveMemoryWarning { 44 | [super didReceiveMemoryWarning]; 45 | // Dispose of any resources that can be recreated. 46 | } 47 | 48 | - (void)setComeFrom:(NSString *)comeFrom 49 | { 50 | _comeFrom = comeFrom; 51 | [self.testView setTestObject:[self testObject]]; 52 | } 53 | 54 | - (JCTestClass *)testObject 55 | { 56 | JCTestClass *testObject = [JCTestClass new]; 57 | testObject.testId = @"FirstLevel"; 58 | testObject.testArray = @[@"come", @"from", self.comeFrom ?:@"Mars!"]; 59 | return testObject; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCSecondLevelViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCSecondLevelViewController.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JCSecondLevelViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCSecondLevelViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCSecondLevelViewController.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCSecondLevelViewController.h" 10 | #import "JCTestHeader.h" 11 | 12 | @interface JCSecondLevelViewController () 13 | 14 | @property (nonatomic, strong) JCTestView *testView; 15 | @property (nonatomic, strong) UIImage *navigationBarBGImage; 16 | 17 | @end 18 | 19 | @implementation JCSecondLevelViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view. 24 | self.title = NSStringFromClass([self class]); 25 | self.view.backgroundColor = [UIColor whiteColor]; 26 | 27 | _testView = [[JCTestView alloc] initWithFrame:CGRectZero pushBlock:^{ 28 | [JCTestModuleMap openThirdLevelVCPresented:NO]; 29 | } presentBlock:^{ 30 | [self showAlert]; 31 | }]; 32 | [self.view addSubview:_testView]; 33 | } 34 | 35 | - (void)viewWillLayoutSubviews 36 | { 37 | [super viewWillLayoutSubviews]; 38 | self.testView.frame = CGRectMake(0, 64, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 64); 39 | } 40 | 41 | - (void)didReceiveMemoryWarning { 42 | [super didReceiveMemoryWarning]; 43 | // Dispose of any resources that can be recreated. 44 | } 45 | 46 | - (void)viewWillAppear:(BOOL)animated 47 | { 48 | [super viewWillAppear:animated]; 49 | self.navigationBarBGImage = [self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault]; 50 | UIImage *image = [self createImageWithColor:[[UIColor blueColor] colorWithAlphaComponent:0.6]]; 51 | [self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault]; 52 | } 53 | 54 | - (void)viewWillDisappear:(BOOL)animated 55 | { 56 | [super viewWillDisappear:animated]; 57 | [self.navigationController.navigationBar setBackgroundImage:self.navigationBarBGImage forBarMetrics:UIBarMetricsDefault]; 58 | } 59 | 60 | - (void)showAlert 61 | { 62 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Tips" message:@"Open ApplicationSettings or contentDetailViewController_2 ?" preferredStyle:UIAlertControllerStyleAlert]; 63 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Settings" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 64 | [[JCNavigator sharedNavigator] openURLString:UIApplicationOpenSettingsURLString]; 65 | }]; 66 | UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"Detail" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 67 | [JCTestModuleMap openContentDetailVCWithCurrentIndex:@"2" testId:nil testArray:nil]; 68 | }]; 69 | [alertController addAction:cancelAction]; 70 | [alertController addAction:confirmAction]; 71 | [[JCNavigator sharedNavigator] presentViewController:alertController animated:YES completion:nil]; 72 | // [self.parentViewController presentViewController:alertController animated:YES completion:nil]; 73 | } 74 | 75 | - (UIImage *)createImageWithColor:(UIColor *)color 76 | { 77 | CGRect rect = CGRectMake(0, 0, 1.f, 1.f); 78 | UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0); 79 | CGContextRef context = UIGraphicsGetCurrentContext(); 80 | CGContextSetFillColorWithColor(context, [color CGColor]); 81 | CGContextFillRect(context, rect); 82 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 83 | UIGraphicsEndImageContext(); 84 | return image; 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCTestClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCTestClass.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/16. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JCTestClass : NSObject 12 | 13 | @property (nonatomic, strong) NSString *testId; 14 | @property (nonatomic, strong) NSArray *testArray; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCTestClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCTestClass.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/16. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCTestClass.h" 10 | 11 | @implementation JCTestClass 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCTestHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCTestHeader.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #ifndef JCTestHeader_h 10 | #define JCTestHeader_h 11 | 12 | #import "JCNavigator.h" 13 | #import "JCTestModuleMap.h" 14 | #import "JCTestView.h" 15 | 16 | #endif /* JCTestHeader_h */ 17 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCTestView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCTestView.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class JCTestClass; 12 | 13 | @interface JCTestView : UIView 14 | 15 | - (instancetype)initWithFrame:(CGRect)frame 16 | pushBlock:(void(^)(void))pushBlock 17 | presentBlock:(void(^)(void))presentBlock; 18 | 19 | - (void)setTestObject:(JCTestClass *)testObject; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCTestView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCTestView.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCTestView.h" 10 | #import "JCTestClass.h" 11 | 12 | @interface JCTestView () 13 | 14 | @property (nonatomic, strong) UIButton *pushButton; 15 | @property (nonatomic, strong) UIButton *presentButton; 16 | @property (nonatomic, copy) void (^pushBlock)(void); 17 | @property (nonatomic, copy) void (^presentBlock)(void); 18 | @property (nonatomic, strong) UILabel *testLabel; 19 | 20 | @end 21 | 22 | @implementation JCTestView 23 | 24 | - (instancetype)initWithFrame:(CGRect)frame pushBlock:(void (^)(void))pushBlock presentBlock:(void (^)(void))presentBlock 25 | { 26 | if (self = [super initWithFrame:frame]) { 27 | _pushBlock = [pushBlock copy]; 28 | _presentBlock = [presentBlock copy]; 29 | 30 | _pushButton = [UIButton buttonWithType:UIButtonTypeCustom]; 31 | [_pushButton addTarget:self action:@selector(pushAction:) forControlEvents:UIControlEventTouchUpInside]; 32 | [_pushButton setTitle:@"PushViewController" forState:UIControlStateNormal]; 33 | [_pushButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 34 | _pushButton.layer.borderWidth = 1; 35 | _pushButton.layer.borderColor = [UIColor darkGrayColor].CGColor; 36 | _pushButton.layer.masksToBounds = YES; 37 | [self addSubview:_pushButton]; 38 | 39 | _presentButton = [UIButton buttonWithType:UIButtonTypeCustom]; 40 | [_presentButton addTarget:self action:@selector(presentAction:) forControlEvents:UIControlEventTouchUpInside]; 41 | [_presentButton setTitle:@"PresentViewController" forState:UIControlStateNormal]; 42 | [_presentButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 43 | _presentButton.layer.borderWidth = 1; 44 | _presentButton.layer.borderColor = [UIColor darkGrayColor].CGColor; 45 | _presentButton.layer.masksToBounds = YES; 46 | [self addSubview:_presentButton]; 47 | } 48 | return self; 49 | } 50 | 51 | - (void)layoutSubviews 52 | { 53 | [super layoutSubviews]; 54 | CGFloat width = CGRectGetWidth(self.bounds); 55 | CGFloat height = CGRectGetHeight(self.bounds); 56 | _pushButton.bounds = CGRectMake(0, 0, width - 32, 64); 57 | _pushButton.center = CGPointMake(width / 2, height / 2 - 64); 58 | _presentButton.bounds = _pushButton.bounds; 59 | _presentButton.center = CGPointMake(width / 2, height / 2 + 64); 60 | _testLabel.frame = CGRectMake(16, CGRectGetMinY(_pushButton.frame) - 96, CGRectGetWidth(self.bounds) - 32, 96); 61 | } 62 | 63 | - (void)pushAction:(id)sender 64 | { 65 | if (self.pushBlock) { 66 | self.pushBlock(); 67 | } 68 | } 69 | 70 | - (void)presentAction:(id)sender 71 | { 72 | if (self.presentBlock) { 73 | self.presentBlock(); 74 | } 75 | } 76 | 77 | - (void)setTestObject:(JCTestClass *)testObject 78 | { 79 | if (!testObject) { 80 | return; 81 | } 82 | NSString *text = testObject.testId ?:@""; 83 | for (NSString *string in testObject.testArray) { 84 | text = [NSString stringWithFormat:@"%@ %@", text, string]; 85 | } 86 | self.testLabel.text = text; 87 | } 88 | 89 | - (UILabel *)testLabel 90 | { 91 | if (!_testLabel) { 92 | _testLabel = [UILabel new]; 93 | _testLabel.textColor = [[UIColor blueColor] colorWithAlphaComponent:0.9]; 94 | _testLabel.font = [UIFont boldSystemFontOfSize:18]; 95 | _testLabel.textAlignment = NSTextAlignmentCenter; 96 | _testLabel.numberOfLines = 2; 97 | [self addSubview:_testLabel]; 98 | } 99 | return _testLabel; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCThirdLevelViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JCThirdLevelViewController.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JCThirdLevelViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/TestModule/Private/JCThirdLevelViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JCThirdLevelViewController.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "JCThirdLevelViewController.h" 10 | #import "JCTestHeader.h" 11 | 12 | @interface JCThirdLevelViewController () 13 | 14 | @property (nonatomic, strong) JCTestView *testView; 15 | 16 | @end 17 | 18 | @implementation JCThirdLevelViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | self.title = NSStringFromClass([self class]); 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | _testView = [[JCTestView alloc] initWithFrame:CGRectZero pushBlock:^{ 27 | [JCTestModuleMap openFirstLevelVCPresented:NO propertiesDict:@{@"comeFrom": @"Third Level"}]; 28 | } presentBlock:^{ 29 | [JCTestModuleMap openContentDetailVCWithCurrentIndex:@"3" testId:@"hahaha666" testArray:@[@"Hello", @"world", @"!"]]; 30 | }]; 31 | [self.view addSubview:_testView]; 32 | } 33 | 34 | - (void)viewWillLayoutSubviews 35 | { 36 | [super viewWillLayoutSubviews]; 37 | self.testView.frame = CGRectMake(0, 64, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 64); 38 | } 39 | 40 | - (void)didReceiveMemoryWarning { 41 | [super didReceiveMemoryWarning]; 42 | // Dispose of any resources that can be recreated. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/UIViewController+JCNavigationItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+JCNavigationItem.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIViewController (JCNavigationItem) 12 | 13 | - (UIButton *)jc_setupLeftBackBarButtonItem; 14 | 15 | - (UIButton *)jc_setupLeftCloseBarButtonItem; 16 | 17 | - (void)jc_leftBackButtonPressed:(id)sender; 18 | 19 | - (void)jc_leftCloseButtonPressed:(id)sender; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/UIViewController+JCNavigationItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+JCNavigationItem.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+JCNavigationItem.h" 10 | #import "JCNavigator.h" 11 | 12 | @implementation UIViewController (JCNavigationItem) 13 | 14 | - (UIButton *)jc_setupLeftBackBarButtonItem 15 | { 16 | UIButton *button = [self buttonWithTitle:nil 17 | image:[UIImage imageNamed:@"icon_back"] 18 | highLightedImage:[UIImage imageNamed:@"icon_back"]]; 19 | [button addTarget:self 20 | action:@selector(jc_leftBackButtonPressed:) 21 | forControlEvents:UIControlEventTouchUpInside]; 22 | button.frame = CGRectMake(0, 0, 40, 44); 23 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button]; 24 | return button; 25 | } 26 | 27 | - (UIButton *)jc_setupLeftCloseBarButtonItem 28 | { 29 | UIButton *button = [self buttonWithTitle:nil 30 | image:[UIImage imageNamed:@"icon_close"] 31 | highLightedImage:[UIImage imageNamed:@"icon_close"]]; 32 | [button addTarget:self 33 | action:@selector(jc_leftCloseButtonPressed:) 34 | forControlEvents:UIControlEventTouchUpInside]; 35 | button.frame = CGRectMake(0, 0, 40, 44); 36 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button]; 37 | return button; 38 | } 39 | 40 | - (void)jc_leftBackButtonPressed:(id)sender 41 | { 42 | [[JCNavigator sharedNavigator] popViewControllerAnimated:YES]; 43 | } 44 | 45 | - (void)jc_leftCloseButtonPressed:(id)sender 46 | { 47 | [[JCNavigator sharedNavigator] dismissViewControllerAnimated:YES completion:nil]; 48 | } 49 | 50 | #pragma mark - 51 | 52 | - (UIButton *)buttonWithTitle:(NSString *)title 53 | image:(UIImage *)image 54 | highLightedImage:(UIImage *)highLightedImage 55 | { 56 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 57 | title = [title stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 58 | if (title.length > 0) { 59 | [button setTitle:title forState:UIControlStateNormal]; 60 | button.titleLabel.font = [UIFont systemFontOfSize:16.f]; 61 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 62 | [button setTitleColor:[[UIColor whiteColor] colorWithAlphaComponent:0.7f] forState:UIControlStateDisabled]; 63 | button.frame = CGRectMake(0, 0, title.length * 16 + 8, 28); 64 | } 65 | if (image) { 66 | if (!highLightedImage) { 67 | highLightedImage = image; 68 | } 69 | [button setImage:image forState:UIControlStateNormal]; 70 | [button setImage:highLightedImage forState:UIControlStateHighlighted]; 71 | [button sizeToFit]; 72 | } 73 | return button; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "JCTestModuleMap.h" 11 | #import "JCRootView.h" 12 | 13 | @interface ViewController () 14 | 15 | @property (nonatomic, strong) JCRootView *rootView; 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view, typically from a nib. 24 | self.title = NSStringFromClass([self class]); 25 | self.view.backgroundColor = [UIColor whiteColor]; 26 | 27 | _rootView = [[JCRootView alloc] initWithFrame:CGRectZero pushBlock:^{ 28 | [JCTestModuleMap openFirstLevelVCPresented:NO propertiesDict:@{@"comeFrom": @"Root"}]; 29 | } presentBlock:^{ 30 | [JCTestModuleMap openContentDetailVCWithCurrentIndex:@"0" testId:nil testArray:nil]; 31 | }]; 32 | [self.view addSubview:_rootView]; 33 | } 34 | 35 | - (void)viewWillLayoutSubviews 36 | { 37 | [super viewWillLayoutSubviews]; 38 | self.rootView.frame = CGRectMake(0, 64, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 64); 39 | } 40 | 41 | - (void)didReceiveMemoryWarning { 42 | [super didReceiveMemoryWarning]; 43 | // Dispose of any resources that can be recreated. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /JCNavigatorDemo/JCNavigatorDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JCNavigatorDemo 4 | // 5 | // Created by ChenJianjun on 2018/5/5. 6 | // Copyright © 2018 Joych. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Joych 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JCNavigator 2 | A decoupled navigator framework of jumping between modules or apps for iOS development. 3 | 4 | ## Features 5 | This framework supports the development of iOS 8.0+ in ARC. 6 | 7 | * JCNavigator configs. 8 | * Implement module maps. 9 | * Jump operations with method openURL: or openWithMapKey:. 10 | * Parameters passing between modules. 11 | 12 | ### JCNavigator configs 13 | 14 | Set URL jump rules with hostList for scheme 15 | ```objective-c 16 | [[JCNavigator sharedNavigator] addURLScheme:@"joych" hostList:@[@"com.joych.JCNavigatorDemo"]]; 17 | ``` 18 | 19 | Set rootViewController 20 | ```objective-c 21 | ViewController *vc = [[ViewController alloc] init]; 22 | [[JCNavigator sharedNavigator] setRootViewController:vc]; 23 | ``` 24 | 25 | ### Implement module maps 26 | 27 | JCTestModuleMap class is declared as the subclass of JCModuleMap. 28 | * Map key should be defined with the same prefix and appended with "_". 29 | * Map key will be used in the category of JCNavigator which associated with this module. 30 | * Implemented interfaces which are used for jumps between modules. 31 | ```objective-c 32 | // JCTestModuleMap.h 33 | 34 | FOUNDATION_EXPORT NSString *const JCFirstLevelMapKey; 35 | FOUNDATION_EXPORT NSString *const JCSecondLevelMapKey; 36 | FOUNDATION_EXPORT NSString *const JCThirdLevelMapKey; 37 | FOUNDATION_EXPORT NSString *const JCContentDetailMapKey; 38 | 39 | @interface JCTestModuleMap : JCModuleMap 40 | 41 | + (void)openFirstLevelVCPresented:(BOOL)presented 42 | propertiesDict:(NSDictionary *)propertiesDict; 43 | 44 | + (void)openSecondLevelVCPresented:(BOOL)presented; 45 | 46 | + (void)openThirdLevelVCPresented:(BOOL)presented; 47 | 48 | + (void)openContentDetailViewControllerWithCurrentIndex:(NSString *)currentIndex 49 | testId:(NSString *)testId 50 | testArray:(NSArray *)testArray; 51 | @end 52 | ``` 53 | ```objective-c 54 | // JCTestModuleMap.m 55 | 56 | NSString *const JCFirstLevelMapKey = @"JC_firstLevel"; 57 | NSString *const JCSecondLevelMapKey = @"JC_secondLevel"; 58 | NSString *const JCThirdLevelMapKey = @"JC_thirdLevel"; 59 | NSString *const JCContentDetailMapKey = @"JC_contentDetail"; 60 | 61 | @implementation JCTestModuleMap 62 | 63 | + (void)initialize 64 | { 65 | [[JCNavigator sharedNavigator] addModuleMap:[JCTestModuleMap new]]; 66 | } 67 | 68 | + (void)openFirstLevelVCPresented:(BOOL)presented propertiesDict:(NSDictionary *)propertiesDict 69 | { 70 | if (presented) { 71 | [[JCNavigator sharedNavigator] openWithMapKey:JCFirstLevelMapKey propertiesBlock:^NSDictionary *{ 72 | return propertiesDict; 73 | } presented:YES animated:YES]; 74 | return; 75 | } 76 | [[JCNavigator sharedNavigator] openWithMapKey:JCFirstLevelMapKey propertiesBlock:^NSDictionary *{ 77 | return propertiesDict; 78 | }]; 79 | // [[JCNavigator sharedNavigator] openURL:[NSURL URLWithString:@"joych://com.joych.JCNavigatorDemo/firstlevel"]]; 80 | // [[JCNavigator sharedNavigator] openURLString:@"joych://com.joych.JCNavigatorDemo/firstlevel"]; 81 | } 82 | 83 | + (void)openSecondLevelVCPresented:(BOOL)presented 84 | { 85 | if (presented) { 86 | [[JCNavigator sharedNavigator] openWithMapKey:JCSecondLevelMapKey propertiesBlock:nil presented:YES animated:YES]; 87 | return; 88 | } 89 | [[JCNavigator sharedNavigator] openWithMapKey:JCSecondLevelMapKey]; 90 | // [[JCNavigator sharedNavigator] openURL:[NSURL URLWithString:@"joych://com.joych.JCNavigatorDemo/secondlevel"]]; 91 | // [[JCNavigator sharedNavigator] openURLString:@"joych://com.joych.JCNavigatorDemo/secondlevel"]; 92 | } 93 | 94 | + (void)openThirdLevelVCPresented:(BOOL)presented 95 | { 96 | if (presented) { 97 | [[JCNavigator sharedNavigator] openWithMapKey:JCThirdLevelMapKey propertiesBlock:nil presented:YES animated:YES]; 98 | return; 99 | } 100 | [[JCNavigator sharedNavigator] openWithMapKey:JCThirdLevelMapKey]; 101 | // [[JCNavigator sharedNavigator] openURL:[NSURL URLWithString:@"joych://com.joych.JCNavigatorDemo/thirdlevel"]]; 102 | // [[JCNavigator sharedNavigator] openURLString:@"joych://com.joych.JCNavigatorDemo/thirdlevel"]; 103 | } 104 | 105 | + (void)openContentDetailViewControllerWithCurrentIndex:(NSString *)currentIndex testId:(NSString *)testId testArray:(NSArray *)testArray 106 | { 107 | NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:3]; 108 | if ([currentIndex isKindOfClass:[NSString class]]) { 109 | params[@"currentIndex"] = currentIndex; 110 | } 111 | if ([testId isKindOfClass:[NSString class]]) { 112 | params[@"testId"] = testId; 113 | } 114 | if ([testArray isKindOfClass:[NSArray class]]) { 115 | params[@"testArray"] = testArray; 116 | } 117 | [[JCNavigator sharedNavigator] openWithMapKey:JCContentDetailMapKey propertiesBlock:^NSDictionary *{ 118 | return params; 119 | } presented:YES animated:YES]; 120 | } 121 | 122 | - (NSDictionary *)classesForMapKeys 123 | { 124 | return @{JCFirstLevelMapKey: NSClassFromString(@"JCFirstLevelViewController"), 125 | JCSecondLevelMapKey: NSClassFromString(@"JCSecondLevelViewController"), 126 | JCThirdLevelMapKey: NSClassFromString(@"JCThirdLevelViewController"), 127 | JCContentDetailMapKey: NSClassFromString(@"JCContentDetailViewController"), 128 | }; 129 | } 130 | 131 | - (BOOL)presentedForClass:(Class)viewControllerClass 132 | { 133 | if ([viewControllerClass isEqual:NSClassFromString(@"JCContentDetailViewController")]) { 134 | return YES; 135 | } 136 | return NO; 137 | } 138 | 139 | - (NSArray *)reuseViewControllerClasses 140 | { 141 | return @[NSClassFromString(@"JCFirstLevelViewController")]; 142 | } 143 | 144 | - (NSDictionary *)propertiesMapOfURLQueryForClasses 145 | { 146 | return @{@"JCContentDetailViewController": @{@"pageindex": @"currentIndex"}}; 147 | } 148 | 149 | @end 150 | ``` 151 | 152 | ### Jump operations with method openURL: or openWithMapKey: 153 | 154 | Open URL between apps or modules. 155 | ```objective-c 156 | - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options 157 | { 158 | return [[JCNavigator sharedNavigator] openURL:url options:options]; 159 | } 160 | ``` 161 | ```objective-c 162 | [[JCNavigator sharedNavigator] openURL:[NSURL URLWithString:@"joych://com.joych.JCNavigatorDemo/firstlevel"]]; 163 | [[JCNavigator sharedNavigator] openURLString:@"joych://com.joych.jcnavigatordemo/contentdetail?pageindex=1"]; 164 | ``` 165 | 166 | ### Parameters passing between modules 167 | 168 | Parameters passing between modules is realized by properties assignment. 169 | * Properties are suggested to be declared as NSString class because openURL: method only supports this data type. 170 | * Properties also can be declared as NSArray / NSDictionary / NSSet / UIImage and so on data types, which can be used with openWithMapKey:propertiesBlock: method. For decoupling between modules, although you can use a custom object, it is not recommended. 171 | ```objective-c 172 | // JCContentDetailViewController.h 173 | 174 | @protocol JCContentDetailDelegate 175 | 176 | @property (nonatomic, strong) NSString *currentIndex; 177 | @property (nonatomic, strong) NSString *testId; 178 | @property (nonatomic, strong) NSArray *testArray; 179 | 180 | @end 181 | 182 | @interface JCContentDetailViewController : UIViewController 183 | 184 | @end 185 | ``` 186 | 187 | ## CocoaPods 188 | To integrate JCNavigator into your iOS project, specify it in your Podfile: 189 | 190 | pod 'JCNavigator' 191 | 192 | ## Contacts 193 | If you have any questions or suggestions about the framework, please E-mail to contact me. 194 | 195 | Author: [Joych](https://github.com/imjoych) 196 | E-mail: imjoych@gmail.com 197 | 198 | ## License 199 | JCNavigator is released under the [MIT License](https://github.com/imjoych/JCNavigator/blob/master/LICENSE). 200 | --------------------------------------------------------------------------------