在 iOS組件化方案調研 這篇中,對組件化的應用場景和實現方式做了簡單的調研。最終在項目中采用的是 casa 的
CTMediator
這套方案。以下是CTMediator 的一個簡單的使用教程。
一、普通頁面跳轉用法
假設我們有個頁面叫 OneViewController
,當前頁面為 HomeViewController,普通情況下頁面的間的跳轉方式如下:
#import "HomeViewController.h"
#import "OneViewController.h"
@implementation HomeViewController
- (void)aButtonClick:(UIButton *)sender {
OneViewController *viewController = [[OneViewController alloc] init];
viewController.name = @"普通用法"; //傳遞必要參數
[self.navigationController pushViewController:viewController animated:YES];
}
@end
這樣做看上去沒什么問題,實際也沒什么問題。
但是,考慮以下情況:
1,如果HomeViewController
里有 N 個這樣的 button
事件,每個點擊后的跳轉都是不同的頁面,那么則 HomeViewController
里,需要導入 N 個這樣的 OneViewController.h
;
2,如果HomeViewController
是一個可以移植到其它項目的業務模塊,在拖出首頁 HomeVC
相關的業務代碼時,難道還要把 HomeViewController.m
導入的 N 個其它 XxxViewController.h
都一塊拖到新項目中么?
這點就是因為代碼的耦合導致了首頁 HomeVC
沒法方便的移植。
說這樣沒有問題,是因為普通情況下,我們并沒有移植 HomeVC
到其它項目的需求。
至于什么時候會有這樣的問題,以及,這樣的問題如果解決,在 iOS組件化方案調研這篇中,已經做過簡單的討論,這篇主要是選取了我個人較偏向的 Target-Action
這套方案,簡單講一下實現方式。
二、Target-Action 實現頁面跳轉
采用的是 CTMediator
這套方案
Demo地址
還是假設我們有個頁面叫 NewsViewController
, 當前頁面為HomeViewController
那么,我們按照CTMediator
設計的架構來寫一遍這個流程
1.創建Target-Action
創建一個 Target_News
類,在這個文件里,我們主要生成 NewsViewController
實例并為其進行一些必要的賦值。例如:
// Target_News.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface Target_News : NSObject
- (UIViewController *)Action_NativeToNewsViewController:(NSDictionary *)params;
@end
這個類需要直接 #import "NewsViewController.h"
// Target_News.m
#import "Target_News.h"
#import "NewsViewController.h"
@implementation Target_News
- (UIViewController *)Action_NativeToNewsViewController:(NSDictionary *)params {
NewsViewController *newsVC = [[NewsViewController alloc] init];
if ([params valueForKey:@"newsID"]) {
newsVC.newsID = params[@"newsID"];
}
return newsVC;
}
@end
2.創建 CTMediator 的Category.
CTMediator+NewsActions.這個Category利用Runtime調用我們剛剛生成的Target_News。
由于利用了Runtime,導致我們完全不用#import剛剛生成的Target_News即可執行里面的方法,所以這一步,兩個類是完全解耦的。也即是說,我們在完全解耦的情況下獲取到了我們需要的NewsViewController。例如:
// CTMediator+NewsActions.h
#import "CTMediator.h"
#import <UIKit/UIKit.h>
@interface CTMediator (NewsActions)
- (UIViewController *)yt_mediator_newsViewControllerWithParams:(NSDictionary *)dict;
@end
// CTMediator+NewsActions.m
#import "CTMediator+NewsActions.h"
NSString * const kCTMediatorTarget_News = @"News";
NSString * const kCTMediatorActionNativTo_NewsViewController = @"NativeToNewsViewController";
@implementation CTMediator (NewsActions)
- (UIViewController *)yt_mediator_newsViewControllerWithParams:(NSDictionary *)dict {
UIViewController *viewController = [self performTarget:kCTMediatorTarget_News
action:kCTMediatorActionNativTo_NewsViewController
params:dict];
if ([viewController isKindOfClass:[UIViewController class]]) {
return viewController;
} else {
NSLog(@"%@ 未能實例化頁面", NSStringFromSelector(_cmd));
return [[UIViewController alloc] init];
}
}
@end
3.最終使用
由于在Target中,傳遞值得方式采用了去Model化得方式,導致我們在整個過程中也沒有#import任何Model。所以,我們的每個類都與Model解耦。
// HomeViewController.m
#import "HomeViewController.h"
#import "CTMediator+NewsActions.h"
@implementation HomeViewController
- (void)bButtonClick:(UIButton *)sender {
UIViewController *viewController = [[CTMediator sharedInstance] yt_mediator_newsViewControllerWithParams:@{@"newsID":@"123456"}];
[self.navigationController pushViewController:viewController animated:YES];
}
@end
4.不足
這里其實唯一的問題就是,Target_Action里不得不填入一些 Hard Code,就是對創建的VC的賦值語句。不過這也是為了達到最大限度的解耦和靈活度而做的權衡。
// 1. kCTMediatorTarget_News字符串 是 Target_xxx.h 中的 xxx 部分
NSString * const kCTMediatorTarget_News = @"News";
// 2. kCTMediatorActionNativTo_NewsViewController 是 Target_xxx.h 中 定義的 Action_xxxx 函數名的 xxx 部分
NSString * const kCTMediatorActionNativTo_NewsViewController = @"NativeToNewsViewController";
注:這篇寫的較早。作者前段時間也親自寫了篇教程:在現有工程中實施基于CTMediator的組件化方案 比這篇詳盡,各位還是移步原 po 的教程吧……