Web(Html或Html5)与Native(Objective-C或者Swift)交互

一、利用WebViewJavascriptBridge实现Native和Web的交互

参考链接1
参考链接2

WebViewJavascriptBridge.jpg

四个必须知道的方法:

一、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函数了。


必知.png

实现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;// 和注册事件搭配使用。


两次回调.gif

关键代码:

***************************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的代理有两个,名称分别为WKNavigationDelegateWKUIDelegate
参考链接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:在UIWebViewwebView:shouldStartLoadWithRequest:navigationType:代理方法中拦截URL实现Native和Web的交互
做法2:在WKWebViewwebView:decidePolicyForNavigationAction:decisionHandler:代理方法中拦截URL实现Native和Web的交互

交互步骤(UIWebViewWKWebView大致相同,这里只说具体步骤):

一、创建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。


我之前的文章1
我之前的文章2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容