路由,各个模块之间的调度,跳转。
iOS路由,其实就是一单例类,使用需通过注册组件,调用方通过URL调用服务方页面。通过路由表的映射关系进行关联,调用方可以传入复 杂的参数、对象等。
基本跳转主要是注册组件,调用openURl方法,传入参数即可实现。
反向传值亦可通过参数传递。
JLRoutes路由,控制系统中所有的跳转,目前系统最基本的跳转有三种:
1,Tabbar – Selected;
2,UINavigationController – Push&Pop;
3,UIViewController(UINavigationController)- modal – Present&Dismiss。
JLRoutes是一第三方库,主要保存了当前注册的页面信息,例如class,params,等。
首先它会将所有的类信息封装成一个字典,然后保存在字典里边,键值分别为类信息和类class字符串,注册到路由中之后,等到发送消息时,会返回类的消息,然后可进行操纵。
当当前vc信息注册成功后,再次给vc发送消息时,连通vc的所有保存信息进行返回,然后获取到信息之后再进行操作,其实就是对跳转的一种数据保存然后进行的封装。
最大的好处就是保存字符串,使其内存不会暴增,在运行速率上跟常规没什么两样。
对传参也进行了简洁封装。
回调进行了简洁封装,能保证日常使用,如果有定制化需求,只需要往路由中注册不同的规则即可。
封装后使用:
//头文件 #import "SkyRouter+Handle.h" //push [SkyRouter openURL:SkyRouterSecondCtrl parameters:@{SkyRouteSegueType: SkyRouteSeguePush,@"delegate":self}]; //模态并且传递了block void(^complate)(void) = ^{ NSLog(@"complate"); }; [SkyRouter openURL:SkyRouterSecondCtrl parameters:@{SkyRouteSegueType: SkyRouteSegueModal, SkyRouteSegueNeedNavigation: @(YES),SkyRouteModalCompletionHandleKey:complate,@"delegate":self}]; // //pop __weak typeof(self) weakSelf = self; void(^popEndHandle)(void) = ^(){ if(weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(delegateEvent:)]){ [weakSelf.delegate delegateEvent:@"这是代理内容"]; } }; //pop [SkyRouter openURL:SkyRouteSegueBack parameters:@{SkyRouteSegueBackType:SkyRouteSegueBackPop,SkyRoutePopCompletionBlock:popEndHandle}]; //等价于 //[SkyRouter openURL:SkyRouteSegueBack parameters:@{SkyRoutePopCompletionBlock:popEndHandle}]; // //dismiss void(^dismissHandle)(void) = ^(){ NSLog(@"%@",@"ssssssssssssssssss"); if(self.delegate && [self.delegate respondsToSelector:@selector(delegateEvent:)]){ [self.delegate delegateEvent:@"这是代理内容"]; } }; [SkyRouter openURL:SkyRouteSegueBack parameters:@{SkyRouteSegueBackType:SkyRouteSegueBackDismiss,SkyRouteDismissCompletionHandleKey:dismissHandle,@"delegate":self}];
具体代码如下:
// // SkyRouter+Handle.m // RouterDemo // // Created by skyzizhu on 2020/12/12. // #import "SkyRouter+Handle.h" #import "UIViewController+Index.h" #import "UIViewController+PopHandle.h" @implementation SkyRouter (Handle) + (void)load { //系统链接的时候,注册到里边 static dispatch_once_t onceQueue; dispatch_once(&onceQueue, ^{ [self performSelectorOnMainThread:@selector(sky_registerRouter) withObject:nil waitUntilDone:false]; }); } + (void)sky_registerRouter { //获取全局 RouterMapInfo NSDictionary* routerMapInfo = [SkyRouterMap skyRouterMapInfo]; // router 对应控制器路径, 使用其来注册 Route, 当调用当前 Route 时会执行回调; 回调参数 parameters: 在执行 Route 时传入的参数; for (NSString* router in routerMapInfo.allKeys) { NSDictionary* routerMap = routerMapInfo[router]; NSString* className = routerMap[SkyRouteClassName]; if (IsStringClass(className)) { /*注册所有控制器 Router, 使用 [... openURL:...]; push 到 AppearVC; [... openURL: parameters:@{kJSDVCRouteSegue: RouteSegueModal, @"name": @"jersey"}]; Modal 到 Appear VC 并携带参数 name; */ [self addRoute:router handler:^BOOL(NSDictionary * _Nonnull parameters) { //执行路由匹配成功之后,跳转逻辑回调; // NSMutableDictionary* mapInfo = [NSMutableDictionary dictionaryWithDictionary:parameters]; // // [mapInfo setValue:className forKey:kJSDVCRouteClassName]; // [mapInfo setValue:routerMap[kJSDVCRouteClassTitle] forKey:kJSDVCRouteClassTitle]; // [mapInfo setValue:routerMap[kJSDVCRouteClassFlags] forKey:kJSDVCRouteClassFlags]; // [mapInfo setValue:@(needLogin) forKey:kJSDVCRouteNeedLogin]; /*执行 Route 回调; 处理控制器跳转 + 传参; ** routerMap: 当前 route 映射的 routeMap; 我们在 RouterConfig 配置的 Map; ** parameters: 调用 route 时, 传入的参数; */ return [self executeRouterClassName:className routerMap:routerMap parameters:parameters]; }]; } } // 注册 Router 到指定TabBar Index; 使用 [ openURL:SkyRouteMineTab] 切换到 mine Index [self addRoute:@"/rootTab/:index" handler:^BOOL(NSDictionary * _Nonnull parameters) { NSInteger index = [parameters[@"index"] integerValue]; // 处理 UITabBarControllerIndex 切换; UITabBarController* tabBarVC = (UITabBarController* )[UIViewController index_rootViewController]; if ([tabBarVC isKindOfClass:[UITabBarController class]] && index >= 0 && tabBarVC.viewControllers.count >= index) { UIViewController* indexVC = tabBarVC.viewControllers[index]; if ([indexVC isKindOfClass:[UINavigationController class]]) { indexVC = ((UINavigationController *)indexVC).topViewController; } //传参 [self setupParameters:parameters forViewController:indexVC]; tabBarVC.selectedIndex = index; return YES; } else { return NO; } }]; // 注册返回上层页面 Router, 使用 [... parameters:@{kJSDVCRouteBackIndex: @(2)}]返回前两页 [self addRoute:SkyRouteSegueBack handler:^BOOL(NSDictionary * _Nonnull parameters) { return [self executeBackRouterParameters:parameters]; }]; } #pragma mark - execute Router VC // 当查找到指定 Router 时, 触发路由回调逻辑; 找不到已注册 Router 则直接返回 NO; 如需要的话, 也可以在这里注册一个全局未匹配到 Router 执行的回调进行异常处理; + (BOOL)executeRouterClassName:(NSString *)className routerMap:(NSDictionary* )routerMap parameters:(NSDictionary* )parameters { BOOL userIsLogin = NO;//获取当前是否登录 // 拦截 Router 映射参数,是否需要登录才可跳转; BOOL needLogin = [routerMap[SkyRouteClassNeedLogin] boolValue]; if (needLogin && !userIsLogin) { //这里打开注册的login页面 [SkyRouter openURL:@""]; return NO; } //统一初始化控制器,传参和跳转; UIViewController* vc = [self viewControllerWithClassName:className routerMap:routerMap parameters: parameters]; if (vc) { [self gotoViewController:vc parameters:parameters]; return YES; } else { return NO; } } // 根据 Router 映射到的类名实例化控制器; + (UIViewController *)viewControllerWithClassName:(NSString *)className routerMap:(NSDictionary *)routerMap parameters:(NSDictionary* )parameters { id vc = [[NSClassFromString(className) alloc] init]; if (![vc isKindOfClass:[UIViewController class]]) { vc = nil; } #if DEBUG //vc不是UIViewController NSAssert(vc, @"%s: %@ is not kind of UIViewController class, routerMap: %@",__func__ ,className, routerMap); #endif //参数赋值 [self setupParameters:parameters forViewController:vc]; return vc; } // 对 VC 参数赋值 + (void)setupParameters:(NSDictionary *)params forViewController:(UIViewController* )vc { for (NSString *key in params.allKeys) { BOOL hasKey = [vc respondsToSelector:NSSelectorFromString(key)]; BOOL notNil = params[key] != nil; if (hasKey && notNil) { [vc setValue:params[key] forKey:key]; } #if DEBUG //vc没有相应属性,但却传了值 if ([key hasPrefix:@"JLRouteURL"]==NO && [key hasPrefix:@"JLRouteURL"]==NO && [params[@"JLRoutePattern"] rangeOfString:[NSString stringWithFormat:@":%@",key]].location==NSNotFound) { //NSAssert(hasKey == YES, @"%s: %@ is not property for the key %@",__func__ ,vc,key); } #endif }; } // 跳转和参数设置; + (void)gotoViewController:(UIViewController *)vc parameters:(NSDictionary *)parameters { UIViewController* currentVC = [UIViewController index_findVisibleViewController]; NSString *segue = parameters[SkyRouteSegueType] ? parameters[SkyRouteSegueType] : SkyRouteSeguePush; // 决定 present 或者 Push; 默认值 Push BOOL animated = parameters[SkyRouteAnimated] ? [parameters[SkyRouteAnimated] boolValue] : YES; // 转场动画; NSLog(@"%s 跳转: %@ %@ %@",__func__ ,currentVC, segue,vc); if ([segue isEqualToString:SkyRouteSeguePush]) { //PUSH if (currentVC.navigationController) { NSString *backIndexString = [NSString stringWithFormat:@"%@",parameters[SkyRouteBackIndex]]; UINavigationController* nav = currentVC.navigationController; if ([backIndexString isEqualToString:SkyRouteIndexRoot]) { NSMutableArray *vcs = [NSMutableArray arrayWithObject:nav.viewControllers.firstObject]; [vcs addObject:vc]; [nav setViewControllers:vcs animated:animated]; } else if ([backIndexString integerValue] && [backIndexString integerValue] < nav.viewControllers.count) { //移除掉指定数量的 VC, 在Push; NSMutableArray *vcs = [nav.viewControllers mutableCopy]; [vcs removeObjectsInRange:NSMakeRange(vcs.count - [backIndexString integerValue], [backIndexString integerValue])]; nav.viewControllers = vcs; [nav pushViewController:vc animated:YES]; } else { [nav pushViewController:vc animated:animated]; } }else { //由于无导航栏, 直接执行 Modal BOOL needNavigation = parameters[SkyRouteSegueNeedNavigation] ? NO : YES; if (needNavigation) { UINavigationController* navigationVC = [[UINavigationController alloc] initWithRootViewController:vc]; [currentVC presentViewController:navigationVC animated:YES completion:[parameters objectForKey:SkyRouteModalCompletionHandleKey]]; } else { [currentVC presentViewController:vc animated:animated completion:[parameters objectForKey:SkyRouteModalCompletionHandleKey]]; } } } else { //Modal BOOL needNavigation = parameters[SkyRouteSegueNeedNavigation] ? parameters[SkyRouteSegueNeedNavigation] : NO; if (needNavigation) { UINavigationController* navigationVC = [[UINavigationController alloc] initWithRootViewController:vc]; // [currentVC presentViewController:navigationVC animated:animated completion:[parameters objectForKey:SkyRouteModalCompletionHandleKey]]; } else { //vc.modalPresentationStyle = UIModalPresentationFullScreen; [currentVC presentViewController:vc animated:animated completion:[parameters objectForKey:SkyRouteModalCompletionHandleKey]]; } } } // 返回上层页面回调; + (BOOL)executeBackRouterParameters:(NSDictionary *)parameters { // UIViewController* visibleVC = [UIViewController index_findVisibleViewController]; //1,首先判断参数是返回dismiss还是pop //2,如果是pop,则判断有无index,如果有则popto,如果没有则pop //如果是dismiss,判断是否有navigationVc,如果有,则连通navigationVcdismiss //如果没有,则直接dismiss VC //默认是pop // //是否执行动画,默认执行 BOOL animated = parameters[SkyRouteAnimated] ? [parameters[SkyRouteAnimated] boolValue] : YES; //获取返回类型,默认是pop NSString *backType = parameters[SkyRouteSegueBackType] ? parameters[SkyRouteSegueBackType] : SkyRouteSegueBackPop; //如果是dismiss,则处理dismiss事件 if([backType isEqualToString:SkyRouteSegueBackDismiss]){ //判断当前dismiss时是否带有NavigationController,默认没有 BOOL hasNaviCtrlByDismiss = parameters[SkyRouteSegueNeedNavigation] ? [parameters[SkyRouteSegueNeedNavigation] boolValue] : NO; if(hasNaviCtrlByDismiss){ if(!visibleVC.navigationController){ return NO; } [visibleVC.navigationController dismissViewControllerAnimated:animated completion:[parameters objectForKey:SkyRouteDismissCompletionHandleKey]]; }else{ [visibleVC dismissViewControllerAnimated:animated completion:[parameters objectForKey:SkyRouteDismissCompletionHandleKey]]; } return YES; //[parameters objectForKey:SkyRouteDismissCompletionHandleKey] //为dismiss后参数传过来的block,要dismiss的controller这么写 /* void(^dismissHandle)(void) = ^(){ NSLog(@"%@",@"ssssssssssssssssss"); if(self.delegate && [self.delegate respondsToSelector:@selector(delegateEvent:)]){ [self.delegate delegateEvent:@"这是代理内容"]; } }; [SkyRouter openURL:SkyRouteSegueBack parameters:@{SkyRouteSegueBackType:SkyRouteSegueBackDismiss,SkyRouteDismissCompletionHandleKey:dismissHandle,@"delegate":self}]; */ } //这里全部是pop的事件 NSString *backIndexString = parameters[SkyRouteBackIndex] ? [NSString stringWithFormat:@"%@",parameters[SkyRouteBackIndex]] : nil; // 指定返回个数, 优先处理此参数; id backPage = parameters[SkyRouteBackPage] ? parameters[SkyRouteBackPage] : nil; // 指定返回到某个页面, NSInteger backPageOffset = parameters[SkyRouteBackPageOffset] ? [parameters[SkyRouteBackPageOffset] integerValue] : 0; // 指定返回到的页面并进行偏移; // UIViewController* visibleVC = [UIViewController index_findVisibleViewController]; UINavigationController* navigationVC = visibleVC.navigationController; if (navigationVC) { // 处理 pop 按索引值处理; if (IsStringClass(backIndexString)) { if ([backIndexString isEqualToString:SkyRouteIndexRoot]) {//返回根 [navigationVC popToRootViewControllerAnimated:animated]; }else { NSUInteger backIndex = backIndexString.integerValue; NSMutableArray* vcs = navigationVC.viewControllers.mutableCopy; if (vcs.count > backIndex) { [vcs removeObjectsInRange:NSMakeRange(vcs.count - backIndex, backIndex)]; [navigationVC setViewControllers:vcs animated:animated]; return YES; }else { return NO; //指定返回索引值超过当前导航控制器包含的子控制器; } } } else if (backPage) { //处理返回指定的控制器, 可以处理 NSMutableArray *vcs = navigationVC.viewControllers.mutableCopy; NSInteger pageIndex = NSNotFound; //页面标识为字符串 if ([backPage isKindOfClass:[NSString class]]) { for (int i=0; i<vcs.count; i++) { if ([vcs[i] isKindOfClass:NSClassFromString(backPage)]) { pageIndex = i; break; } } } //页面标识为vc实例 else if ([backPage isKindOfClass:[UIViewController class]]) { for (int i=0; i<vcs.count; i++) { if (vcs[i] == backPage) { pageIndex = i; break; } } } //有指定页面,根据参数跳转 if (pageIndex != NSNotFound) { NSUInteger backIndex = (vcs.count-1) - pageIndex + backPageOffset; if (vcs.count > backIndex) { [vcs removeObjectsInRange:NSMakeRange(vcs.count-backIndex, backIndex)]; [navigationVC setViewControllers:vcs animated:animated]; return YES; } } //指定页面不存在,return NO,可用于判断当前vc栈里有没有当前页面。 } else { // [navigationVC popViewControllerAnimated:animated]; void(^popEndBlock)(void) = [parameters objectForKey:SkyRoutePopCompletionBlock]; if(popEndBlock){ //pop传递代理执行的block [visibleVC setNavigationControllerDelegate:visibleVC popHandle:popEndBlock]; //将要pop的controller的写法 /* 传递一个block到参数,然后让其navigationController代理去执行 __weak typeof(self) weakSelf = self; void(^popEndHandle)(void) = ^(){ if(weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(delegateEvent:)]){ [weakSelf.delegate delegateEvent:@"这是代理内容"]; } }; [SkyRouter openURL:SkyRouteSegueBack parameters:@{SkyRouteSegueBackType:SkyRouteSegueBackPop,SkyRoutePopCompletionBlock:popEndHandle}]; */ } return YES; } } return NO; } @end
Demo:
http://image.iashes.com/content-blog/file/RouterDemo.zip