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. android 框架层 常用类介绍

    名称 功能描述 示意图 activitymanager 管理应用程序的周期并提供常用的回退功能 window manager 窗口管理者 content provider 用于访问另一个的数据,或者共 ...

  2. 基于IE的多标签的浏览器-世界之窗2.4

    经常在Win32服务器上打开网页,但可能只是打开一个网页或是下载一点东西而已,虽然默认的IE浏览器基本上能满足需求 但还是存在不少的不便,比如如果要打开多个网页的话则需要再打开一个IE,还有就是习惯了 ...

  3. w3cscholl的在线代码编辑工具

    https://www.w3cschool.cn/tryrun/runcode?lang=c

  4. 使用CSS3生成的电子时钟特效

    在线演示 本地下载 突然觉得自己对带工作的态度亟需改正,虽然不喜欢现在的加班生活,但是自己要去接受自己不喜欢的,才能获得自己喜欢的. 这是自己好久之前丛过的一个时钟,网上应该有这个的教程,虽然实现的效 ...

  5. Android 多语言支持

    本文内容 字符串本地化原理 环境 创建项目 测试其他语言 Android 本地化语言 ISO 编码 参考资料 使用 Android 的人越来越多,每天都在增加.因此,当你想把你的应用成功地全球化时,通 ...

  6. Android 模仿QQ风格的 UI

    本文内容 环境 演示模仿QQ风格的界面 本文主要演示的是 UI,如何模仿 QQ 风格的界面.虽然这个 UI 跟现在的QQ空间有点差别,但是也能学到很多东西. 下载 Demo 环境 Windows 7 ...

  7. CAD2006您没有足够的权限来安装本产品

    在Win10的环境下安装CAD2006,可能会报错"您没有足够的权限来安装本产品". 解决方法是,右键以"兼容性疑难解答"运行 在弹出的对话框中,点击 &quo ...

  8. PHP传值与传址(引用)

    传值和传引用的区别在于,如果一个参数比较大,占用大量的内存空间,那么传引用的话就会节省拷贝空间. 传值:是把实参的值赋值给行参 ,那么对行参的修改,不会影响实参的值 传引用 :真正的以地址的方式传递参 ...

  9. VCAP5-DCA Objective 1.3 – Configure and Manage Complex Multipathing and PSA Plug-ins

    http://virtuallyhyper.com/2012/10/vcap5-dca-objective-1-3-configure-and-manage-complex-multipathing- ...

  10. 在CentOS7(虚拟机)下通过源码安装Postgresql10以及基本配置

    操作系统:CentOS7 安装文件:postgresql-10.0.tar.gz 系统环境:gcc.Python 1:源码安装 [postgres@localhost ~]# tar zxvf pos ...