日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

WKWebView特性及使用

 UchihaItachiF 2015-12-16

引言

一直聽(tīng)說(shuō)WKWebViewUIWebView強(qiáng)大許多,可是一直沒(méi)有使用到,今天花了點(diǎn)時(shí)間看寫(xiě)了個(gè)例子,對(duì)其API的使用有所了解,為了日后能少走彎路,也為了讓大家更容易學(xué)習(xí)上手,這里寫(xiě)下這篇文章來(lái)記錄如何使用以及需要注意的地方。

溫馨提示:本人在學(xué)習(xí)使用過(guò)程中,確實(shí)有此體會(huì),WKWebView的確比UIWebView強(qiáng)大很多,與JS交互的能力顯示增強(qiáng),在加載速度上有所提升。

WKWebView新特性

  • 性能、穩(wěn)定性、功能大幅度提升
  • 允許JavaScript的Nitro庫(kù)加載并使用(UIWebView中限制)
  • 支持了更多的HTML5特性
  • 高達(dá)60fps的滾動(dòng)刷新率以及內(nèi)置手勢(shì)
  • GPU硬件加速
  • KVO
  • 重構(gòu)UIWebView成14類(lèi)與3個(gè)協(xié)議,查看官方文檔

準(zhǔn)備工作

首先,我們?cè)谑褂玫牡胤揭肽K:

import Webkit

在學(xué)習(xí)之前,建議大家先點(diǎn)擊WKWebView進(jìn)去閱讀里面的相關(guān)API,讀完一遍,有個(gè)大概的印象,學(xué)習(xí)起來(lái)就很快了。

其初始化方法有:

public init()
public init(frame: CGRect)
public init(frame: CGRect, configuration: WKWebViewConfiguration)

加載HTML的方式與UIWebView的方式大致相同。其中,loadFileURL方法通常用于加載服務(wù)器的HTML頁(yè)面或者JS,而loadHTMLString方法通常用于加載本地HTML或者JS:

public func loadRequest(request: NSURLRequest) -> WKNavigation?

// 9.0以后才支持   
@available(iOS 9.0, *)
public func loadFileURL(URL: NSURL, allowingReadAccessToURL readAccessURL: NSURL) -> WKNavigation?

// 通常用于加載本地HTML或者JS
public func loadHTMLString(string: String, baseURL: NSURL?) -> WKNavigation?

// 9.0以后才支持
@available(iOS 9.0, *)
public func loadData(data: NSData, MIMEType: String, characterEncodingName: String, baseURL: NSURL) -> WKNavigation

與之交互用到的三大代理:

  • WKNavigationDelegate,與頁(yè)面導(dǎo)航加載相關(guān)
  • WKUIDelegate,與JS交互時(shí)的ui展示相關(guān),比較JS的alert、confirm、prompt
  • WKScriptMessageHandler,與js交互相關(guān),通常是ios端注入名稱(chēng),js端通過(guò)window.webkit.messageHandlers.{NAME}.postMessage()來(lái)發(fā)消息到ios端

創(chuàng)建WKWebView

首先,我們?cè)?code style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); border-radius: 3px; display: inline; background-color: rgb(248, 248, 248);">ViewController中先遵守協(xié)議:

class ViewController: UIViewController, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate

我們可以先創(chuàng)建一個(gè)WKWebView的配置項(xiàng)WKWebViewConfiguration,這可以設(shè)置偏好設(shè)置,與網(wǎng)頁(yè)交互的配置,注入對(duì)象,注入js等:

// 創(chuàng)建一個(gè)webiview的配置項(xiàng)
let configuretion = WKWebViewConfiguration()

// Webview的偏好設(shè)置
configuretion.preferences = WKPreferences()
configuretion.preferences.minimumFontSize = 10
configuretion.preferences.javaScriptEnabled = true
// 默認(rèn)是不能通過(guò)JS自動(dòng)打開(kāi)窗口的,必須通過(guò)用戶(hù)交互才能打開(kāi)
configuretion.preferences.javaScriptCanOpenWindowsAutomatically = false

