首先看看这篇文章,写得很好:http://nshipster.cn/wkwebkit/

再推荐去看看 iOS_8_by_Tutorials 这本书里的 WKWebView相关章节!

我这里说下自己的简单体会:

1.对比UIWebView ,网上说WKWebView的效率要高,到底高多少,不清楚。

2.WKWebView将javascript的注入,以及javascript传回数据的方法标准化了。在UIWebView时代,执行javascript没什么问题,但是从javascript传回数据就麻烦得多,大多是通过拼写url,调用shouldStartLoadWithRequest方法时传入json数据,写起来十分不规范。也有一些第三方库实现的不错,但毕竟不是原生的。使用WKWebView就可以通过在js中调用webkit.messageHandlers 发送数据到在oc中的代理函数。详见 iOS_8_by_Tutorials。

在swift中插入函数接口的方法:

class NotificationScriptMessageHandler: NSObject, WKScriptMessageHandler {
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage!) {
println(message.body)
}
} let userContentController = WKUserContentController()
let handler = NotificationScriptMessageHandler()
userContentController.addScriptMessageHandler(handler, name: "handlerName")

下面是js中的方法,注意,可以直接传入js数组,会自动转化为swift可识别的数组!这一点非常好,不需要使用json自己转换了。

function getRelatedArticles() {
var related = [];
var elements = document.getElementById("related").getElementsByTagName("a");
for (i = ; i < elements.length; i++) {
var a = elements[i];
related.push({href: a.href, title: a.title});
} window.webkit.messageHandlers.handlerName.postMessage({articles: related});
}

3.WKWebView可以监听到载入进度了。

4.用新的代理函数

func webView(webView: WKWebView!, decidePolicyForNavigationAction navigationAction: WKNavigationAction!, decisionHandler: ((WKNavigationActionPolicy) -> Void)!)

替代了 UIWebView使用的

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

去决定一个url请求是否应该被执行。

5.ios9用WKWebView读取本地文件时,需要用到一个特殊函数,不然没有权限

- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL NS_AVAILABLE(10_11, 9_0);

这个函数是ios9才有的,如果你要支持ios8,请参考以下链接 http://stackoverflow.com/questions/24882834/wkwebview-not-loading-local-files-under-ios-8/28676439#28676439 简单地说就是创建了一个特殊目录。

6.如果使用WKWebView读取本地文件,就涉及到一个获取本地文件url的问题,比如有一个文件,它在main bundle中,路径是

/Users/Rufus/Library/Developer/CoreSimulator/Devices/15F63516-5F19-4CE9-B709-AC7FC0F9E660/data/Containers/Bundle/Application/C5EDBEDF-54B1-4B7C-9EE5-216337584D2D/HtmlWrapper.app/1.png

为了显示它,我们可以需要创建一个request,而创建request需要url,那么用下面方法中的哪一个方法创建url呢?

 let fileUrl = URL(fileURLWithPath: resourcePath)
let normalUrl = URL(string: resourcePath)

答案是,第一个 let fileUrl = URL(fileURLWithPath: resourcePath)。

如果你使用了let normalUrl = URL(string: resourcePath) 生成一个url,并利用这个url生成request,再传递给webview,webview是读不出任何东西的。

这是因为fileURLWithPath这个方法生成的是一个

file:///Users/Rufus/Library/Developer/CoreSimulator/Devices/15F63516-5F19-4CE9-B709-AC7FC0F9E660/data/Containers/Bundle/Application/C5EDBEDF-54B1-4B7C-9EE5-216337584D2D/HtmlWrapper.app/1.png

这种形式的url。这个file,就像http一样,是一种协议,这个协议指的是从本地存储中读取资源,这个协议不需要自己架设什么服务器,系统底层就会执行具体操作,返回文件内容。

而如果我们使用URL(string: resourcePath),生成的就是一个

/Users/Rufus/Library/Developer/CoreSimulator/Devices/15F63516-5F19-4CE9-B709-AC7FC0F9E660/data/Containers/Bundle/Application/C5EDBEDF-54B1-4B7C-9EE5-216337584D2D/HtmlWrapper.app/1.png

这样的Url,这个url没有写明协议,那么系统会默认为http协议,并可能补全缺少的主机名,所以WKWebView最终可能使用的是

http://localhost/Users/Rufus/Library/Developer/CoreSimulator/Devices/15F63516-5F19-4CE9-B709-AC7FC0F9E660/data/Containers/Bundle/Application/C5EDBEDF-54B1-4B7C-9EE5-216337584D2D/HtmlWrapper.app/1.png

想用http协议,必须有服务器,然而我们又没有在ios设备上运行http服务器,当然无法通过http协议读取这个所谓路径的任何东西了。

