前言
项目中H5与原生的交互是一个比较大的问题,当然,只要是涉及到交互逻辑的,都会很繁琐,并不是说很难,但绝对是很繁琐的。原来项目中使用的是JavaScriptCore这个库,通过JS与原生进行交互,但是使用下来,觉得并不是很稳定,所以新版本更新的时候,准备把之前所有的交互逻辑全部抛弃,采用url跳转并截取url信息的思路,创建通用的跳转方法机制;
(一)H5与原生的交互方法封装类;
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface H5NativeInteractTool : NSObject<NSCopying>
+ (H5NativeInteractTool *)shareInstance;
- (void)goFromSourceController:(UIViewController *)source toTargetUrl:(NSString *)url refreshBlock:(void(^)(NSString *url))refreshBlock;
- (NSString *)addParaOfSessionKeyByUrlString:(NSString *)url;
@end
#import "H5NativeInteractTool.h"
#import "PayViewController.h"
#import "Contants.h"
@interface H5NativeInteractTool ()
@property (nonatomic,strong) UIViewController *object;
@property (nonatomic,copy) void(^backRefreshBlock)(NSString *url);
@end
@implementation H5NativeInteractTool
static H5NativeInteractTool *_h5NativeInteractTool = nil;
+ (H5NativeInteractTool *)shareInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!_h5NativeInteractTool) {
_h5NativeInteractTool = [[self alloc] init];
}
});
return _h5NativeInteractTool;
}
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!_h5NativeInteractTool) {
_h5NativeInteractTool = [super allocWithZone:zone];
}
});
return _h5NativeInteractTool;
}
- (instancetype)copyWithZone:(NSZone *)zone
{
return _h5NativeInteractTool;
}
- (NSString *)addParaOfSessionKeyByUrlString:(NSString *)url
{
if (!url) {
return nil;
}
if ([url rangeOfString:@"SessionKey=" options:NSCaseInsensitiveSearch].location != NSNotFound) {
url = [url stringByReplacingOccurrencesOfString:@"{0}" withString:APPDELEGETE.sessionKey];
}
return url;
}
//根据url提供的信息,跳转至相应的原生界面,成功执行完操作最后会执行refreshBlock,回到原页面刷新;
- (void)goFromSourceController:(UIViewController *)source toTargetUrl:(NSString *)url refreshBlock:(void (^)(NSString *))refreshBlock
{
self.object = source;
self.backRefreshBlock = refreshBlock;
url = [url stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSRange start = [url rangeOfString:@":"];
NSRange end = [url rangeOfString:@"?"];
NSString *methodStr = [url substringWithRange:NSMakeRange(start.location+start.length, end.location-start.location-start.length)];
NSString *paraStr = [url substringWithRange:NSMakeRange(end.location+end.length, url.length-(end.length+end.location))];
NSData *paraData = [paraStr dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *paraDict = [NSJSONSerialization JSONObjectWithData:paraData options:NSJSONReadingAllowFragments error:nil];
if (paraDict) {
methodStr = [NSString stringWithFormat:@"%@:",methodStr];
}
SEL selector = NSSelectorFromString(methodStr);
if (![self respondsToSelector:selector]) {
return;
}
[self performSelector:selector withObject:paraDict afterDelay:0.1f];
}
- (void)Recharge:(NSDictionary *)paraDict
{
PayViewController *pay = [[PayViewController alloc] init];
if ([paraDict objectForKey:@"back"]) {
pay.backRefreshBlock = _backRefreshBlock;
pay.backRefreshUrl = [paraDict objectForKey:@"back"];
}
pay.InvestState = @"Invest";
__weak typeof(self) weakSelf = self;
[weakSelf.object.navigationController pushViewController:pay animated:YES];
}
@end
以上的内容只包含了一个函数;后面所有的H5交互都会采用这种形式进行交互。
(二)如何使用,下面给出一个例子;
[[H5NativeInteractTool shareInstance] goFromSourceController:self toTargetUrl:urlString refreshBlock:^(NSString *url) {
__weak typeof(self) weakSelf = self;
NSURLRequest *newRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0f];
[weakSelf.allWebVC loadRequest:newRequest];
weakSelf.backRefreshState = @"refresh";
}];
执行完跳转页面的所有逻辑成功后,最后执行refreshBlock;
(三)webview中有回调地址的疑惑;
(1)上述代码中有一个block(refreshBlock),这个主要是用来,操作成功后页面回调所执行的刷新url的操作。我使用它之前一直是有疑问的,因为我见很多的app都没有backurl的概念,一般都是截取url,push到新的原生界面,然后在pop回来。
(2)我个人认为,如果真的要实现跳转指定的url,就应该创建一个通用的webview界面,而所有的跳转url都会到这个webview中显示,因为这样控制性会相对来得好一些,webview的goBack操作,如果webview的历史记录混乱了,将是一个比较难忍的问题。