iOS8之后,苹果推出了WebKit这个框架,用来替换原有的UIWebView,新的控件优点多多。由于一直在适配iOS7,就没有去替换,现在仍掉了iOS7,以为很简单的就替换过来了,然而在替换的过程中,却遇到了很多坑。还有一点就是原来写过一篇文章 Objective-C与JavaScript交互的那些事以为年代久远的UIWebView已经作古,可这篇文章现在依然有一定的阅读量。所以在决定在续一篇此文,以引导大家转向WKWebView,并指出自己踩过的坑,让大家少走弯路。

  逻辑图如下所示:

 

使用及注意点

WKWebView只能用代码创建,而且自身就支持了右滑返回手势allowsBackForwardNavigationGestures和加载进度estimatedProgress等一些UIWebView不具备却非常好用的属性。在创建的时候,指定初始化方法中要求传入一个WKWebViewConfiguration对象,一般我们使用默认配置就好,但是有些地方是要根据自己的情况去做更改。比如,配置中的allowsInlineMediaPlayback这个属性,默认为NO,如果不做更改,网页中内嵌的视频就无法正常播放。

更改User-Agent

有时我们需要在User-Agent添加一些额外的信息,这时就要更改默认的User-Agent在使用UIWebView的时候,可用如下代码(在使用UIWebView之前执行)全局更改User-Agent:

// 获取默认User-Agent

UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];

NSString *oldAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];

// 给User-Agent添加额外的信息

NSString *newAgent = [NSString stringWithFormat:@"%@;%@", oldAgent, @"extra_user_agent"];

// 设置global User-Agent

NSDictionary *dictionnary = [[NSDictionary alloc] initWithObjectsAndKeys:newAgent, @"UserAgent", nil];

[[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];

以上代码是全局更改User-Agent,也就是说,App内所有的Web请求的User-Agent都被修改。替换为WKWebView后更改全局User-Agent可以继续使用上面的一段代码,或者改为用WKWebView获取默认的User-Agent,代码如下:

self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectZero];

// 获取默认User-Agent

[self.wkWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {

NSString *oldAgent = result;

// 给User-Agent添加额外的信息

NSString *newAgent = [NSString stringWithFormat:@"%@;%@", oldAgent, @"extra_user_agent"];

// 设置global User-Agent

NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:newAgent, @"UserAgent", nil];

[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];

}];

对比发现,这两种方法并没有本质的区别,一点小区别在于一个是用UIWebView获取的默认User-Agent,一个是用WKWebView获取的默认User-Agent。上面方法的缺点也是很明显的,就是App内所有Web请求的User-Agent全部被修改。

在iOS9,WKWebView提供了一个非常便捷的属性去更改User-Agent,就是customUserAgent属性。这样使用起来不仅方便,也不会全局更改User-Agent,可惜的是iOS9才有,如果适配iOS8,还是要使用上面的方法。

WKWebView的相关的代理方法

WKWebView的相关的代理方法分别在WKNavigationDelegate和WKUIDelegate以及WKScriptMessageHandler这个与JavaScript交互相关的代理方法。

  • WKNavigationDelegate: 此代理方法中除了原有的UIWebView的四个代理方法,还增加了其他的一些方法,具体可参考我下面给出的Demo。

  • WKUIDelegate: 此代理方法在使用中最好实现,否则遇到网页alert的时候,如果此代理方法没有实现,则不会出现弹框提示。

  • WKScriptMessageHandler: 此代理方法就是和JavaScript交互相关,具体介绍参考下面的专门讲解。

WKWebView使用过程中的坑

WKWebView下面添加自定义View

因为我们有个需求是在网页下面在添加一个View,用来展示此链接内容的相关评论。在使用UIWebView的时候,做法非常简单粗暴,在UIWebView的ScrollView后面添加一个自定义View,然后根据View的高度,在改变一下scrollView的contentSize属性。以为WKWebView也可以这样简单粗暴的去搞一下,结果却并不是这样。

首先改变WKWebView的scrollView的contentSize属性,系统会在下一次帧率刷新的时候,再给你改变回原有的,这样这条路就行不通了。我马上想到了另一个办法,改变scrollView的contentInset这个系统倒不会在变化回原来的,自以为完事大吉。后来过了两天,发现有些页面的部分区域的点击事件无法响应,百思不得其解,最后想到可能是设置的contentInset对其有了影响,事实上正是如此。查来查去,最后找到了一个解决办法是,就是当页面加载完成时,在网页下面拼一个空白的div,高度就是你添加的View的高度,让网页多出一个空白区域,自定义的View就添加在这个空白的区域上面。这样就完美解决了此问题。具体可参考Demo所写,核心代码如下:

self.addView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, addViewHeight)];

self.addView.backgroundColor = [UIColor redColor];

[self.webView.scrollView addSubview:self.addView];