这里需要注意的是:在UIWebView上,是可以使用URL(string: resourcePath)读取一个本地的文件的!应该是UIWebView内部有逻辑,能够自动识别file 协议的url,并按照file协议读取对应文件。但是,这并不是一个严谨的方法,在创建本地文件url时,就该使用 let fileUrl = URL(fileURLWithPath: resourcePath)这种形式。

7.

在用webview请求内容时,经常会遇到重定向问题,看一下重定向的相关知识:

http://blog.csdn.net/bluishglc/article/details/7953614

这里简单地总结下,最常遇到的重定向是服务器返回一个301或者302状态的http response,并将新的地址加到这个response的header中,对应的key是Location。webview收到response后,查看是这个状态,就会自动发起另一个请求,跳转到新地址。

8.对于ios8以上的版本,不应该使用UIWebView了,官方文档已经明确指出:

Important

Starting in iOS 8.0 and OS X 10.10, use WKWebView to add web content to your app. Do not use UIWebView or WebView.

9.对于WKWebView,主要有4个load方法,我们分别看一看

open func load(_ request: URLRequest) -> WKNavigation?
open func loadFileURL(_ URL: URL, allowingReadAccessTo readAccessURL: URL) -> WKNavigation?
open func loadHTMLString(_ string: String, baseURL: URL?) -> WKNavigation?
open func load(_ data: Data, mimeType MIMEType: String, characterEncodingName: String, baseURL: URL) -> WKNavigation?

先看看,open func loadHTMLString(_ string: String, baseURL: URL?) -> WKNavigation?

这个方法可以通过baseURL的解释如下:A URL that is used to resolve relative URLs within the document.

举个例子,比如在html中,存在这样的代码

<script src="/javascripts/browser.min.js"></script>
那么,这个/javascripts/browser.min.js就是相对路径,relative url,单凭这个url是读不到任何资源的,因为它不完整,解析这个html的模块,都会负责把这个url补全,再去加载对应的资源。而wevView就可以利用这里的baseUrl进行补全。

说到baseUrl,再解释一下baseURL。在stackoverflow上有如下答案:
-baseURL is a concept purely of NSURL/CFURL rather than URLs in general
就是说 baseURL并不存在真正的URL定义中,仅仅是cocoa 库的一个写法,再看看URL的定义
Uniform / Universal Resource Locator,常缩写为URL,

统一资源定位符的标准格式如下:

协议类型://服务器地址(必要时需加上端口号)/路径/文件名

这里的协议,除了常用的http,https,还有一些别的,比如file,ftp 类型。我们把桌面上的图片拖入游览器当中,游览器显示了图片,并且在地址栏上显示了file:///Users/Rufus/Desktop/1.png,这个就是URL的一种,file协议省略了服务器名,所以出现了三个/,其实是  file://指的是协议,后面指的是路径。
可以看到,根本没有什么baseUrl的说明,这个baseUrl,就是ios提供的便利方法,方便把html中常出现的相对路径(relative url),转化为一个完整的url(absolute url)。
具体的作用和使用方法需要根据每个api而定。

再看看这个open func loadFileURL(_ URL: URL, allowingReadAccessTo readAccessURL: URL) -> WKNavigation?
读起来好像可以给出访问资源的权限?但是什么地方的文件会有权限不让访问呢?我在ios10上做了以下测试:
       print("home is",NSHomeDirectory())
// Do any additional setup after loading the view, typically from a nib. let wkWebView = WKWebView(frame:CGRect(x: , y: , width: , height: ))
self.view.addSubview(wkWebView) let path = Bundle.main.path(forResource:"", ofType: "png") let documentsPath = NSHomeDirectory()+"/Documents/1.png"
let tmpPath = NSTemporaryDirectory()+"/1.png"
let cachePath = NSHomeDirectory()+"/Library/Caches/1.png" let fileManager = FileManager.default; do{
try fileManager.copyItem(atPath: path!, toPath: cachePath)
try fileManager.copyItem(atPath: path!, toPath: documentsPath)
try fileManager.copyItem(atPath: path!, toPath: tmpPath) }catch{
print(error)
} wkWebView.load(URLRequest(url:URL(fileURLWithPath: path!))) //wkWebView.load(URLRequest(url:URL(fileURLWithPath: documentsPath)))
//wkWebView.load(URLRequest(url:URL(fileURLWithPath: tmpPath)))
//wkWebView.load(URLRequest(url:URL(fileURLWithPath: cachePath)))
常用的4个路径都测试了,都可以正确地读取数据。是不是由于这个文件是本地拷贝的所以才行呢?我决定再实验一个从远程下载的文件。
 let urlSession = URLSession(configuration: URLSessionConfiguration.default)
