之前写过web与html5的交互,那时的载体是UIWebview,基于uiwebview属于比较老旧的网页载体,且内存消耗比较大,故今日打算把UIWebview替换成WKwebview,起初只认为是换个类名那么简单,替换过程才发现,是自己想的太简单了,下面来说说uiweb与wkweb与html5交互时的异同(注:这篇文章只是针对原生与html5交互,且UIWebview与原生交互用的是javascriptcore框架):
一,关于代理方法与传值
uiwebview常用的是UIWebviewDelegate 与JSExport,wkwebview常用的是WKSCriptMessageHandler,WKNavigationDelegate,WKUIDelagete。
1, UIWebview的UIWebviewDelegate与WKWebview的WKNavigationDelegate功能相似,体现网页的加载周期;这两个协议里的都有网页在不同阶段里的代理方法,现列出如下:
1》页面准备加载:UIWebViewDelegate - webView:shouldStartLoadWithRequest:navigationType相当于 WKNavigationDelegate - webView:didStartProvisionalNavigation:
2》网页已开始加载:UIWebViewDelegate - webViewDidStartLoad: 相当于WKNavigationDelegate - webView:didCommitNavigation:
3》页面全部加载:WKNavigationDelegate - webView:didCommitNavigation:相当于WKNavigationDelegate - webView:didFinishNavigation
4》页面加载失败:UIWebViewDelegate - webView:didFailLoadWithError:相当于WKNavigationDelegate - webView:didFailNavigation:withError:或者WKNavigationDelegate - webView:didFailProvisionalNavigation:withError:
如果你之前只是用到了以上列出的 UIWebViewDelegate 中的几个方法,那么只是简单地换一个方法名,让你的 ViewController 继承 WKNavigationDelegate ,继续用就可以了。
2,UIWebView的JSExport与WKWebview的WKSCriptMessageHandler都是与js交互的;
UIWebview与js交互分为js向oc传数据与oc向js传数据。而这双向的传值方法,都是声明在遵守JSExport的在自定义协议中,当然这个自定义协议中的代理方法也都是自定义的。而WKwebview的WKSCriptMessageHandler协议中只有一个系统的方法,这个代理方法只能用来接收js传到原生的数据,而原生要想给js传数据,需要用WKWebViewConfiguration这个类对象的userContentController属性(此属性是WKUserContentController类型的),举个��:
1》UIWebview
oc给js传数据:(1)oc端:在遵守JSExport的自定义协议中声明一个方法-(void)getJsonText;
(2)oc端:在web的控制器的.m文件实现该方法,实现此方法时,通过jsContex创建了JSValue类型的对象,此对象即为js中的一个全局的方法
-(void)getJsonText{
JSValue *getJsonTextJsMethod =self.jsContext[@"getJsonTextJsMethod"];
NSArray *array =@[@“你好吗”];
NSString *string = [[NSStringalloc]initWithFormat:@"%@",array[0]];
[getJsonTextJsMethodcallWithArguments:array];
}
(3)js端:在js里调用getJsonText方法,并在getJsonTextJsMethod中接收oc端来的数据
$(document).ready(function() { jsInterface.getJsonText()} //调用自定义协议的代理方法
var getJsonTextJsMethod =function(json){} // json即为从oc端来的数据
js给oc传数据:(1)oc端:在遵守JSExport的自定义协议中声明一个方法-(void)gotoNext:(NSString*)string;
(2)oc端:在web的控制器的.m文件实现该方法
-(void)gotoNext:(NSString*)string{NSLog(@"gotoNex:%lu",string);} //参数string即为从js传到oc端的数据
(3)js端:在适当的地方给自定义代理方法的参数赋值即可,此参数即为要传输的数据;
jsInterface.gotoNext(‘很好啊’);
2》WKwebView
wkwebview与js交互时,创建web的方式与uiwebview有所区别,创建方式如下:
WKWebViewConfiguration *config = [[WKWebViewConfigurationalloc] init];
self.webView = [[WKWebViewalloc] initWithFrame:[UIScreenmainScreen].boundsconfiguration:config];
并设置两个代理 self.webView.navigationDelegate =self; self.webView.UIDelegate =self;
oc给js传数据:(1) oc端:WKWebview的 evaluateJavaScript方法,调用js的全局方法,如下
[self.webViewevaluateJavaScript:@"jsMethodTwo('你好吗')"completionHandler:nil] //jsMethod为js的全局方法,此方法的参数即为要传的数据
(2)js端:在js的全局方法中获取传来的数据
function jsMethodTwo(msg) {parseJson(msg);} // msg即为传过来的数据,内容为'你好吗'
js给oc传数据:(1)oc端:创建WKUserContentController类实例,并将passFromJsToOC添加到类此实例对象中,如下
WKWebViewConfiguration *config = [[WKWebViewConfigurationalloc] init];
WKUserContentController *userCC = config.userContentController
[userCCaddScriptMessageHandler:selfname:@"passFromJsToOC"]
(2)js端:调用如下方法:window.webkit.messageHandlers."oc中添加到WKUserContentController的方法名".postMessage('要传的数据'),距离如下
window.webkit.messageHandlers.passFromJsToOC.postMessage('文字') //passFromJsToOC为添加到WKUserContentController类的实例对象
(3)js端:在wkwebview的WKScriptMessageHandler协议的代理方法-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{}中,接收js传过来的数据,如下:-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ self.myString = message.body;} //注 message.body即为传过来的数据
二,关于弹框
1》UIWebview:
h5代码与原生oc交互时,调试是个很令人头疼的问题,h5的代码要拷贝到我们本地的文件中,h5的逻辑层js在我们的工程中打断点不好使,调试起来很受罪啊!还好js端有alert,这个alert弹框在UIWebview交互时还是挺给力的;
2》WKWebview:
上边提到的alert是原生与h5交互的利器,可是在WKWebview时却不好使了,在此我们就不得不提那个叫做WKUIDelegate的协议了,我们需要实现此协议的代理方法,此协议关于弹窗的代理方法有三个,分别针对js端不同的弹框,当然与我们调试相关性最大的就是提示框alert了,其他两个弹框分别是确认框confirm以及输入框prompt;这三种弹框分别对应着三个不同的代理方法,代理方法里要实现的东西就感觉比较恶心了,在代理方法里我们需要创建oc原生的对应的弹窗,真不知道wkwebview为何要这么弄一下。js弹框与之对应的代理方法及代理方法内的实现如下:
2.1》js的提示框alert 对应的代理方法及其实现是:
-(void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
UIAlertController *alertController = [UIAlertControlleralertControllerWithTitle:@"提示"message:message?:@""preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertActionactionWithTitle:@"确认"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {
completionHandler();
}])];
[selfpresentViewController:alertControlleranimated:YEScompletion:nil];
}
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"确认框"message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertActionactionWithTitle:@"确定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {
completionHandler(YES);
}]];
[alert addAction:[UIAlertActionactionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:^(UIAlertAction *_Nonnull action) {
completionHandler(NO);
}]];
[selfpresentViewController:alert animated:YEScompletion:NULL];
}
2.3》js的输入框 对应的代理方法及实现如下:
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"输入框"message:prompt preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *_Nonnull textField) {
textField.textColor = [UIColorblackColor];
textField.placeholder = defaultText;
}];
[alert addAction:[UIAlertActionactionWithTitle:@"确定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {
completionHandler([[alert.textFieldslastObject] text]);
}]];
[alert addAction:[UIAlertActionactionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:^(UIAlertAction *_Nonnull action) {
completionHandler(nil);
}]];
[selfpresentViewController:alert animated:YEScompletion:NULL];
}
当然了,如果不使用这个代理也是可以的,你可以先将你想要弹出的内容传到原生oc端,在收到js传来消息的代理方法里弹弹框,当然也是原生的弹框。
三、关于html5导入本地的路径
这个与web的载体无关,无论UIWebview与WKWebview都有这个问题
html5放在本地交互时,如果将html5文件整个的按group的形式导入,在加载本地html5时,js与css都没有出现,后闭门造车的发现把html文件中引入的js与css的路径去掉之后,ok,问题解决了,于是乎,以为万事ok了,最近在捣鼓WKWebview的时候,发现其实有其他更好的方法去解决这个问题。只需要在导入html5文件时换个方法就可以了,导入过程如下:
1》 以group的方式导入父文件夹
2》以folder的方式导入子文件夹,即css js html文件夹
3》导入结果截图如下:
按上述方式到文件后,运行正常,ok问题解决
四、关于oc顶用js的方法问题(只针对于WKWebview evaluateJavaScript):
上边提到WKWebview的oc调用js的方法用的是evaluateJavaScript,调用js的方法分以下情况:
1》调用js无参数的方法(对于交互似乎没啥意义):
oc端 [self.wkWebViewevaluateJavaScript:@"alertMobile()"completionHandler:nil]
js端 function alertMobile() {}
2》调用js有参数的方法(用参数传数据)
[self.wkWebViewevaluateJavaScript:@"alertName('小红')"completionHandler:nil]
function alertName(msg) {alert( msg)}
3》调用js有参数的方法(用参数传数据,用返回值对oc端做回执)[self.wkWebViewevaluateJavaScript:@"alertSendMsg('哈喽')" completionHandler:^(id result,NSError * _Nullable error) {NSLog(@"返回的值是:%@",result);}]
function alertSendMsg(num,msg) { document.getElementById('msg').innerHTML ='这是我的手机号:' + num +',' + msg + '!!'return'nihaoma';}
result即为js方法返回的nihaoma感觉这个回执挺有用的,省去alert查看了
五、关于加载本地html并操控本地html跳页
这个题目五有点烦人啊,乍看一脸茫然吧,是的,这种跳转方式我也感觉一脸愕然。是的,就是这样设计的,客户端加载本地a页面之后如果想跳的本地b网页,是由客户端操控的,并不是我们平时那样的非本地的链接方式或本地的href方式,而是超出于宇宙之上的第三种方式--客户端控制,并给下一页传报文。
1》UIWebview:
需要前面提到的自定义协议里的代理方法了,跳转下一页事件中,js传值给oc,在oc获取值时跳转,然后按给js传值的方式传报文给js,当然这需要在网页初始加载的时候传过去
2》WKWebview:
这个就需要研究下web的加载周期了,依旧是在跳转下一页事件中,js传值给oc,在oc获取值时按加载前页的方式加载第二页,这是后报文就得跟上节奏适时传给js了,这个传报文的位置没有UIwebview那么自动了,我们需要在第一页页面加载完成的时候(即如下代理方法)把第二页需要的报文按上述相应传值方式传过去。
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{}
五、关于oc给js传数据的数据格式这里只说WKWebview,UIWebview没有这个问题
WKWebview中oc在给js传值时,传的数据不能有\n,否则传不过去,如果你要穿的是报文之类的数据,千万要遍历一遍这个数据去掉里边的换行,否则是传不过来的。