58

WKWebView的15条应用指南

 6 years ago
source link: http://www.10tiao.com/html/216/201806/2652561589/2.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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)


相关推荐:



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK