在iOS应用的开发过程中,我们经常会使用到WebView,当我们对WebView进行操作的时候,有时会需要进行源生的操作.那么我记下来就与大家分享一下OC与JS交互.

首先先说第一种方法,并没有牵扯OC与JS交互,只是做拦截和跳转.

拦截跳转的URL,跳转源生界面(用起来感觉怪怪的,万一URL更换了怎么办.)
UIWebView
//UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *url = request.URL.absoluteString;
if ([url rangeOfString:@"需要跳转源生界面的URL判断"].location != NSNotFound) {
//跳转原生界面
return NO;
}
return YES;
}
WKWebView
//使用WKWebview需要导入WebKit
#import <WebKit/WebKit.h>
//WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString *url = navigationAction.request.URL.absoluteString;
if ([url rangeOfString:@"需要跳转源生界面的URL判断"].location != NSNotFound) {
//跳转原生界面 //Cancel the navigation
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}

----------✨↓↓↓↓↓↓✨----------

OC与JS交互(WebView监听事件)

正入主题.

一.OC调用JS

1.UIWebView

①直接运行

NSString *jsStr = @"执行的JS代码";
[webView stringByEvaluatingJavaScriptFromString:jsStr];

②使用JavaScriptCore框架

#import <JavaScriptCore/JavaScriptCore.h>
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//获取webview中的JS内容
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
NSString *runJS = @"执行的JS代码";
//准备执行的JS代码
[context evaluateScript:runJS];
}
2.WKWebView
[webView evaluateJavaScript:@"执行的JS代码" completionHandler:^(id _Nullable response, NSError * _Nullable error) {

}];

二.JS调用OC ✨✨✨✨✨✨

当网页触发某种操作,可以给App传递消息.比如WebView中购买某样东西,点击购买,需要获取这件商品的订单信息,并且需要App进行源生的支付.
这种方法需要你和后台或者前端协商好一下,让他们在执行JS方法的时候,将你需要的数据放到你能拿到的位置.

下面简单贴一个HTML文件.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>App与WebView交互</title>
</head>
<body>
<button style="width: 100%; height: 100px;" onclick="buttonClick()">点击购买</button>
</body>
<script>
//按钮点击事件
function buttonClick() {
//传递的信息
var jsonStr = '{"id":"666", "message":"我是传递的数据"}'; //UIWebView使用
getMessage(jsonStr); //WKWebView使用
//使用下方方法,会报错,为使界面执行逻辑通畅,因此使用try-catch
try {
window.webkit.messageHandlers.getMessage.postMessage(jsonStr)
} catch(error) {
console.log(error)
}
}
function getMessage(json){
//空方法
}
</script>
</html>
window.webkit.messageHandlers.<方法名>.postMessage(<数据>)
JS端写此方法的盆友可能会报错,导致界面逻辑无法进行,因此使用try-catch就好了.

我在网页上只写了一个按钮.点击按钮,会触发buttonClick()方法.

UIWebView

在网页加载完成的时候检测JS方法执行.

- (void)webViewDidFinishLoad:(UIWebView *)webView {
//核心方法如下
JSContext *content = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//此处的getMessage和JS方法中的getMessage名称一致.
content[@"getMessage"] = ^() {
NSArray *arguments = [JSContext currentArguments];
for (JSValue *jsValue in arguments) {
NSLog(@"=======%@",jsValue);
}
};
}

由上方方法,当JS方法getMessage()执行的时候,此方法回调的jsValue内容就是我们需要的内容.(HTML中JS传递的数据)

WebView中的getMessage与HTML文件JS方法的getMessage名称需保持一致.

WKWebView

实现WKScriptMessageHandler的代理方法.

//设置addScriptMessageHandler与JS对应方法名.并且设置<WKScriptMessageHandler>协议与协议方法
[[_webView configuration].userContentController addScriptMessageHandler:self name:@"getMessage"]; //WKScriptMessageHandler协议方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
//code
NSLog(@"name = %@, body = %@", message.name, message.body);
}

