页面路由
页面路由为您提供VC页面的解耦能力,使您可以通过自定义URL的方式访问VC并传值。
继承协议
在ViewController中引入TMFURLRouter.h头文件
#import "TMFURLRouter.h"
实现协议TMFURLRouterProtocol
@interface TMFURLRouterTest1VC ()<TMFURLRouterProtocol>
@end
@implementation TMFURLRouterTest1VC
+ (void)registerRouter {
}
@end
页面路由注册方法
在+(void)registerRouter中,有两种注册页面路由的方法:
+ (void)registerURLPattern:(NSString *)urlPattern toHandler:(TMFURLRouterHandler)handler;
+ (void)registerURLPattern:(NSString *)urlPattern toObjectHandler:(TMFURLRouterObjectHandler)handler;
第一种方式提供一般的页面路由注册方式,handler会传一个字典,包含了注册的 URL 中对应的变量,无返回值。具体使用方式如下:
[TMFURLRouter registerURLPattern:@"tmf://router/test/1/push" toHandler:^(NSDictionary *routerParameters) { TMFURLRouterTest1VC *vc = [[TMFURLRouterTest1VC alloc] init]; vc.hidesBottomBarWhenPushed = YES; if (routerParameters[@"titleStr"]) { vc.titleStr = routerParameters[@"titleStr"]; } else { vc.titleStr = routerParameters.routerUserInfo[@"titleStr"]; } vc.testBlock = routerParameters.routerUserInfo[@"testBlock"]; vc.completion = routerParameters[TMFURLRouterParameterCompletion]; vc.failure = routerParameters[TMFURLRouterParameterFailure]; [[UIViewController currentNavigationViewController] pushViewController:vc animated:YES]; }];
注意:routerParameters里包含了传递各种参数,如果是基本类型参数,可以直接在routerParameters中取到,如果你将其放到了routerUserInfo中,也可以在routerUserInfo中取到;如果是block,就只能在routerParameters.routerUserInfo中提取。
第二种方式的handler需要提供返回值,这适合您需要取到VC实例变量的情况,如创建TabbarViewController的情况。具体使用方式如下:
[TMFURLRouter registerURLPattern:@"tmf://router/test/1" toObjectHandler:^id(NSDictionary *routerParameters) { TMFURLRouterTest1VC *vc = [[TMFURLRouterTest1VC alloc] init]; vc.titleStr = routerParameters.routerUserInfo[@"titleStr"]; return vc; }];
注意:您也可以为一个页面同时在registerRouter中注册两个路由,但请使用不同的路由标示,如上面示例代码中的tmf://router/test/1/push和tmf://router/test/1
调用注册函数
TMFURLRouter并不推荐在+(void)load;中注册路由,因为这会拖慢App启动速度,所以您需要在controller中实现TMFURLRouterProtocol协议,并在合适的时机去注册页面路由,推荐在AppDelegate的didFinishLaunchingWithOptions中注册,注册方式如下:
NSArray<NSString *> *array = @[@"TMFURLRouterTest1VC",@"TMFURLRouterTest2VC",@"TMFURLRouterWebVC"];
NSBundle *bundle = [NSBundle bundleForClass:self.class];
NSString *bundleName = [bundle.bundlePath.lastPathComponent componentsSeparatedByString:@"."].firstObject;
for (NSString *aClassName in array) {
Class aClass = NSClassFromString(aClassName);
if (!aClass) {
aClass = NSClassFromString([NSString stringWithFormat:@"%@.%@", bundleName, aClassName]);
}
if (aClass) {
id<TMFURLRouterProtocol> p = (id<TMFURLRouterProtocol>)aClass;
[p registerRouter];
}
}
如果您的工程采用分模块的开发方式,推荐每个模块的头文件也继承TMFURLRouterProtocol协议,并实现+ (void)registerRouter;方法,将上述代码放到此方法实现中,再在AppDelegate的didFinishLaunchingWithOptions中调用模块的registerRouter方法注册。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSArray<NSString *> *array = @[@"TMFTestModule1", @"TMFTestModule2"];
for (NSString *aClassName in array) {
Class aClass = NSClassFromString(aClassName);
if (aClass) {
id<TMFURLRouterProtocol> p = (id<TMFURLRouterProtocol>)aClass;
[p registerRouter];
}
}
}
调用方式
普通页面跳转
// 下面 1 和 2写法等效
// 1
[TMFURLRouter openURL:@"tmf://router/test/1/push?titleStr=标题1"];
// 2
[TMFURLRouter openURL:[@"tmf://router/test/1/push" appendingParams:@{@"titleStr":@"标题1"}]];
// 3 把参数放在userInfo里 取的时候需要从.routerUserInfo中取 可以查看3.1.2 页面路由注册方法中registerRouter中的写法
[TMFURLRouter openURL:@"tmf://router/test/1/push" userInfo:@{@"titleStr":@"标题1"}];
// 4 blcok应放在userInfo里传递 下面为完整的调用方法 block无法拼接在url里 所以要放在userInfo里
void (^testBlock)(void) = ^(){
NSLog(@"test block");
};
[TMFURLRouter openURL:[@"tmf://router/test/1/push" appendingParams:@{@"titleStr":@"标题1"}] withUserInfo:@{@"testBlock":testBlock} completion:^(id result) {
NSLog(@"completion block");
} failure:^(NSError *error) {
NSLog(@"failure block");
}];
注意:页面最终采取何种跳转方式(push或者present等),是您在ViewController中注册路由时决定的,请参考页面路由注册方法
返回页面实例
UITabBarController *tabVC = [[UITabBarController alloc] init];
UIViewController *vc1 = [TMFURLRouter objectForURL:[@"tmf://router/test/1" appendingParams:@{@"titleStr":@"标题1"}]];
UITabBarItem *item1 = [[UITabBarItem alloc] initWithTitle:@"第一" image:nil selectedImage:nil];
vc1.tabBarItem = item1;
UIViewController *vc2 = [TMFURLRouter objectForURL:[@"tmf://router/test/2" appendingParams:@{@"titleStr":@"标题2"}]];
UITabBarItem *item2 = [[UITabBarItem alloc] initWithTitle:@"第二" image:nil selectedImage:nil];
vc2.tabBarItem = item2;
tabVC.viewControllers = @[vc1,vc2];
注意:您必须使用页面路由注册方法中的第二种注册方式注册页面才能通过上面示例代码拿到页面实例
H5页面跳转原生页面
直接调用TMFURLRouter
一个简单的HTML页面如下:
<title>路由测试页面</title>
<input class="class1 class2" type="button" value="打开原生页面" style="width:500px;height:150px;font-size:40px"onclick=javascrtpt:jump()>
<script>
function jump(){
window.location="tmf://router/test/2/push?titleStr=从web推过来";
}
</script>
在WKWebview的WKNavigationDelegate代理回调中,可以这样写:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSLog(@"%@",navigationAction.request.URL.absoluteString);
if ([TMFURLRouter canOpenURL:navigationAction.request.URL.absoluteString]) {
[TMFURLRouter openURL:navigationAction.request.URL.absoluteString];
}
decisionHandler(WKNavigationActionPolicyAllow);
}
通过TMFJSAPIs调用
在前端页面可以这样调用:
// 跳转原生页面
function tmf_routerURL() {
TMFJSBridge.invoke('routerURL', {
url : 'tmf://router/test/1/push', // string,必选,原生页面 URL 地址
params : { // dictionary,可选,参数
titleStr : 'H5通过JSAPI跳转', // string,可选,默认标题
},
}, function (res) {
// tmf_logObject(res, 2);
});
}
在客户端,需要先注册JSAPI:
[configuration registerFunction:@"routerURL" withInvocationClass:[TMFJSBridgeInvocation_routerURL class]];
其他注意事项
由于TMFURLRouter把页面跳转语句放在了目标controller中,所以也提供了获取当前VC的扩展:
@interface UIViewController (TMFURLRouter)
+ (UIViewController*)currentViewController;
+ (UINavigationController*)currentNavigationViewController;
+ @end
只要您依赖了TMFURLRouter,即可使用此扩展:
[[UIViewController currentNavigationViewController] pushViewController:vc animated:YES];