urlSession.downloadTask(with:URL(string:"https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png")!) { (url:URL?, response:URLResponse?, error:Error?) in
print("url is ",url); let dstPath = NSHomeDirectory()+"/Documents/2.png" let dstPath2 = NSHomeDirectory()+"/Library/Caches/2.png" let dstUrl = URL(fileURLWithPath: dstPath2) do{
try fileManager.moveItem(at: url!, to: dstUrl) }catch{
print(error)
} wkWebView.load(URLRequest(url:dstUrl)) }.resume()
结果是,也都可以正常读取资源。
那么这个 allowingReadAccessTo 方法到底在什么条件下使用呢? 测试的html文件内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
aaaaa
<img src="./1.png"> </body>
</html>

这个html放在documents下面,1.png也是放在documents下面。


将这个html分别放在实验1中的4个位置,读取资源,这个资源的位置也需要分别放在4个位置,其实就是16种情况。

我做了以下测试:

    let documentDirPath = NSHomeDirectory()+"/Documents"

let documentUrl = URL(fileURLWithPath: documentDirPath)

  

生成Url时的baseUrl 和 webview load 时的baseUrl 没什么联系,后者才能把Html内容中相对路径补全为绝对路径。

let fileUrl = URL(fileURLWithPath: "3.html", relativeTo: documentUrl)

let fileUrl2 = URL(fileURLWithPath:(documentDirPath+"/3.html"))

//wkWebView.loadFileURL(fileUrl, allowingReadAccessTo:documentUrl) //wrong

//wkWebView.loadFileURL(fileUrl2, allowingReadAccessTo:Bundle.main.resourceURL!) //wrong

//wkWebView.loadFileURL(fileUrl2, allowingReadAccessTo:documentUrl)

wkWebView.load(URLRequest(url:fileUrl)) //simulator :correct   device:wrong

先看一下2个url的不同之处,

(lldb) po fileUrl.baseURL
▿ Optional<URL>
▿ some : file:///Users/Rufus/Library/Developer/CoreSimulator/Devices/C83D85F0-82E8-4608-B643-890A75362FEB/data/Containers/Data/Application/E56AF850-3CD2-4D76-88F3-8F37BA74A054/Documents/ (lldb) po fileUrl2.baseURL
nil (lldb) po fileUrl.relativePath
"3.html" (lldb) po fileUrl2.relativePath
"/Users/Rufus/Library/Developer/CoreSimulator/Devices/C83D85F0-82E8-4608-B643-890A75362FEB/data/Containers/Data/Application/E56AF850-3CD2-4D76-88F3-8F37BA74A054/Documents/3.html" (lldb) po fileUrl.absoluteString
"file:///Users/Rufus/Library/Developer/CoreSimulator/Devices/C83D85F0-82E8-4608-B643-890A75362FEB/data/Containers/Data/Application/E56AF850-3CD2-4D76-88F3-8F37BA74A054/Documents/3.html" (lldb) po fileUrl2.absoluteString
"file:///Users/Rufus/Library/Developer/CoreSimulator/Devices/C83D85F0-82E8-4608-B643-890A75362FEB/data/Containers/Data/Application/E56AF850-3CD2-4D76-88F3-8F37BA74A054/Documents/3.html"

注意到,这2个url虽然 absoluteString 值完全一样,但是其他的2个值完全不同。其实,对于RFC来说,URL指的就是absoluteString,另外的2个值,都是为了ios方便使用而设计的。

最后的这4种载入,前2个是错误的,后两个是正确的,我们看看那2个错的有什么问题。

  //wkWebView.loadFileURL(fileUrl, allowingReadAccessTo:documentUrl) //wrong

  //wkWebView.loadFileURL(fileUrl2, allowingReadAccessTo:documentUrl) //correct

显然,wkWebView.loadFileURL对于fileUrl是无法读取的,但是却能读取 fileUrl2这种最普通方法创建的Url!

再看这个

  //wkWebView.loadFileURL(fileUrl, allowingReadAccessTo:documentUrl) //wrong

  wkWebView.load(URLRequest(url:fileUrl)) //simulator:correct device:wrong
 wkWebView.load 方法却是可以使用fileUrl的!但是真机由于访问权限问题,load方法读取不到任何本地资源,想读取本地资源,就要使用loadFileURL方法

这样看起来,平常最好还是使用最基本的字符串创建Url! 再看下面的对比
//wkWebView.loadFileURL(fileUrl2, allowingReadAccessTo:Bundle.main.resourceURL!) //wrong
//wkWebView.loadFileURL(fileUrl2, allowingReadAccessTo:documentUrl) //correct

这组对比,就能体现allowingReadAccessTo的作用了。第一个失败了,就是因为在这次加载过程中,仅仅赋予了Bundle.main.resourceURL中的权限,那么放在documents目录下的html自然无法被webview载入了!

 @discussion If readAccessURL references a single file, only that file may be loaded by WebKit.
If readAccessURL references a directory, files inside that file may be loaded by WebKit.