上方-addScriptMessageHandler:name:方法中name填写的方法名必须与window.webkit.messageHandlers.<方法名>.postMessage(<数据>)中的方法名一致.

当JS端执行window.webkit.messageHandlers.<方法名>.postMessage(<数据>).
此协议方法就会被执行.,根据message.name判断一下自己需要执行哪步操作.message.body即是传输的参数信息.(HTML中JS传递的数据)

WKWebView 内存泄露
如果这时候你在selfdealloc打个断点,会发现self没有释放!这显然是不行的!谷歌后看到一种解决方法,如下

思路是:可以另外创建一个代理对象,然后通过代理对象回调指定的self.

WeakScriptMessageDelegate
//.h
@interface WeakScriptMessageDelegate : NSObject<WKScriptMessageHandler> @property (nonatomic, assign) id<WKScriptMessageHandler> scriptDelegate; - (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate; @end
//.m
@implementation WeakScriptMessageDelegate - (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
self = [super init];
if (self) {
_scriptDelegate = scriptDelegate;
}
return self;
} - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
[self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
} @end

使用WeakScriptMessageDelegate 设置代理

//设置addScriptMessageHandler与name.并且设置<WKScriptMessageHandler>协议与协议方法
[[_wkWebView configuration].userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"方法名"];

//必须在 WebView的ViewController的- (void)dealloc{}方法中进行销毁. 这样才能销毁

- (void)dealloc {
//...
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"方法名"];
//...
}

当然,安卓开发的盆友也是可以通过这种方式从中获取网页的数据的.安卓注入的接口名称在JS中也是会报错的.因此也需要try-catch.

一下是补充WKWebView用法

一、加载网页

加载网页或HTML代码的方式与UIWebView相同,代码示例如下:

 WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
[self.view addSubview:webView];

二、加载的状态回调 (WKNavigationDelegate)

用来追踪加载过程(页面开始加载、加载完成、加载失败)的方法:

// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;

页面跳转的代理方法:

// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

三、新的WKUIDelegate协议

这个协议主要用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框),下面是警告框的例子:

//在JS端调用alert函数时,会触发此代理方法。
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler; //JS端调用confirm函数时,会触发此方法
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler; //JS端调用prompt函数时,会触发此方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;

四、动态加载并运行JS代码

用于在客户端内部加入JS代码,并执行,示例如下:

// 图片缩放的js代码
NSString *js = @"var count = document.images.length;for (var i = 0; i < count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '张图');";
// 根据JS字符串初始化WKUserScript对象
WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
// 根据生成的WKUserScript对象,初始化WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
[config.userContentController addUserScript:script];
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
[_webView loadHTMLString:@"<head></head><imgea src='http://www.nsu.edu.cn/v/2014v3/img/background/3.jpg' />"baseURL:nil];
[self.view addSubview:_webView];

目前,大多数App需要支持iOS7以上的版本,而WKWebView只在iOS8后才能用,所以需要一个兼容性方案,既iOS7下用UIWebView,iOS8后用WKWebView。这个库提供了这种兼容性方案:https://github.com/wangyangcc/IMYWebView


												

