分享

WKWebView的15条应用指南

 dxw555 2018-06-20

WKWebView是iOS的重要部分,在任何时间地点都能提供高性能网络渲染。


在这篇文章里,我汇总了15条最常用的WKWebView案例,并提供了我验证过的代码解决方案。所以如果你想解决某个具体问题,或想看看WebKit能够做些什么,请往下看!


1.让一个web view充满屏幕


有时候你会看到有人向viewDidLoad()中添加代码,创建一个web view并让它充满整个可用区域。但这样效率很低,用起来很麻烦。


一个简单的方法是在你的视图控制器(view controller)中像这样加入一个属性:

let webView = WKWebView()


之后override loadView()方法,把它分配到你的视图控制器里:

override func loadView() {
   self.view = webView
}


这样一个专用的webView属性很有用,能让人更方便地引用它的属性和方法。


2. 加载远程内容


WKWebView的一个主要用途就是加载远程内容,但需要不止一行代码。你需要用一串字符创建URL,把它放在一个URLRequest中,之后请求web view加载它。

if let url = URL(string: 'https://www.apple.com') {
   let request = URLRequest(url: url)
   webView.load(request)
}


如果你想加载大量URL的话,把这个行为放到一个扩展(extension)里会更简单.

extension WKWebView {
   func load(_ urlString: String) {
       if let url = URL(string: urlString) {
           let request = URLRequest(url: url)
           load(request)
       }
   }
}


现在你可以通过运行webView.load('https://www.apple.com')加载一个网站了。 


3. 加载本地内容


WKWebView可以用loadFileURL()方法加载你app bundle中存储的任何HTML。你需要提供指向一些HTML文件的URL,这些HTML文件需要在你的bundle中,此外还需要提供另一个URL,里面存有你希望web view加载的其他文件。


例如,如果你希望加载help.html,代码如下:

if let url = Bundle.main.url(forResource: 'help', withExtension: 'html') {
   webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
}


其中url.deletingLastPathComponent()告诉了WebKit它可以从包含help.html的目录中加载——这里还可以放图片或者CSS等。


4.读取HTML分段


你可以生成代码形式的HTML,并直接提供给WKWebView。下例展示了一个头信息:

let html = '''
<html>
<body>
<h1>Hello, Swift!</h1>
</body>
</html>
'''


webView.loadHTMLString(html, baseURL: nil)


注意loadHTMLString()的baseURL参数。如果从你的bundle中引用图片或者CSS等,你需要指定Bundle.main.resourceUR,这样才可以读取。像下面这样:

webView.loadHTMLString(html, baseURL: Bundle.main.resourceURL)


5.控制哪些站点可以被访问


WKWebView默认允许访问所有可用站点,但根据你制定的标准锁定站点也很容易。


首先,让一些对象符合WKNavigationDelegate——比如你的视图控制器,或者其他的:

class ViewController: UIViewController, WKNavigationDelegate {


之后,让它不符合你web view的navigationDelegate。以视图控制器为例,代码如下

webView.navigationDelegate = self


最后,执行decidePolicyFor方法,添加你想要的逻辑来判定网页是否允许被加载。下例中,允许用户访问苹果首页,而不能访问其他页面。

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
   if let host = navigationAction.request.url?.host {
       if host == 'www.apple.com' {
           decisionHandler(.allow)
           return
       }
   }
   
   decisionHandler(.cancel)
}


6.用外部浏览器打开一个链接


经常会遇到需要外部处理一些app中链接的情况,通过WKNavigationDelegate协议,这一步并不需要太多工作了。


首先让一个对象符合这个协议——比如你的视图控制器,或者其他的:

class ViewController: UIViewController, WKNavigationDelegate {


之后把这个对象设置成你web view的navigation delegate。如果你是在用视图控制器,代码则如下

webView.navigationDelegate = self


最后,根据自动判定从内部/外部加载网页的逻辑,执行decidePolicyFor方法。对于内部加载,确保你通过.cancel终止了decisionHandler(),这样就停止加载。而对于外部加载,则调用UIApplication.shared.open()从外部浏览器打开URL。

下例中,对于苹果主页以外的链接,都会从web view中打开。

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
   if let url = navigationAction.request.url {
       if url.host == 'www.apple.com' {
           UIApplication.shared.open(url)
           decisionHandler(.cancel)
           return
       }
   }
   
   decisionHandler(.allow)
}


7.监控页面加载


加载一个网页意味着获取一些HTML,下载它用到的JavaScript, CSS和图片以及不可避免的获取一些完整远程代码。


为了让用户了解这些,监控页面加载,更新一些用户界面,这样用户就能知道发生了什么。这些可以通过监控estimatedProgress属性做到:

webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)


现在应该执行observeValue(forKeyPath:)方法,它会传递一串字符,说出有什么改变了。如果它被设定为“estimatedProgress”,我们就可以通过web view的最新estimatedProgress属性做很多事情:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
   if keyPath == 'estimatedProgress' {
       print(Float(webView.estimatedProgress))
   }
}


8.当发生改变时读取网页标题