我觉得这是一个为了安全而设计的方法:用这个方法加载一个不确定是否安全的url,就可以指定这次加载的访问范围,防止这个url访问到别的资源。当怀疑一个url的安全性时,就应该使用这种加载方法!

需要特殊说明的是,这个allowingReadAccessTo参数在模拟器上不能限制html内部资源的访问范围,但是在真机上是可以的!比如:

allowingReadAccessTo设置到/Documents/Sub/范围时,上层目录中的图片也可以被顺利读取。

但是如果实在真机上,就不能读取了!必须获得Documents的权限才行,因为1.png是直接放在documents下的。


iOS 8 WKWebView 知识点的更多相关文章

  1. ios的一些知识点

    ios的一些知识点 一 非ARC的内存管理情况 1-autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段落,开 ...

  2. (转载)ios的一些知识点

    ios的一些知识点 一 非ARC的内存管理情况  1-autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一 段落 ...

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

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

  4. iOS开发零碎知识点

    记录一些常用和不常用的iOS知识点,防止遗忘丢失.(来源为收集自己项目中用到的或者整理看到博客中的知识点),如有错误,欢迎大家批评指正:如有好的知识点,也欢迎大家联系我,添加上去.谢谢! 一.调用代码 ...

  5. iOS 8 WKWebView

    首先看看这篇文章,写得很好:http://nshipster.cn/wkwebkit/ 再推荐去看看 iOS_8_by_Tutorials 这本书里的 WKWebView相关章节! 我这里说下自己的简 ...

  6. iOS学习——iOS开发小知识点集合

    在iOS学习和开发过程中,经常会遇到一些很小的知识点和问题,一两句话就可以解释清楚了,这样的知识点写一篇随笔又没有必要,但是又想mark一下,以备不时之需,所以就有了本文.后面遇到一些小的知识点会不断 ...

  7. C语言 ---- 指针 iOS学习-----细碎知识点总结

    内存的访问形式:1.直接访问:通过变量名进行访问.2.间接访问:先找到变量存放的地址,然后根据地址去访问对应的内存空间. 指针--- // 定义一个整形指针变量,用来存储num1在内存中的地址    ...

  8. C语言 ---- 数组 iOS学习-----细碎知识点总结

    #pragma mark - 数组:用来存放同一数据类型的数据 // 数组的定义:类型说明符 数组名[常量表达式] = {值1, 值2, 值3...};    // 定义一个float类型的数组,用来 ...

  9. iOS项目开发知识点

    前言部分 注:本文并非绝对原创 大部分内容摘自 http://blog.csdn.net/hengshujiyi/article/details/20943045 文中有些方法可能已过时并不适用于现在 ...

随机推荐

  1. logging模块(二十六)

    用于便捷记录日志且线程安全的模块 可在logging.basicConfig()函数中通过具体参数来更改logging模块默认行为,可用参数有 filename:用指定的文件名创建FiledHandl ...

  2. es6/es7/es8常用新特性总结(超实用)

    本文标题有误导性,因为我其实想写node8的新特性,说实话一下子从node v1.x跳跃到node 8.x+ 真有点受宠若惊的感觉.一直觉得node 数组. 对象.序列等的处理没有python方便,因 ...

  3. 九、java容器

    目录 一.容器的概念 二.Cpllection接口 三.Iterator接口 四.增强的for循环 五.Set接口 六.List接口和Comparable接口 八.Map接口 九.自动打包/解包 十. ...

  4. 搭建Telnet服务器

    搭建Telnet服务器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 可能大家都知道现在已经很少有人用TELNET服务器, 因为它传输数据是以明文的方式,我们很容易通过抓包软件讲数 ...

  5. 酷炫的SVG 动态图标

                                                      在  loading.io 上能看到好多效果惊艳的loading图标.它们都是用svg写成的,寥寥几 ...

  6. ATS 自定义日志格式

    字段解释 %<chi> 客户端IP %<caun> The username of the authenticated client. A hyphen (-) means t ...

  7. LINQ to SQL 实现 CASE WHEN THEN 语句

    Ø  前言 没有什么特别的,只是觉得 LINQ 的功能其实还是蛮强大的,所以简单记录下,算是工作笔记吧,有可能还能帮助到其他同学呢^_^. Ø  下面主要使用了 C# 三元运算符实现实现 SQL 中的 ...

  8. ICS Hack Tools

    参考链接:http://icstraining.org/en/security-tools/configurations ICS-Security-Tool: https://github.com/I ...

  9. c#中如何在一个panel中放入窗体

    Form2 f2 = new Form2(); //实例化窗体FORM2 f2.TopLevel = false; //设置为非顶级窗体 f2.FormBorderStyle = FormBorder ...

  10. android 服务解析

    https://blog.csdn.net/luoyanglizi/article/details/51586437 2.service和Thread的区别 定义上: thread是程序运行的最小单元 ...