iOS(UIWebView 和WKWebView)OC与JS交互 之二的更多相关文章

  1. iOS WKWebView OC 与 JS 交互学习

    我写WKWebView 想让 服务端相应 一个 方法但是不响应,根据 UIWebView 用 JSContext就能拿到响应的处理经验是不是服务端 也需要 对 WKwebView有兼容的一个写法??? ...

  2. OC与JS交互之WebViewJavascriptBridge

    上一篇文章介绍了通过UIWebView实现了OC与JS交互的可能性及实现的原理,并且简单的实现了一个小的示例DEMO,当然也有一部分遗留问题,使用原生实现过程比较繁琐,代码难以维护.这篇文章主要介绍下 ...

  3. OC与JS交互之UIWebView

    随着H5的强大,hybrid app已经成为当前互联网的大方向,单纯的native app和web app在某些方面显得就很劣势.关于H5的发展史,这里有一篇文章推荐给大家,今天我们来学习最基础的基于 ...

  4. 李洪强iOS经典面试题147-WebView与JS交互

    李洪强iOS经典面试题147-WebView与JS交互   WebView与JS交互 iOS中调用HTML 1. 加载网页 NSURL *url = [[NSBundle mainBundle] UR ...

  5. OC与JS交互前言

    OC与JS交互过程中,可能会需要使用本地image资源及html,css,js文件,这些资源应该如何被加载? 一.WebView加载HTML UIWebView提供了三个方法来加载html资源 1.  ...

  6. OC和JS交互的三种方法

    看简书上说一共有六种OC和JS交互的方法,但是前三种原理都一致,都是通过检测.拦截Url地址实现互相调用的.剩下的react native等第三方框架原理不一样,也没有去研究,下边记录我使用的三种方法 ...

  7. Mac Webview OC与JS交互实现

    1.首先,需要定义一个JS可识别的变量(如external)用于OC与JS交互 - (void)webView:(WebView *)sender didClearWindowObject:(WebS ...

  8. iOS(WKWebView)OC与JS交互 之三

      随着H5功能愈发的强大,没进行过混合开发的小伙们都不好意思说自己能够独立进行iOS的app开发,在iOS7操作系统下,常用的native,js交互框架有easy-js,WebViewJavascr ...

  9. OC与JS交互之WKWebView

    上一篇文章我们使用了JavaScriptCore框架重写了之前的示例,iOS8苹果偏爱HTML5,重构了UIWebVIew,给我们带来了WKWebView,使其性能.稳定性.功能大幅度提升,也更好的支 ...

随机推荐

  1. Drawing line on a click on ZedGraph Pane

    https://stackoverflow.com/questions/12422398/drawing-line-on-a-click-on-zedgraph-pane public Form1() ...

  2. 1.Linux进程--进程标识号

    函数原型 pid_t fork(void); fork的奇异之处在于它被调用一次,却返回两次,它可能有三种不同的返回值: 1.在父进程中.fork返回新创建的子进程的PID 2.在子进程中,fork返 ...

  3. 用.Net打造一个移动客户端(Android/IOS)的服务端框架NHM(四)——Android端Http访问类(转)

    本章目的 在上一章中,我们利用Hibernate Tools完成了Android Model层的建立,依赖Hibernate Tools的强大功能,自动生成了Model层.在本章,我们将继续我们的项目 ...

  4. 微软BI 之SSAS 系列 - 维度的优化,灌木丛属性关系,以及自然层次结构与非自然层次结构的概念

    维度的优化 在 SSAS 开发设计过程中,维度的优化非常重要,因为它在 SSAS 分析服务性能调优的过程中往往能起到一个非常重要的作用. 一般来说,对于 Cube 的性能优化第一步可能考虑的就是查看维 ...

  5. oracle数据库将一列的值拼接成一行,并且各个值之间用逗号隔开

    使用场景:把某一列值转换为逗号分隔的字符串 例子:比如查询所有的的表空间如下,现在要获得所有的表空间用逗号分隔的字符串(比如rman duplicate的时候skip表空间) SQL> sele ...

  6. void android.graphics.Bitmap.recycle()

    void android.graphics.Bitmap.recycle() Free up the memory associated with this bitmap's pixels, and ...

  7. 阿里云logtail采集IDC机房机器需添加AliUids操作

    Configure AliUids for ECS servers under other Alibaba Cloud accounts or on-premises IDCs If Logtail ...

  8. C#中DataTable删除多条数据

    //一般情况下我们会这么删除 DataTable dt = new DataTable(); for (int i = 0; i < dt.Rows.Count; i++) { if (99 % ...

  9. [Aaronyang] 写给自己的WPF4.5 笔记6[三巴掌-大数据加载与WPF4.5 验证体系详解 2/3]

    我要做回自己--Aaronyang的博客(www.ayjs.net) 博客摘要: Virtualizing虚拟化DEMO 和 大数据加载的思路及相关知识 WPF数据提供者的使用ObjectDataPr ...

  10. FTP上传、下载(简单小例子)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.I ...