iOS Router原理篇

前言:

iOS強調模塊化、組件化,講究模塊內高內聚,模塊間低耦合。
那么模塊與模塊之間的低耦合,就要求模塊間的通信要盡可能的減少依賴,SDRouter,就是這樣的中間產物。

SDRouter的設計是參照一水流年的想法來實現的,總體和他的想法保持一致,但是會更加的簡潔、易用,作者設計的拓展性強,但是依賴過多,導致庫不太容易單獨存在。那么,你可以看到使用該庫,可以使用URL的方式打開任何Native頁面,如下面一個偽協議:asone://oneController?title=as_one
asone是AppSchema.完全按照http協議涉及的跳轉,讓該庫SDRouter的結構簡單,想法也簡單。

先設想一下:如何從AController以URL的方式跳轉到BController并攜帶參數?

先看下SDRouter中是怎么實現的:

- (void)viewDidLoad {
    [super viewDidLoad];
//  在頁面上添加一個button
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 80)];
    [button setTitle:@"one" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [button addTarget:self action:@selector(go) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

//  點擊跳轉到下一個控制器 并攜帶下一個控制器的標題
- (void)go {
    NSDictionary *param = @{@"title":@"as_one"};
    NSURL *url = SDURLRouteQueryLink(OneController, param);
    [[SDRouter shareRutor] rutor:url];
}

再設想一下:如何從BController以OpenUrl的方式打開CController并傳遞參數?

//  先忽略+load方法中的內容
+ (void)load {
    [[SDRouter shareRutor] addPaten:OneController callback:^(SDRouterContext *context) {
        NSLog(@"優品財富: %@",context.paramters);
        OneViewController *textOneVc = [[OneViewController alloc] init];
        textOneVc.navigationItem.title = context.paramters[@"title"];
        [context.topNavigationController pushViewController:textOneVc animated:YES];
    }];
}

//  先看這里
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0];
    //  頁面上添加一個按鈕
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 80)];
    [button setTitle:@"openUrl" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [button addTarget:self action:@selector(openUrl) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    
}
//  點擊調轉,以openUrl的方式打開一個控制器并攜帶參數,該參數是navigationTitle。
- (void)openUrl {
    NSDictionary *dict = @{@"title":@"as_two"};
    NSURL *url = SDURLRouteQueryLink(TwoController, dict);
    [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) {}];
}

最后再想一下,H5怎么快捷的打開CController并攜帶參數?

//  H5頁面添加一個按鈕并添加js跳轉
<input class="class1 class2" type="button" value="跳轉到as_Three頁面" style="width:500px;height:150px;font-size:40px"onclick=javascrtpt:jump()>
function jump(){
    window.location="asone://threeController?title=as??three";
}

上面三種設想可以參看下圖:

hello1.gif

SDRouter設計思想:

  • URL部分:看似URL跳轉,實際上是對URL的解析、組合編解碼。
    SDRouterUtil主要包含了對URL的解析及組合編解碼的方法。
@interface NSString (SDURLEncode)
- (NSString *)URLEncode;
- (NSString *)URLDecode;
@end
//  用來處理參數攜帶 拼接參數 返回編碼、拼接后的URL
FOUNDATION_EXTERN NSURL *SDURLRouteQueryLink(NSString *baseUrl, NSDictionary *query);

//  添加參數
FOUNDATION_EXTERN NSString *SDURLRouteJoinParamterString(NSString *urlStr, NSString *query);

//  將拼接好的參數encode
FOUNDATION_EXTERN NSString *SDURLRouteEncodeURLQueryParamters(NSDictionary *paramter);

//  將參數decode
FOUNDATION_EXTERN NSDictionary *SDURLRouteDecodeURLQueryParamters(NSString *urlStr);
  • 跳轉:
    URL拼接好,從AController跳轉到BController,跳轉的方法其實很簡單。
    [[SDRouter shareRutor] rutor:url];那這句代碼又做了什么呢?重點看這段代碼
+ (void)load {
    [[SDRouter shareRutor] addPaten:OneController callback:^(SDRouterContext *context) {
        NSLog(@"優品財富: %@",context.paramters);
        OneViewController *textOneVc = [[OneViewController alloc] init];
        textOneVc.navigationItem.title = context.paramters[@"title"];
        [context.topNavigationController pushViewController:textOneVc animated:YES];
    }];
}

在SDRouter中提供了一個注冊的方法,必須在load方法中注冊,+load是已知的執行最早的方法,比main函數好要早,這里不做過多解釋,不明白可以參看你真的了解+load方法嗎?
addPaten:OneController其實這個oneController是定義的字符串常量,如:asone://oneController?title=as_one,在App啟動后,SDRouter會記錄當前這個控制器和所攜帶的callback

//  實現比較簡單
- (void)addPaten:(NSString *)paten callback:(SDCompleteCallback)callback{
    NSDictionary *dict = @{paten:callback};
    if (![_results containsObject:dict]) {
        [_results addObject:dict];
    }
}

在調用[[SDRouter shareRutor] rutor:url]的時候,會匹配需要前往的url。

- (void)rutor:(NSURL *)paten {
    SDURLParser *parser = [[SDURLParser alloc] initWithURL:paten];
    [_results enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([[obj allKeys].firstObject isEqualToString:parser.paten]) {
            SDRouterContext *context = [[SDRouterContext alloc] init];
            context.paramters = parser.paramters;
            if ([obj allValues].firstObject) {
                SDCompleteCallback callback = (SDCompleteCallback)[obj allValues].firstObject;
                callback(context);
            }
            *stop = YES;
        }
    }];
}
  • push
    到這里,已經能夠保證在+load方法中獲取到參數,重點是頁面怎么push出來。因為+load方法執行時,是不能夠獲取到navigationController的,那么怎么獲取到并push呢?

  • 全局UI棧
    其實一開始并沒有該類,但是通過其他方式獲取總是覺的雞肋,還是通過一水文章的點撥才有了該類。
    該類中利用runtime方法替換,重寫Controller的生命周期方法,聲明一個指針數組,在viewWillApear的時候加入UI棧,在disWill的時候移除UI棧,參數的攜帶和棧頂控制器的攜帶,是通過SDContext來完成的。該類中包含了參數及navigation。

講解比較粗略,想了解的可以到demo中查看。
github

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,216評論 30 472
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,991評論 19 139
  • 那條林蔭大道 首發:沐遙詩雨(MoyleSY) 作者:李沐遙 . 秋寒乍起,秋葉簌簌 鋪一地錦繡 落一場金燦燦的雨...
    李沐遙閱讀 272評論 2 2
  • 金庸和古龍,共同打造了一個武俠世界。在這個武俠世界里,有愛恨情仇,有江湖恩怨門派斗爭。 金庸的人物內...
    張小白在努力閱讀 472評論 0 0
  • 陽春三月,四處彌漫著一種“叫春的氣息”,單身的弟弟妹妹們紛紛扎入愛河,一發不可收拾。找到一個理想的如意郎君或如意嬌...
    婷不下來ing閱讀 335評論 0 0