你可以使用webView.title读取当前页面标题,但是因为随着用户浏览,页面标题可能改变,那么当标题改变时最好可以获得通知。


要做到这个,首先注册获取标题改变的通知

webView.addObserver(self, forKeyPath: #keyPath(WKWebView.title), options: .new, context: nil)


现在执行observeValue(forKeyPath:)方法,它会通过字符串告诉你什么改变了,如果改变的是标题,我们就可以做下一步了。


下面的代码仅当标题变化时才会打印

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
   if keyPath == 'title' {
       if let title = webView.title {
           print(title)
       }
   }
}


9.读取用户访问过的页面


如果你想建立一个有用的浏览记录,你很可能需要读取用户访问过的页面列表。这些在就用到了web view的backForwardList属性,它包含了backList和forwardList数组。


在每一个数组中,你可以读取每个被浏览页面的URL,以及被使用的标题。例如,你可以使用下面的循环打出用户访问过的站点列表。

for page in webView.backForwardList.backList {
   print('User visited (page.url.absoluteString)')
}


10.向页面注入JavaScript


在你的web view加载一些内容后,你可以使用evaluateJavaScript()方法在已渲染的页面中执行JavaScript。你只需要提供一些用于执行的JavaScript就可以了——例如读取一些数值,并当执行结束时关闭。


举个例子,如果你有一个包含<div id='username'>@twostraws</div>的页面,并且想读取“@twostraws”部分,则这么做:

webView.evaluateJavaScript('document.getElementById('username').innerText') { (result, error) in
   if let result = result {
       print(result)
   }
}


11.读取和删除cookies


你可以使用web view的httpCookieStore属性,读取一个网站相关的完整cookies列表。它被埋藏在configuration.websiteDataStore属性下,但是只要你找到它,就可以用getAllCookies()来获取cookies列表,或用delete()来删除某个cookie。


下面例子中,代码会循环遍历所有cookies,并且当它找到名为“authentication”的cookie时就删除它,并把所有其他cookies打印出来:

webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
   for cookie in cookies {
       if cookie.name == 'authentication' {
           self.webView.configuration.websiteDataStore.httpCookieStore.delete(cookie)
       } else {
           print('(cookie.name) is set to (cookie.value)')
       }
   }
}


12.提供自定用户代理(user agent)


用户代理让你的服务器鉴别出正在访问页面的是哪种浏览器,常用于开启/限制某些特性是否可用。


如果你正在阅读自己的服务器上的网页,你可以把用户代理调整为你自己的字符串,这样你就可以鉴别你app的用户:

webView.customUserAgent = 'My Awesome App'


注意:当访问其他资源时,你确实能够改变用户代理,但是记住一些网站也许会读取用户代理字符串,如果这和它期望的不同,会感到混乱。


13.展示自定UI


WKWebView在iOS Safari app中有点像拥有自己独有标签,这意味着用户不能开启关闭新窗口来浏览多页面,它甚至不会显示JavaScript触发的警告或确认请求。


好在你可以通过WKUIDelegate协议改变这些:把一个对象设置为你web view的UI delegate,你就可以显示自定警告,管理自己的标签等等。


首先让一些对象,比如你的ViewController符合它:

class ViewController: UIViewController, WKUIDelegate {


之后根据web view的uiDelegate属性配置视图控制器

webView.uiDelegate = self


最后可选WKUIDelegate的任意个方法执行。比如当任意网页使用alert() JavaScript函数时,让WKWebView显示一个自定警告控制器: 

func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
   let ac = UIAlertController(title: 'Hey, listen!', message: message, preferredStyle: .alert)
   ac.addAction(UIAlertAction(title: 'OK', style: .default, handler: nil))
   present(ac, animated: true)
   completionHandler()
}


此外还有runJavaScriptConfirmPanelWithMessage用于展示确认或拒绝UI,runJavaScriptTextInputPanelWithPrompt用于请求用户输入文本,等等


注意:当你完成后,必须调用完成处理器(completion handler)。JavaScript在警告都完成之前无法继续执行,所以你需要让WebKit知道你什么时候做完了。


14.部分页面快照


你可以用常规的drawHierarchy()方法把页面转化为图片,此外还可以用WebKit的takeSnapshot()方法。它可以让你剪裁或调整图像大小。


下例会产生一个web view 左上部的150X50图片

let config = WKSnapshotConfiguration()
config.rect = CGRect(x: 0, y: 0, width: 150, height: 50)

webView.takeSnapshot(with: config) { image, error in
   if let image = image {
       print(image.size)
   }
}


如果你想要完整图像,仅需要用nil替换掉config


15.检测数据


Web views有内建的数据监测器,意味着它们可以把类似电话号码,日历事件,航班号等放到可输入的链接(tappable link)中。


默认这些都被禁用了,会按照设计渲染网页。但是可以不遵照它,使用自定的WKWebViewConfiguration对象创建你的web view就行了。


下例命令web view去检测所有可能的数据类型:

let config = WKWebViewConfiguration()
config.dataDetectorTypes = [.all]
let webView = WKWebView(frame: .zero, configuration: config)


相关推荐:


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多