#import "FirstViewController.h"
FirstViewController *controller = [[FirstViewController alloc] init];
[self.navigationController pushViewController:controller animated:YES];
上面這段代碼是ios開發(fā)中很常見的一段代碼扣癣,但是這平常無奇的代碼卻有一個隱患,這個隱患在隨項目不斷擴展會越來越嚴重憨降。那就是
ViewController之間是存在耦合的父虑,想要跳轉(zhuǎn)目標ViewController,則必須引入對應(yīng)的類頭文件授药。更有甚者士嚎,在ViewController的.h
文件中暴露屬性和方法,簡直無法直視悔叽。這次主要解決的問題就是徹底剪斷ViewController之間的耦合莱衩,清理ViewController的.h
文件中暴露的內(nèi)容,還一個清爽的ViewController娇澎。
流程圖
導(dǎo)航欄流程圖.png
自定義全局導(dǎo)航欄
- 初始化導(dǎo)航欄
因為需要統(tǒng)一對目標ViewController初始化笨蚁,增刪改查等操作,需要自定義一個全局導(dǎo)航欄趟庄,為了方便處理括细,把導(dǎo)航欄做成單例。
// WBNavigationController.h
@interface WBNavigationController : UINavigationController
+ (instancetype)sharedInstance;
@end
// WBNavigationController.m
@interface WBNavigationController ()
// 保存所有注冊的ViewController的URL與類名
@property (nonatomic, strong) NSMutableDictionary *registerVCCls;
@end
@implementation WBNavigationController
+ (instancetype)sharedInstance {
static WBNavigationController *navigationController = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
navigationController = [[WBNavigationController alloc] init];
});
return navigationController;
}
- 導(dǎo)航欄操作
// WBNavigationController.m
// 注冊一個ViewController到導(dǎo)航欄
+ (void)registerWithUrl:(NSString *)url viewControllerClass:(Class)cls {
[WBNavigationController sharedInstance].registerVCCls[url] = cls;
}
// 移除導(dǎo)航欄中一個ViewController的實例
+ (void)removeViewControllerWithUrl:(NSString *)url {
NSMutableArray *viewControllers = [[WBNavigationController sharedInstance].viewControllers mutableCopy];
UIViewController *targetVC = [[self class] findViewControllerIfExistWithUrl:url];
if ( [viewControllers containsObject:targetVC] ) {
[viewControllers removeObject:targetVC];
}
[WBNavigationController sharedInstance].viewControllers = viewControllers;
}
// 根據(jù)url查找ViewController的類名
+ (Class)findViewControllerClassWithUrl:(NSString *)url {
return [WBNavigationController sharedInstance].registerVCCls[url];
}
// 導(dǎo)航欄中是否存在ViewController的實例
+ (BOOL)existViewControllerWithUrl:(NSString *)url {
NSMutableArray *viewControllers = [[WBNavigationController sharedInstance].viewControllers mutableCopy];
UIViewController *targetVC = [[self class] findViewControllerIfExistWithUrl:url];
if ( [viewControllers containsObject:targetVC] ) {
return YES;
}
return NO;
}
// 獲取導(dǎo)航欄中的ViewController的實例
+ (UIViewController *)findViewControllerIfExistWithUrl:(NSString *)url {
Class vcClassName = [[self class] findViewControllerClassWithUrl:url];
for (UIViewController *vc in [WBNavigationController sharedInstance].viewControllers) {
if ( vcClassName == vc.class ) {
return vc;
}
}
return nil;
}
// 取消注冊
+ (void)deregisterUrl:(NSString *)url {
[[WBNavigationController sharedInstance].registerVCCls removeObjectForKey:url];
}
ViewController類別
為了方便調(diào)用戚啥,給ViewController添加一個類別用于調(diào)用導(dǎo)航欄的操作奋单。
- 初始化目標ViewController
在.h
頭文件中暴露屬性與方法無非就是傳遞參數(shù),與適時的回調(diào)猫十。為了清除這些览濒,給每個目標ViewController添加參數(shù)傳遞與回調(diào)block。
#import "UIViewController+URL.h"
@interface UIViewController (URL)
// 給目標ViewController傳遞的參數(shù)
@property (nonatomic, strong) id wb_params;
// 給目標ViewController的回調(diào)
@property (nonatomic, copy) WBReplyAction wb_replyAction;
// 初始化目標ViewController
- (instancetype)initWithParams:(id)params;
- (instancetype)initWithParams:(id)params replyAction:(WBReplyAction)replyAction;
- 封裝導(dǎo)航欄操作
封裝導(dǎo)航欄常用操作拖云,push贷笛,pop,以及目標ViewController的present&&dismiss操作宙项。以下以push為例昨忆。
#import "UIViewController+URL.h"
// push操作
- (void)wb_pushViewController:(WBParams)params;
- (void)wb_pushSimpleViewController:(NSString *)url;
- (void)wb_popViewController;
- (void)wb_popViewControllerAnimate:(BOOL)animated;
- (void)wb_popToRootViewControllerAnimated:(BOOL)animated;
- (void)wb_popToViewControllerWithUrl:(NSString *)url animated:(BOOL)animated;
- (void)wb_presentViewController:(WBParams)params;
- (void)wb_presentSimpleViewController:(NSString *)url;
- (void)wb_dismissSimpleViewController;
- (void)wb_dismissViewControllerAnimated:(BOOL)animated completion:(WBCompleteAction)completion;
#import "UIViewController+URL.m"
- (void)wb_pushSimpleViewController:(NSString *)url {
[self wb_pushViewController:^(WBNode *node) {
node.url = url;
}];
}
- (void)wb_pushViewController:(WBParams)params {
WBNode *node = [self setupNode:params];
Class vcClass = [WBNavigationController findViewControllerClassWithUrl:node.url];
if ( !vcClass ) {
NSLog(@"URL:%@ not register", node.url);
}
UIViewController *controller = [[vcClass alloc] initWithParams:node.params replyAction:node.replyAction];
[[WBNavigationController sharedInstance] pushViewController:controller animated:node.animate];
}
- 測試調(diào)用
- 在AppDelegate設(shè)置window的rootViewController為全局導(dǎo)航欄。
FirstViewController *rootViewController = [[FirstViewController alloc] init];
[[WBNavigationController sharedInstance] pushViewController:rootViewController animated:NO];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = [WBNavigationController sharedInstance];
[self.window makeKeyAndVisible];
2.注冊ViewController到全局導(dǎo)航欄杉允。
// 定義快速注冊viewcontroller的宏
#undef WB_IMPLEMENT_LOAD
#define WB_IMPLEMENT_LOAD( url ) \
+ (void)load { \
@autoreleasepool { \
[WBNavigationController registerWithUrl:url viewControllerClass:[self class]]; \
} \
}
#import "SecondViewController.h"
// 注冊
@implementation SecondViewController
WB_IMPLEMENT_LOAD(URL_SECOND_VC)
3.跳轉(zhuǎn)調(diào)用
#import "FirstViewController.h" // 不需要引用目標ViewController邑贴,此處是主調(diào)方的席里。
// 簡單調(diào)用,不需要傳遞參數(shù)與回調(diào)
[self wb_pushSimpleViewController: URL_SECOND_VC];
// 完全調(diào)用
[self wb_pushViewController:^(WBNode *node) {
node.url = URL_SECOND_VC;
// node.animate = NO;
node.params = @{@"params": @"push data"};// 參數(shù)傳遞
node.replyAction = ^(id result) { // 回調(diào)
NSLog(@"result >> %@", result[@"result"]);
};
}];
#import "SecondViewController.h"
// 獲取從前頁面?zhèn)鬟f來的參數(shù)
if( self.wb_params ) NSLog(@"push get params >> %@", self.wb_params[@"params"]);
// 觸發(fā)前頁面的回調(diào)
if ( self.wb_replyAction ) {
self.wb_replyAction(@{@"result": @"pop return data"});
}
至此已完成了解決UIViewController之間的耦合問題÷<荩現(xiàn)在我們來對比一下前后代碼對照:
#import "FirstViewController.h"
// 優(yōu)化前
FirstViewController *controller = [[FirstViewController alloc] init];
[self.navigationController pushViewController:controller animated:YES];
// 優(yōu)化后
[self wb_pushSimpleViewController: URL_FIRST_VC];
// 優(yōu)化后所有ViewController的頭文件應(yīng)該都是這樣奖磁,清爽無比。
@interface FirstViewController : UIViewController
@end
以上因為跳轉(zhuǎn)ViewController間已不存在任何依賴繁疤,調(diào)用簡潔清晰咖为,更有利于項目的模塊化。demo已上傳至github,有任何錯誤與建議可以評論指出稠腊。