// 通過(guò)js與webview內(nèi)容交互配置
configuretion.userContentController = WKUserContentController()

// 添加一個(gè)JS到HTML中,這樣就可以直接在JS中調(diào)用我們添加的JS方法
let script = WKUserScript(source: "function showAlert() { alert('在載入webview時(shí)通過(guò)Swift注入的JS方法'); }",
  injectionTime: .AtDocumentStart,// 在載入時(shí)就添加JS
  forMainFrameOnly: true) // 只添加到mainFrame中
configuretion.userContentController.addUserScript(script)

// 添加一個(gè)名稱(chēng),就可以在JS通過(guò)這個(gè)名稱(chēng)發(fā)送消息:
// window.webkit.messageHandlers.AppModel.postMessage({body: 'xxx'})
configuretion.userContentController.addScriptMessageHandler(self, name: "AppModel")

創(chuàng)建對(duì)象并遵守代理:

self.webView = WKWebView(frame: self.view.bounds, configuration: configuretion)

self.webView.navigationDelegate = self
self.webView.UIDelegate = self

加載我們的本地HTML頁(yè)面:

let url = NSBundle.mainBundle().URLForResource("test", withExtension: "html")
self.webView.loadRequest(NSURLRequest(URL: url!))
self.view.addSubview(self.webView);

我們?cè)偬砑忧斑M(jìn)、后退按鈕和添加一個(gè)加載進(jìn)度的控制顯示在Webview上:

self.progressView = UIProgressView(progressViewStyle: .Default)
self.progressView.frame.size.width = self.view.frame.size.width
self.progressView.backgroundColor = UIColor.redColor()
self.view.addSubview(self.progressView)

self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "上一個(gè)頁(yè)面", style: .Done, target: self, action: "previousPage")
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "下一個(gè)頁(yè)面", style: .Done, target: self, action: "nextPage")

頁(yè)面前進(jìn)、后退

對(duì)于前進(jìn)后退的事件處理就很簡(jiǎn)單的,要注意判斷一下是否可以后退、前進(jìn)才調(diào)用:

func previousPage() {
    if self.webView.canGoBack {
      self.webView.goBack()
    }
}

func nextPage() {
    if self.webView.canGoForward {
      self.webView.goForward()
    }
}

當(dāng)然除了這些方法之處,還有重新載入等。

WKWebView的KVO

對(duì)于WKWebView,有三個(gè)屬性支持KVO,因此我們可以監(jiān)聽(tīng)其值的變化,分別是:loading,title,estimatedProgress,對(duì)應(yīng)功能表示為:是否正在加載中,頁(yè)面的標(biāo)題,頁(yè)面內(nèi)容加載的進(jìn)度(值為0.0~1.0)

// 監(jiān)聽(tīng)支持KVO的屬性
self.webView.addObserver(self, forKeyPath: "loading", options: .New, context: nil)
self.webView.addObserver(self, forKeyPath: "title", options: .New, context: nil)
self.webView.addObserver(self, forKeyPath: "estimatedProgress", options: .New, context: nil)

然后就可以重寫(xiě)監(jiān)聽(tīng)的方法來(lái)處理。這里只是取頁(yè)面的標(biāo)題,更新加載的進(jìn)度條,在加載完成時(shí),手動(dòng)調(diào)用執(zhí)行一個(gè)JS方法:

// MARK: - KVO
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
  if keyPath == "loading" {
    print("loading")
  } else if keyPath == "title" {
    self.title = self.webView.title
  } else if keyPath == "estimatedProgress" {
    print(webView.estimatedProgress)
    self.progressView.setProgress(Float(webView.estimatedProgress), animated: true)
  }

  // 已經(jīng)完成加載時(shí),我們就可以做我們的事了
  if !webView.loading {
    // 手動(dòng)調(diào)用JS代碼
    let js = "callJsAlert()";
    self.webView.evaluateJavaScript(js) { (_, _) -> Void in
      print("call js alert")
    }

    UIView.animateWithDuration(0.55, animations: { () -> Void in
      self.progressView.alpha = 0.0;
    })
  }
}

