JLRoutes使用及封装

路由,各个模块之间的调度,跳转。
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

Leave a Reply

Required fields are marked *