UIWebView和WKWebView与JS的通讯

简介


UIWebView 可以在你的应用程序嵌入网页内容并与之交互,但它笨重难用还有内存泄漏且。
WKWebView 代替了UIKit 中的 UIWebView 和 AppKit 中的 WebView,提供了统一的跨双平台 API。它拥有高达60fps的滚动刷新率以及内置手势,在性能、稳定性、功能方面有很大提升,支持了更多的HTML5特性,和 Safari 相同的 JavaScript 引擎。

1. UIWebView


1.1 加载方法

//创建UIWebView
UIWebView  *webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
webView.delegate = self;
[self.view addSubview:webView];
//加载URL
NSURL *url = [[NSURL alloc] initWithString:@"http://www.biadu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];

加载方法有3种

- (void)loadRequest:(NSURLRequest *)request; //直接装载URL
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;//加载HTML代码
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;//用于转载本地页面或者外部传来的NSData

1.2 代理回调

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;//进行加载前的预判断,如果返回YES,则会进入后续流程
- (void)webViewDidStartLoad:(UIWebView *)webView;//开始加载网页
- (void)webViewDidFinishLoad:(UIWebView *)webView;//加载完成
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;//加载失败

1.3 Native与JS的相互调用

1.3.1 Native调用JS中的方法

- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
//使用方法  JSFunctionName为JS内方法名称 paramete是参数
[self. webView stringByEvaluatingJavaScriptFromString:@"JSFunctionName('paramete')"]

1.3.2 JS调用Native中的方法

JS调用Native方法会在其他线程中调用,所以更新UI则需要放在主线程中进行
#import <JavaScriptCore/JavaScriptCore.h>
可以给类添加JSExport协议,
凡是添加了JSExport协议类我们就可以通过JS调用到其方法变量等。

//此宏定义用于js调用oc方法
#define JSExportAs(PropertyName, Selector) \
   @optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector
#endif

例如如下:在js中调用doFoo 即调用了本类的- (void)doFoo:(id)foo 方法

@textblock
    @protocol MyClassJavaScriptMethods <JSExport>
    JSExportAs(doFoo,
    - (NSString *)doFoo:(NSString *)foo
    );
    @end
@/textblock
//OC中的实现都需要加入 #import <JavaScriptCore/JavaScriptCore.h>
@property(nonatomic,strong)JSContext *jsContext;

//配置JSContext
-(void)configJsContext{
    if (_jsContext)  return;
    _jsContext  = [[self contentWebview] valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    JSExportObject *jsExportObject = [[JSExportObject alloc] init];
    jsExportObject.delegate = self;
    //把实现JSExport协议的对象添加到JSContext中的native对象doFoo方法 它会自动指定到 -(NSString *)doFoo:(id)foo这个方法当中去 且doFoo可以有NSString返回值
    _jsContext[@"native"] = jsExportObject;
}

//在OC中已经添加了native 对象,可以在js中调用native 定义的
   var s= {
        methodId: i,
        params: p
    };
  native. doFoo(JSON.stringify(s))

2. WKWebView


2.1 加载方法

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
YFMWKWebView *wkWebView = [[YFMWKWebView alloc] initWithFrame:self.view.bounds configuration:config];
wkWebView.navigationDelegate = self;
wkWebView.UIDelegate = self;
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.biadu.com"]];
[wkWebView loadRequest:mutableRequest];

加载方法有4种, 基本与UIWebView相同只是多了一项本地文件加载

//iOS 8
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
//iOS 9
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));

2.2 代理回调

3种代理类型
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{
    decisionHandler(WKNavigationResponsePolicyAllow);
}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    decisionHandler(WKNavigationActionPolicyAllow);
}

WKUIDelegate(用原生控件显示网页的方法回调)
主要用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框)

//警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
//确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
//输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;

WKScriptMessageHandler(提供从网页中收消息的回调方法)
OC中接收JS的调用信息

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
 NSLog(@"JS 调用了 %@ 方法,传回参数 %@",message.name, message.body);
}

2.3 Native与JS的相互调用

2.3.1 Native调用JS中的方法

- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
//使用方法  JSFunctionName为JS内方法名称 paramete是参数
[wkWebView evaluateJavaScript:@"JSFunctionName(‘paramete’)" completionHandler:^(id item, NSError * error) {
 
}];

2.3.2 JS调用Native中的方法

WKWebView中JS调用Native方法会在主线程中进行
如要进行JS调用Native需要进行WKWebView的配置 方法如下:
Native中需要添加配置信息,最后把配置好的config添加到列表2.1中。

//创建配置
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//添加消息处理  
WKUserContentController *controller = wkWebView.configuration.userContentController;
[controller addScriptMessageHandler:self name:@"callMe"];

Native向Web中注入自定义的JS代码
在WKUserScript中添加自定义JS代码,然后提交到configuration.userContentController中

//把[configuration.userContentController addUserScript:self.script];添加到列表2.1中
- (WKUserScript *)script {
    if (!_script) {
        NSString *jsString = @"function JSFunctionName(a) {\
           var i = {\
           params: a\
          };\
        return window.webkit.messageHandlers.callMe.postMessage(i);\
        }";
        // 根据JS字符串初始化WKUserScript对象
        WKUserScript *script = [[WKUserScript alloc] initWithSource:jsString
                                                      injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
                                                   forMainFrameOnly:YES];
        _script = script;
    }
    return _script;
}

JS中添加如下方法进行调用

//此方法内的<name>可以写成ScriptMessageHandler中定义的name,<messageBody>为要传的信息内容
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)

3. 问题解决


一 . 在使用WKWebView时如果你在dealloc打个断点,如果发现没有被释放有可能是引起了循环引用问题(发生在addScriptMessageHandler方法中)
解决方法:
1.自定义一个WKScriptMessageHandler 采用weak方法实现代理方法
2.在离开页面时添加removeScriptMessageHandlerForName这个方法进行移除操作

二. WKWebView JS调用OC 获取返回值问题 因为- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message 此方法没有返回值所以不能实现返回值回调。
方法一: 需要借用到OC调用JS方法逻辑重新调用JS中Callback方法来实现回调 参考本文2.3 或者使用

方法二:可以利用runJavaScriptTextInputPanelWithPrompt回调来实现。
首先注入如下代码,主要实现所有调用OC方法都是通过prompt来实现

//把[configuration.userContentController addUserScript:self.script];添加到列表2.1中
- (WKUserScript *)script {
    if (!_script) {
        NSString *jsString = @"function JSFunctionName(a) {\
           var i = {\
           params: a\
          };\
          return prompt('Native_'+i);\
        }";
        // 根据JS字符串初始化WKUserScript对象
        WKUserScript *script = [[WKUserScript alloc] initWithSource:jsString
                                                      injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
                                                   forMainFrameOnly:YES];
        _script = script;
    }
    return _script;
}

其次在OC中截获Prompt并处理后返回数据

-(void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
    //如果是调用原生
    if (prompt && [prompt hasPrefix:@"Native_"]) {
        completionHandler(此处填写处理内容);
    }else{
    //此处填写自定义promot
    }
}```
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容