WKUIDelegate

我們看看WKUIDelegate的幾個(gè)代理方法,雖然不是必須實(shí)現(xiàn)的,但是如果我們的頁(yè)面中有調(diào)用了js的alert、confirm、prompt方法,我們應(yīng)該實(shí)現(xiàn)下面這幾個(gè)代理方法,然后在原來(lái)這里調(diào)用native的彈出窗,因?yàn)槭褂?code style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); border-radius: 3px; display: inline; background-color: rgb(248, 248, 248);">WKWebView后,HTML中的alert、confirm、prompt方法調(diào)用是不會(huì)再?gòu)棾龃翱诹?,只是轉(zhuǎn)化成ios的native回調(diào)代理方法:

// MARK: - WKUIDelegate
// 這個(gè)方法是在HTML中調(diào)用了JS的alert()方法時(shí),就會(huì)回調(diào)此API。
// 注意,使用了`WKWebView`后,在JS端調(diào)用alert()就不會(huì)在HTML
// 中顯示彈出窗口。因此,我們需要在此處手動(dòng)彈出ios系統(tǒng)的alert。
func webView(webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: () -> Void) {
  let alert = UIAlertController(title: "Tip", message: message, preferredStyle: .Alert)
  alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (_) -> Void in
    // We must call back js
    completionHandler()
  }))

  self.presentViewController(alert, animated: true, completion: nil)
}

func webView(webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: (Bool) -> Void) {
  let alert = UIAlertController(title: "Tip", message: message, preferredStyle: .Alert)
  alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (_) -> Void in
    // 點(diǎn)擊完成后,可以做相應(yīng)處理,最后再回調(diào)js端
    completionHandler(true)
  }))
  alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: { (_) -> Void in
    // 點(diǎn)擊取消后,可以做相應(yīng)處理,最后再回調(diào)js端
    completionHandler(false)
  }))

  self.presentViewController(alert, animated: true, completion: nil)
}

func webView(webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: (String?) -> Void) {
  let alert = UIAlertController(title: prompt, message: defaultText, preferredStyle: .Alert)

  alert.addTextFieldWithConfigurationHandler { (textField: UITextField) -> Void in
    textField.textColor = UIColor.redColor()
  }
  alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (_) -> Void in
     // 處理好之前,將值傳到j(luò)s端
    completionHandler(alert.textFields![0].text!)
  }))

  self.presentViewController(alert, animated: true, completion: nil)
}

func webViewDidClose(webView: WKWebView) {
  print(__FUNCTION__)
}

WKScriptMessageHandler

接下來(lái),我們看看WKScriptMessageHandler,這個(gè)是注入js名稱(chēng),在js端通過(guò)window.webkit.messageHandlers.{InjectedName}.postMessage()方法來(lái)發(fā)送消息到native。我們需要遵守此協(xié)議,然后實(shí)現(xiàn)其代理方法,就可以收到消息,并做相應(yīng)處理。這個(gè)協(xié)議只有一個(gè)方法:

// MARK: - WKScriptMessageHandler
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
  print(message.body)
  // 如果在開(kāi)始時(shí)就注入有很多的名稱(chēng),那么我們就需要區(qū)分來(lái)處理
  if message.name == "AppModel" {
    print("message name is AppModel")
  }
}

這個(gè)方法是相當(dāng)好的API,我們給js注入一個(gè)名稱(chēng),就會(huì)自動(dòng)轉(zhuǎn)換成js的對(duì)象,然后就可以發(fā)送消息到native端。

WKNavigationDelegate

還有一個(gè)非常關(guān)鍵的代理WKNavigationDelegate,這個(gè)代理有很多的代理方法,可以控制頁(yè)面導(dǎo)航。

其調(diào)用順序?yàn)椋?br>1、這個(gè)代理方法是用于處理是否允許跳轉(zhuǎn)導(dǎo)航。對(duì)于跨域只有Safari瀏覽器才允許,其他瀏覽器是不允許的,因此我們需要額外處理跨域的鏈接。