NSString *js = [NSString stringWithFormat:@"\

var appendDiv = document.getElementById(\"AppAppendDIV\");\

if (appendDiv) {\

appendDiv.style.height = %@+\"px\";\

} else {\

var appendDiv = document.createElement(\"div\");\

appendDiv.setAttribute(\"id\",\"AppAppendDIV\");\

appendDiv.style.width=%@+\"px\";\

appendDiv.style.height=%@+\"px\";\

document.body.appendChild(appendDiv);\

}\

", @(addViewHeight), @(self.webView.scrollView.contentSize.width), @(addViewHeight)];

[self.webView evaluateJavaScript:js completionHandler:nil];

WKWebView加载HTTPS的链接

HTTPS已经越来越被重视,前面我也写过一系列的HTTPS的相关文章HTTPS从原理到应用(四):iOS中HTTPS实际使用当加载一些HTTPS的页面的时候,如果此网站使用的根证书已经内置到了手机中这些HTTPS的链接可以正常的通过验证并正常加载。但是如果使用的证书(一般为自建证书)的根证书并没有内置到手机中,这时是链接是无法正常加载的,必须要做一个权限认证。开始在UIWebView的时候,是把请求存储下来然后使用NSURLConnection去重新发起请求,然后走NSURLConnection的权限认证通道,认证通过后,在使用UIWebView去加载这个请求。

在WKWebView中,WKNavigationDelegate中提供了一个权限认证的代理方法,这是权限认证更为便捷。代理方法如下:

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {

if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

if ([challenge previousFailureCount] == 0) {

NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

completionHandler(NSURLSessionAuthChallengeUseCredential, credential);

} else {

completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);

}

} else {

completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);

}

}

这个方法比原来UIWebView的认证简单的多。但是使用中却发现了一个很蛋疼的问题,iOS8系统下,自建证书的HTTPS链接,不调用此代理方法。查来查去,原来是一个bug,在iOS9中已经修复,这明显就是不管iOS8的情况了,而且此方法也没有标记在iOS9中使用,这点让我感到有点失望。这样我就又想到了换回原来UIWebView的权限认证方式,但是试来试去,发现也不能使用了。所以关于自建证书的HTTPS链接在iOS8下面使用WKWebView加载,我没有找到很好的办法去解决此问题。这样我不得已有些链接换回了HTTP,或者在iOS8下面在换回UIWebView。如果你有解决办法,也欢迎私信我,感激不尽。

WKWebView和JavaScript交互

WKWebView和JavaScript交互,在WKUserContentController.h这个头文件中- (void)addScriptMessageHandler:(id )scriptMessageHandler name:(NSString *)name;这个方法的注释中已经明确给出了交互办法。使用起来倒是非常的简单。创建WKWebView的时候添加交互对象,并让交互对象实现WKScriptMessageHandler中的唯一的一个代理方法。具体的方式参考Demo中的使用。

// 添加交互对象

[config.userContentController addScriptMessageHandler:(id)self.ocjsHelper name:@"timefor"];

// 代理方法

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

JavaScript调用Objective-C的时候,使用window.webkit.messageHandlers.timefor.postMessage({code: '0001', functionName: 'getdevideId'}); Objective-C自动对交互参数包装成了WKScriptMessage对象,其属性body则为传送过来的参数,name为添加交互对象的时候设置的名字,以此名字可以过滤掉不属于自己的交互方法。其中body可以为NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull。

而Objective-C在回调JavaScript的时候,不能像我原来在 Objective-C与JavaScript交互的那些事这篇文章中写的那样,JavaScript传过来一个匿名函数,Objective-C这边直接调用一下就完事。WKWebView没有办法传过来一个匿名函数,所以回调方式,要么执行一段JavaScript代码,或者就是调用JavaScript那边的一个全局函数。一般是采用后者,至于Web端虽说暴露了一个全局函数,同样可以把这一点代码处理的很优雅。Objective-C传给JavaScript的参数,可以为Number, String, and Object。参考如下:

// 数字

NSString *js = [NSString stringWithFormat:@"globalCallback(%@)", number];

[self.webView evaluateJavaScript:js completionHandler:nil];

// 字符串

NSString *js = [NSString stringWithFormat:@"globalCallback(\'%@\')", string];

[self.webView evaluateJavaScript:js completionHandler:nil];

// 对象

NSString *js = [NSString stringWithFormat:@"globalCallback(%@)", @{@"name" : @"timefor"}];

[self.webView evaluateJavaScript:js completionHandler:nil];

// 带返回值的JS函数

[self.webView evaluateJavaScript:@"globalCallback()" completionHandler:^(id result, NSError * _Nullable error) {

// 接受返回的参数,result中

}];

总结

此文主要介绍了WKWebView使用中的注意点,一般也都是常用的,还有缓存等一些不是太常用的就没有具体介绍。如果在其他方面遇到问题,也欢迎你私信我共同探讨进步。

此文的Demo地址:https://github.com/SSiming/WKWebViewDemo 如果此文对你有所帮助,请给个star吧。

参考

  • http://stackoverflow.com/questions/34693311/links-in-wkwebview-randomly-not-clickable/35100064#35100064

  • https://bugs.webkit.org/show_bug.cgi?id=140197

WKWebView 使用及注意事项的更多相关文章

  1. 利用WKWebView实现js与OC交互注意事项

    最近在写一些关于wkwebview的一些代码,发现了几点心得,记录一下. 1.js调用OC 我是利用wkwebview进行的开发实现,主要代码有三部分 1.向config注入OC对象 [config. ...

  2. jQuery UI resizable使用注意事项、实时等比例拉伸及你不知道的技巧

    这篇文章总结的是我在使用resizable插件的过程中,遇到的问题及变通应用的奇思妙想. 一.resizable使用注意事项 以下是我在jsfiddle上写的测试demo:http://jsfiddl ...

  3. Windows Server 2012 NIC Teaming介绍及注意事项

    Windows Server 2012 NIC Teaming介绍及注意事项 转载自:http://www.it165.net/os/html/201303/4799.html Windows Ser ...

  4. TODO:Golang指针使用注意事项

    TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...

  5. app开发外包注意事项,2017最新资讯

    我们见过很多创业者,栽在这app外包上.很多创业者对于app外包这件事情不是特别重视,以为将事情交给app外包公司就完事了,实际上不是的.无论是从选择app外包公司还是签订合同.售后维护等各方面都有许 ...

  6. favicon.ioc使用以及注意事项

    1.效果 2.使用引入方法 2.1 注意事项:(把图标命名为favicon.ico,并且放在根目录下,同时使用Link标签,多重保险) 浏览器默认使用根目录下的favicon.ico 图标(如果你并没 ...

  7. ORACLE分区表梳理系列(二)- 分区表日常维护及注意事项(红字需要留意)

    版权声明:本文发布于http://www.cnblogs.com/yumiko/,版权由Yumiko_sunny所有,欢迎转载.转载时,请在文章明显位置注明原文链接.若在未经作者同意的情况下,将本文内 ...

  8. 【原】Masonry+UIScrollView的使用注意事项

    [原]Masonry+UIScrollView的使用注意事项 本文转载请注明出处 —— polobymulberry-博客园 1.问题描述 我想实现的使用在一个UIScrollView依次添加三个UI ...

  9. 《连载 | 物联网框架ServerSuperIO教程》- 5.轮询通讯模式开发及注意事项。附:网友制作的类库说明(CHM)

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

随机推荐

  1. MFC剪贴板通信

    1.建立一个基于对话框的应用程序,界面如下: 2.对两个按钮进行消息响应: void CChipBoardOperateDlg::OnBnClickedBtnCopycb() { // TODO: 在 ...

  2. PHPExcel-设置表格字体颜色背景样式、数据格式、对齐方式、添加图片、批注、文字块、合并拆分单元格、单元格密码保护

    首先到phpexcel官网上下载最新的phpexcel类,下周解压缩一个classes文件夹,里面包含了PHPExcel.php和PHPExcel的文件夹,这个类文件和文件夹是我们需要的,把class ...

  3. easyui权限系统改造备忘

    修改list.js文件 //// 如果所有操作按钮隐藏,则list-toolbar隐藏 //if ($(".list-toolbar").length > 0) { // v ...

  4. OpenNebula学习第四节之磁盘镜像的制作

    一.准备工作 需要准备一个Ubuntu16.04的ISO文件上传至服务器,如下图所示 二.操作步骤 2.1:创建操作目录 创建一个/var/tmp/cloud_image目录,把Ubuntu16.04 ...

  5. javascript中addEventListener(attachEvent)具体解释

    addEventListener 有三个參数:第一个參数表示事件名称(不含 on,如 "click").第二个參数表示要接收事件处理的函数:第三个參数为 useCapture.样例 ...

  6. curl_setopt — 设置 cURL 传输选项

    curl_setopt (PHP 4 >= 4.0.2, PHP 5, PHP 7) curl_setopt — 设置 cURL 传输选项 bool curl_setopt ( resource ...

  7. 基于PHP构建OAuth 2.0 服务端 认证平台

    OAuth2.0 认证服务 安装 你可以在github上下载OAuth Server PHP,也可以用下列命令下载,不过内容都是一样的 mkdir my-oauth2-walkthrough cd m ...

  8. 使用FractionSlider生成的视差幻灯效果原型

    在线演示 本地下载 非常棒的jQuery插件,帮助你创建超酷的视差效果幻灯!

  9. powerdesigner 不显示表字段只显示表名

    在空白的地方右键选择 Display Preferences然后在左边的General Settings里选Table然后把Columns 的All Columns勾上 如果能帮上您,请选为满意答案, ...

  10. ubuntu12.04 lts 安装gcc 4.8

    gcc 4.8.1 是第一个完全支持C++11 的编译器,Windows上可以安装mingw版的,在sourceforge 上有下载,安装也比较方便.在Linux上安装的话需要首先安装一些依赖库.在U ...