一、利用WebViewJavascriptBridge实现Native和Web的交互
四个必须知道的方法:
一、Native(即:Objective-C或Swift)调用Javascript方法,也就是说,调用了该方法,程序就是自动执行html页面中的shareResult函数,实现弹框显示内容.我们无需过问为什么会执行,该方法是系统给我们封装好的,就是实现这一个功能的。
NSString *script = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
[self.webView stringByEvaluatingJavaScriptFromString:script]; // 就会执行index.html文件中的shareResult函数了
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
二、Javascript调用Native(即:Objective-C或Swift)方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
三,四、通过registerHandler+callHandler这俩方法实现Native(即OC\Swift)和JS的信息传递(两个方法在WebViewJavascriptBridge类中)
#warning 点击JS页面上的按钮要将修改OC端的内容,必在JS页面调用callHandler,必在OC端调用registerHandler,反之点击OC端的按钮要修改JS页面上的内容,必在OC端调用callHandler,必在JS页面上调用registerHandler。
// 注册事件
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
// 和注册事件搭配使用。
- (void)callHandler:(NSString*)handlerName;
stringByEvaluatingJavaScriptFromString实现Native(即:Objective-C或Swift)调用Javascript方法
利用stringByEvaluatingJavaScriptFromString方法可以很方便的操作UIWebView中的页面元素。该方法传入的参数就是JavaScript的代码,只要传入了JavaScript的代码,我们就可以直接在UIWebView上面执行JS方法。也就是说将javascript嵌入uiwebview中,通过这个方法我们可以在iOS中与UIWebView中的网页元素交互。
本文要介绍的内容,主要是讲解了stringByEvaluatingJavaScriptFromString的用法,它的功能非常的强大,用起来非常简单,通过它我们可以很方便的操作uiwebview中的页面元素。
UIWebView是iOS最常用的SDK之一,它有一个stringByEvaluatingJavaScriptFromString方法可以将javascript嵌入页面中,通过这个方法我们可以在iOS中与UIWebView中的网页元素交互。
使用stringByEvaluatingJavaScriptFromString方法,需要等UIWebView中的页面加载完成之后去调用。我们在界面上拖放一个UIWebView控件。在Load中将google mobile加载到这个控件中,代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
webview.backgroundColor = [UIColor clearColor];
webview.scalesPageToFit =YES;
webview.delegate =self;
// google浏览器
NSURL *url =[[NSURL alloc] initWithString:@"http://www.google.com.hk/m?gl=CN&hl=zh_CN&source=ihp"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
// 在webview中加载google浏览器
[webview loadRequest:request];
}
我们在webViewDidFinishLoad方法中就可以通过javascript操作界面元素了。
1、获取当前页面的url。
- (void)webViewDidFinishLoad:(UIWebView *)webView { NSString *currentURL = [webView stringByEvaluatingJavaScriptFromString:@"document.location.href"]; }
2、获取页面title:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *currentURL = [webView stringByEvaluatingJavaScriptFromString:@"document.location.href"];
NSString *title = [webview stringByEvaluatingJavaScriptFromString:@"document.title"];
}
3、修改界面元素的值。
NSString *js_result = [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByName('q')[0].value='侯文富专栏';"];
4、表单提交:
NSString *js_result2 = [webView stringByEvaluatingJavaScriptFromString:@"document.forms[0].submit(); "];
这样就实现了在google搜索关键字:“侯文富的专栏”的功能。
5、插入js代码
上面的功能我们可以封装到一个js函数中,将这个函数插入到页面上执行,代码如下:
[webView stringByEvaluatingJavaScriptFromString:@"var script = document.createElement('script');"
"script.type = 'text/javascript';"
"script.text = \"function myFunction() { "
"var field = document.getElementsByName('q')[0];"
"field.value='CoderZb的简书';"
"document.forms[0].submit();"
"}
\";"
"document.getElementsByTagName('head')[0].appendChild(script); "];
[webView stringByEvaluatingJavaScriptFromString:@"myFunction();" ];
大体步骤:
a、首先通过js创建一个script的标签,type为'text/javascript'。
b、然后在这个标签中插入一段字符串,这段字符串就是一个函数:myFunction,这个函数可以实现 在手机中打开google浏览器时,输入CoderZb,就可以自动搜索到和CoderZb相关的内容。
c、然后使用stringByEvaluatingJavaScriptFromString执行myFunction函数,就可以自动执行Html文件中嵌套的myFunction函数了。
实现OC和JS的交互,有一个核心知识点:OC会调用JS的时候,JS也必须回调OC,这样达到的效果是:
点击JS页面上的某个按钮,OC中就会监听到,这个过程用了一个回调,然后将OC中的"我是CoderZb"的回调给JS,这是第二个回调,这样JS中就拿到了"我是CoderZb",将这段内容显示到JS页面中的指定位置。
- 用到的方法:
#warning 通过registerHandler+callHandler这俩方法实现Native(即OC\Swift)和JS的信息传递。
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;// 注册事件
- (void)callHandler:(NSString*)handlerName;// 和注册事件搭配使用。
关键代码:
***************************OC页面*****************************
- (void)registScanFunction{// 扫一扫
// OC端对JS页面上的"扫一扫"按钮的注册事件(只有注册了,那么点击JS页面上的"扫一扫"按钮,就会实时触发并执行OC端的这段代码块,相当于监听,监听就是实时的,代理和block也是实时监听的。).
// (注册一个名为scanClick1的事件,在JS页面我们只要点击了"扫一扫"按钮(强烈注意:"扫一扫"按钮是JS页面中的,也就是Html页面中的,不是OC中的哦,OC只是加载了Html页面中的控件而已),就会执行JS页面中的scanClick()方法,然后在方法中执行callHandler方法,从而就可以触发并执行oc中 参数为scanClick1的registerHandler方法中的代码块(注意:只是执行代码块中的内容哦)。
[_webViewBridge registerHandler:@"scanClick1" handler:^(id data, WVJBResponseCallback responseCallback) {// 执行时间:index.html中的callHandler('scanClick1', {'foo': 'bar'}, function(response) {方法。 真实调用时间:点击JS页面上的"扫一扫"按钮就会调用
NSLog(@"扫一扫");
NSString *scanResult = @"我是CoderZb";
// 触发block,将结果返回给JS。就会执行index.html中的function(response) {alert('扫描结果:' + response);document.getElementById("returnValue").value = response;}
responseCallback(scanResult);
}];
}
******************************JS页面***************************
function scanClick() {
// JS调用WebViewJavascriptBridge类中的callHandler方法并传入名为的scanClick1参数(还有其他参数),就可以触发并执行 在OC中注册的参数为scanClick1的registerHandler方法中的代码块(注意:只是执行代码块中的内容哦),然后在代码块中触发responseCallback(location);这个block,将location中的内容返回给JS。具体返回给JS的哪个地方:返回给当时的调用者,就是下面这段代码的callHandler,因为传递过来了location,所以location就是下面代码的response。
WebViewJavascriptBridge.callHandler('scanClick1', {'foo': 'bar'}, function(response) {
// alert()是弹框提示的标志,属于js原生方法.http://www.w3school.com.cn/jsref/met_win_alert.asp
// response来自WebViewController.h类中的NSString *scanResult = @"http://www.baidu.com";
alert('扫描结果:' + response);
// 将id为returnValue的元素的值改为response。即修改的是当前文件的 <textarea id ="returnValue" type="value" rows="5" cols="50"> 代码中的值
document.getElementById("returnValue").value = response;
})
}
UIWebView的代理,名称为UIWebViewDelegate
参考链接
注意:代理方法的执行顺序就是下面标号的顺序
// 1.准备加载内容时调用的方法,通过返回值来进行是否加载的设置.一般在此方法截获数据。当webview中有多次加载url的请求,这个代理方法就会调用多次。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
// 2.开始加载时调用的方法
- (void)webViewDidStartLoad:(UIWebView *)webView;
// 3.结束加载时调用的方法(**执行完这个方法,网页才会显示,注意顺序**)
- (void)webViewDidFinishLoad:(UIWebView *)webView;
// 3.加载失败时调用的方法
- (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error;
拓展:UIWebView的代理UIWebViewDelegate等同于WKWebView的代理WKNavigationDelegate
// 1.等同于UIWebView的shouldStartLoadWithRequest:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
// 2.等同于UIWebView的webViewDidStartLoad:
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// 3.等同于UIWebView的webViewDidFinishLoad:
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;
// 3.等同于UIWebView的didFailLoadWithError:
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
WKWebView的代理有两个,名称分别为WKNavigationDelegate和WKUIDelegate
参考链接1
参考链接2
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
NSLog(@"webViewWebContentProcessDidTerminate: 当Web视图的网页内容被终止时调用。");
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSLog(@"webView:didFinishNavigation: 响应渲染完成后调用该方法 webView : %@ -- navigation : %@ \n\n",webView,navigation);
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSLog(@"webView:didStartProvisionalNavigation: 开始请求 \n\n");
}
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"webView:didCommitNavigation: 响应的内容到达主页面的时候响应,刚准备开始渲染页面应用 \n\n");
}
// error
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
// 类似 UIWebView 的- webView:didFailLoadWithError:
NSLog(@"webView:didFailProvisionalNavigation:withError: 启动时加载数据发生错误就会调用这个方法。 \n\n");
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
NSLog(@"webView:didFailNavigation: 当一个正在提交的页面在跳转过程中出现错误时调用这个方法。 \n\n");
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSLog(@"请求前会先进入这个方法 webView:decidePolicyForNavigationActiondecisionHandler: %@ \n\n ",navigationAction.request);
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSLog(@"返回响应前先会调用这个方法 并且已经能接收到响应webView:decidePolicyForNavigationResponse:decisionHandler: Response?%@ \n\n",navigationResponse.response);
decisionHandler(WKNavigationResponsePolicyAllow);
}
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
NSLog(@"webView:didReceiveServerRedirectForProvisionalNavigation: 重定向的时候就会调用 \n\n");
}
// 用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框)
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
// NSLog(@"%@",message);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
二、利用苹果提供的JavaScriptCore原生框架实现Native和Web的交互
利用JavaScriptCore原生框架 实现Native(即:Objective-C或Swift)调用Javascript方法 有两个核心的做法:
- 做法1
NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"北京市房山区"];
// 使用JSContext的evaluateScript方法来调用JS的setLocation函数。
[[JSContext currentContext] evaluateScript:jsStr];
- 做法2
// 调用Html页面中的setLocation函数并将"北京市房山区"作为函数的参数
[JSContext currentContext][@"setLocation"] callWithArguments:@[@"北京市房山区"]];
JSContext, JSContext是代表JS的执行环境,通过-evaluateScript:方法就可以执行JS代码
JSValue, JSValue封装了JS与ObjC中的对应的类型,以及调用JS的API等
JSExport, JSExport是一个协议,遵守此协议,就可以定义我们自己的协议,在协议中声明的API都会在JS中暴露出来,才能调用。
交互步骤:
一、创建UIWebView,并加载本地HTML
self.webView = [[UIWebView alloc] initWithFrame:self.view.frame]; self.webView.delegate = self;
NSURL *htmlURL = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];
// NSURL *htmlURL = [NSURL URLWithString:@"http://www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL];
// 如果不想要webView 的回弹效果
self.webView.scrollView.bounces = NO;
// UIWebView 滚动的比较慢,这里设置为正常速度
self.webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
[self.webView loadRequest:request]; [self.view addSubview:self.webView];
二、JSContext 是运行 JavaScript 代码的环境,所以创建一个 JSContext
后,可以很容易地运行 JavaScript 代码
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
三、点击Html界面上的"获取定位"按钮
function locationClick() {
// 就会执行WebViewController类的context[@"getLocation"] = ^() {代码块中的内容
getLocation();
}
四、触发并执行OC端的代码块
- 写法1
context[@"getLocation"] = ^() {// 执行时间:index.html中的getLocation();函数
// 获取位置信息
// 将结果返回给js
NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"北京市房山区"];
// 将JS的代码包装在JSContext,这样就能识别并运行JS代码了。因此就会去index.html文件中调用setLocation函数(为什么会去index.html文件中找?因为index.html文件已经被加载到了webView上)实现弹框功能。
[[JSContext currentContext] evaluateScript:jsStr];
};
- 写法2
context[@"getLocation"] = ^() {// 执行时间:index.html中的getLocation();函数
// 获取位置信息
// 将结果返回给js
// 调用Html页面中的setLocation函数并将"北京市房山区"作为函数的参数
[[JSContext currentContext][@"setLocation"] callWithArguments:@[@"北京市房山区"]];
};
五、调用JSContext的evaluateScript:方法,就会执行包装的JS代码,也就是执行Html页面上的setLocation函数,从而弹框提示location中的内容。
function setLocation(location) {
// 弹框
asyncAlert(location);
// 将id为returnValue的元素的值改为location。即修改的是当前文件的 <textarea id ="returnValue" type="value" rows="5" cols="50"> 代码中的值
document.getElementById("returnValue").value = location;
}
注意点:WKWebView 不支持通过如下的KVC的方式创建JSContext ,因此就不能在WKWebView中使用JavaScriptCore
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
三、通过在拦截URL实现Native和Web的交互
做法1
:在UIWebView的webView:shouldStartLoadWithRequest:navigationType:代理方法中拦截URL实现Native和Web的交互
做法2
:在WKWebView的webView:decidePolicyForNavigationAction:decisionHandler:代理方法中拦截URL实现Native和Web的交互
交互步骤(UIWebView和WKWebView大致相同,这里只说具体步骤):
一、创建UIWebView,并加载本地HTML
**************UIWebView**************
self.webView = [[UIWebView alloc] initWithFrame:self.view.frame];
self.webView.delegate = self;
NSURL *htmlURL = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];
// NSURL *htmlURL = [NSURL URLWithString:@"http://www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL];
// 如果不想要webView 的回弹效果
self.webView.scrollView.bounces = NO;
// UIWebView 滚动的比较慢,这里设置为正常速度
self.webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
[self.webView loadRequest:request];
[self.view addSubview:self.webView];
**************WKWebView**************
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [WKUserContentController new];
WKPreferences *preferences = [WKPreferences new];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
preferences.minimumFontSize = 30.0;
configuration.preferences = preferences;
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
// NSString *urlStr = @"http://www.baidu.com";
// NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
// [self.webView loadRequest:request];
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
[self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
[self.view addSubview:self.webView];
二、点击Html界面上的"获取定位"按钮,然后调用loadURL(URL)这个API
function locationClick() {
loadURL("haleyAction://getLocation");
}
三、在代理方法中拦截URL
*******UIWebView的代理方法*******
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSLog(@"%@",request);
NSURL *URL = request.URL;
// 获取URL中的协议头
NSString *scheme = [URL scheme];
if ([scheme isEqualToString:@"haleyaction"]) {// 协议头确实为haleyaction
// 调用stringByEvaluatingJavaScriptFromString:方法,参数为JS代码,就会去index.html文件中执行JS代码里面的对应的函数
[self handleCustomAction:URL];
return NO;
}
return YES;
}
*******WKWebView的代理方法*******
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSURL *URL = navigationAction.request.URL;
NSString *scheme = [URL scheme];
if ([scheme isEqualToString:@"haleyaction"]) {
// 调用evaluateJavaScript:方法,参数为JS代码,就会去index.html文件中执行JS代码里面的对应的函数
[self handleCustomAction:URL];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
四、执行UIWebView或者WKWebview用于包装的JS代码的方法
********UIWebView********
NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"北京市房山区"];
// 将结果返回给js。
// 即:Native(即:Objective-C或Swift)调用Javascript方法。就会执行index.html文件中的setLocation()函数,实现弹框显示内容
[self.webView stringByEvaluatingJavaScriptFromString:jsStr];
********WKWebView********
NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"北京市房山区"];
// 将结果返回给js
// 即:Native(即:Objective-C或Swift)调用Javascript方法。就会执行index.html文件中的setLocation()函数,实现弹框显示内容
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
五、包装的是setLocation()函数,所以会执行如下代码
function setLocation(location) {
// 如果为UIWebview,直接显示默认的弹框,但是内容仍为location中的内容
// 如果为WKWebview,会执行WKWebview的代理方法runJavaScriptAlertPanelWithMessage:显示自定义的标题,但是内容仍为location中的内容,所以显示的是自定义的弹框
asyncAlert(location);
document.getElementById("returnValue").value = location;
}
六、第五步和UIWebview无关,只和WKWebview有关,即:执行WKWebview的代理方法实现自定义弹框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
// 自定义弹框
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
四、通过WKScriptMessageHandler实现Native和Web的交互
一、Native中注册函数。该函数必须和Html页面中的某个函数一致.
比如在OC中注册Location函数,那么在Html文件中必须有类似window.webkit.messageHandlers.Location.postMessage(null);这样的函数
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [WKUserContentController new];
// 不写这句代码,那么在userContentController代理方法的message就为空,即message.name为nil,
// 写了这句代码message.name的值为Location,那么经过if和else的嵌套,找到[self getLocation];符合要求,因此就会执行evaluateJavaScript:方法,底层会去index.html文件中执行该方法包装的setLocation()函数,从而才会显示弹框以及修改html页面上的内容
[configuration.userContentController addScriptMessageHandler:self name:@"Location"];
[configuration.userContentController addScriptMessageHandler:self name:@"ScanAction"];
[configuration.userContentController addScriptMessageHandler:self name:@"Share"];
[configuration.userContentController addScriptMessageHandler:self name:@"Color"];
[configuration.userContentController addScriptMessageHandler:self name:@"Pay"];
[configuration.userContentController addScriptMessageHandler:self name:@"Shake"];
[configuration.userContentController addScriptMessageHandler:self name:@"GoBack"];
[configuration.userContentController addScriptMessageHandler:self name:@"PlaySound"];
WKPreferences *preferences = [WKPreferences new];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
preferences.minimumFontSize = 40.0;
configuration.preferences = preferences;
二、创建WKWebView
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
// NSString *urlStr = @"http://www.baidu.com";
// NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
// [self.webView loadRequest:request];
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
[self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
self.webView.UIDelegate = self;
[self.view addSubview:self.webView];
三、点击Html界面上的"获取定位"按钮,然后调用以下API。
window.webkit.messageHandlers.{NAME}.postMessage(body) 这个 API 真正神奇的地方在于 JavaScript 对象可以自动转换为 Objective-C 或 Swift 对象。 详见http://nshipster.cn/wkwebkit/
// Location必须和Native端必须注册的Location一致(即代码1已经完成了注册)
window.webkit.messageHandlers.Location.postMessage(null);// 底层会执行**userContentController:didReceiveScriptMessage:**协议方法
四、执行协议方法WKScriptMessageHandler。
WKScriptMessageHandler协议方法中的message参数存储着步骤三中的Location和null
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
// message.body -- Allowed types are NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull.
// 步骤三messageHandlers中的参数Location
NSLog(@"body:%@",message.body);
// 步骤三postMessage中的参数null
NSLog(@"name:%@",message.name);
if ([message.name isEqualToString:@"ScanAction"]) {
NSLog(@"扫一扫");
} else if ([message.name isEqualToString:@"Location"]) {
// 调用evaluateJavaScript:方法,参数为JS代码,就会去index.html文件中执行JS代码里面的对应的函数
[self getLocation];
} else if ([message.name isEqualToString:@"Share"]) {
[self shareWithParams:message.body];
} else if ([message.name isEqualToString:@"Color"]) {
[self changeBGColor:message.body];
} else if ([message.name isEqualToString:@"Pay"]) {
[self payWithParams:message.body];
} else if ([message.name isEqualToString:@"Shake"]) {
[self shakeAction];
} else if ([message.name isEqualToString:@"GoBack"]) {
[self goBack];
} else if ([message.name isEqualToString:@"PlaySound"]) {
[self playSound:message.body];
}
}
五、执行WKWebview用于包装的JS代码的方法
NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"北京市房山区"];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
六、包装的是setLocation()函数,所以会执行如下代码
function setLocation(location) {
// 会去WKWebViewController类中执行webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:代理方法
asyncAlert(location);
// 将id为returnValue的元素的值改为location。即修改的是当前文件的 <textarea id ="returnValue" type="value" rows="5" cols="40"> 代码中的值
document.getElementById("returnValue").value = location;
}
七、执行WKWebview的代理方法实现自定义弹框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
UIWebView和WKWebView的代理方法区分:参考链接
UIWebView的代理UIWebViewDelegate等同于WKWebView的代理WKNavigationDelegate
UIWebViewDelegate
// 1.准备加载内容时调用的方法,通过返回值来进行是否加载的设置.一般在此方法截获数据。当webview中有多次加载url的请求,这个代理方法就会调用多次。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
// 2.开始加载时调用的方法
- (void)webViewDidStartLoad:(UIWebView *)webView;
// 3.结束加载时调用的方法(**执行完这个方法,网页才会显示,注意顺序**)
- (void)webViewDidFinishLoad:(UIWebView *)webView;
// 3.加载失败时调用的方法
- (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error;
WKNavigationDelegate
// 1.等同于UIWebView的shouldStartLoadWithRequest:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
// 2.等同于UIWebView的webViewDidStartLoad:
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// 3.等同于UIWebView的webViewDidFinishLoad:
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;
// 3.等同于UIWebView的didFailLoadWithError:
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
注意点:Native和JS的交互时,既可以显示JS中的弹框,也可以显示OC中原生的弹框
// JS中的弹框
alert(message);
// OC原生的弹框
// 弹框
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"方式一" message:@"这是OC原生的弹出窗" delegate:self cancelButtonTitle:@"收到" otherButtonTitles:nil];
[alertView show];
开发移动App主要有三种模式:Native、 Hybrid和Web App。
- Hybrid App 开发模式 http://www.tuicool.com/articles/riE3Yn
- [参考链接1]http://www.tuicool.com/articles/ea2An2Q
- [参考链接2] http://www.jianshu.com/p/6ba2507445e4
- [参考链接3]http://www.jianshu.com/p/e951af9e5e74
- [参考链接4]http://www.jianshu.com/p/b3e7fa514ab7?mType=Group