// 決定導(dǎo)航的動(dòng)作,通常用于處理跨域的鏈接能否導(dǎo)航。WebKit對(duì)跨域進(jìn)行了安全檢查限制,不允許跨域,因此我們要對(duì)不能跨域的鏈接
// 單獨(dú)處理。但是,對(duì)于Safari是允許跨域的,不用這么處理。
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
    print(__FUNCTION__)

    let hostname = navigationAction.request.URL?.host?.lowercaseString

    print(hostname)
    // 處理跨域問(wèn)題
    if navigationAction.navigationType == .LinkActivated && !hostname!.containsString(".baidu.com") {
      // 手動(dòng)跳轉(zhuǎn)
      UIApplication.sharedApplication().openURL(navigationAction.request.URL!)

      // 不允許導(dǎo)航
      decisionHandler(.Cancel)
    } else {
      self.progressView.alpha = 1.0

      decisionHandler(.Allow)
    }
}

2、開(kāi)始加載頁(yè)面內(nèi)容時(shí)就會(huì)回調(diào)此代理方法,與UIWebViewdidStartLoad功能相當(dāng)

func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
    print(__FUNCTION__)
}

3、決定是否允許導(dǎo)航響應(yīng),如果不允許就不會(huì)跳轉(zhuǎn)到該鏈接的頁(yè)面。

func webView(webView: WKWebView, decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void) {
    print(__FUNCTION__)
    decisionHandler(.Allow)
}

4、Invoked when content starts arriving for the main frame.這是API的原注釋。也就是在頁(yè)面內(nèi)容加載到達(dá)mainFrame時(shí)會(huì)回調(diào)此API。如果我們要在mainFrame中注入什么JS,也可以在此處添加。

func webView(webView: WKWebView, didCommitNavigation navigation: WKNavigation!) {
  print(__FUNCTION__)
}

5、加載完成的回調(diào)

func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
  print(__FUNCTION__)
}

如果加載失敗了,會(huì)回調(diào)下面的代理方法:

func webView(webView: WKWebView, didFailNavigation navigation: WKNavigation!, withError error: NSError) {
  print(__FUNCTION__)
}

其實(shí)在還有一些API,一般情況下并不需要。如果我們需要處理在重定向時(shí),需要實(shí)現(xiàn)下面的代理方法就可以接收到。

func webView(webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
  print(__FUNCTION__)
}

如果我們的請(qǐng)求要求授權(quán)、證書(shū)等,我們需要處理下面的代理方法,以提供相應(yīng)的授權(quán)處理等:

func webView(webView: WKWebView, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
    print(__FUNCTION__)
    completionHandler(.PerformDefaultHandling, nil)
}

當(dāng)我們終止頁(yè)面加載時(shí),我們會(huì)可以處理下面的代理方法,如果不需要處理,則不用實(shí)現(xiàn)之:

func webViewWebContentProcessDidTerminate(webView: WKWebView) {
    print(__FUNCTION__)
}

源代碼

具體代碼已經(jīng)發(fā)布到github:https://github.com/CoderJackyHuang/WKWebViewTestDemo

總結(jié)

蘋(píng)果已經(jīng)向我們提供了WKWebView,擁有UIWebView的所有功能,且還提供更多的功能,明示是為了替代UIWebView,但是WKWebView要在ios8.0之后才能使用,因此,如果我們使用Swift來(lái)開(kāi)發(fā)應(yīng)用,兼容版本從8.0開(kāi)始時(shí),可以直接使用WKWebView。

我們可以發(fā)現(xiàn),蘋(píng)果提供了更多簡(jiǎn)便的方式讓native與js交互更加方便,通過(guò)讓native注入名稱(chēng),然后在js端自動(dòng)轉(zhuǎn)換成js的對(duì)象,就可以在js端通過(guò)對(duì)象的方式來(lái)發(fā)送消息到native端。如此一來(lái),就簡(jiǎn)化了js與native的交互了。


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多