UIWebView和WKWebView的使用及js交互

UIWebView和WKWebView的使用及js交互

web頁面和app直接的交互是很常見的東西,以前嘗試過flex和js的相互調用以及android和js的相互調用,卻只有ios沒試過,聽說比較複雜。週末花了點時間研究了一下,確實和其餘的不太同樣,可是 也不見覆雜。css

要知道的事情

ios的webview有2個類,一個叫UIWebView,另外一個是WKWebView。二者的基礎方法都差很少,本文重點是後者,他是取代UIWebView出現的,在app開發者若不須要兼容ios8以前版本,都應該使用WKWebVIew。html

WKWebView 是蘋果在 iOS 8 中引入的新組件,目的是給出一個新的高性能的 Web View 解決方案,擺脫過去 UIWebView 的老舊笨重特別是內存佔用量巨大的問題,它使用Nitro JavaScript引擎,這意味着全部第三方瀏覽器運行JavaScript將會跟safari同樣快.jquery

ios9默認是不容許加載http請求的,對於webview,加載http網頁也是不容許的。能夠經過修改info.plist取消http限制android

在項目中找到info.plist,源文件形式打開,添加下面內容ios

<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict> 

dome截圖git

大綱

  • UIWebView使用
    • 加載網頁或本地頁面
    • app調js方法
    • js調app方法
  • WKWebView的使用
    • 加載頁面,前進,後退,刷新,進度條
    • 前進,後退,刷新,進度條
    • js中alert的攔截
    • app調js方法
    • js調app方法
    • webView生命週期和跳轉代理
  • web頁面
  • 文章demo
  • 參考文章

UIWebView使用


UIVebView如今已經棄用,ios8以上都應該用新的WKWebview,因此UIWebView我就隨意說說,你們隨意看看。github

加載網頁或本地頁面

//從本地加載html let path:String! = NSBundle.mainBundle().pathForResource("index", ofType: "html") webView.loadRequest(NSURLRequest(URL: NSURL.fileURLWithPath(path))) //從網絡加載 webView.loadRequest(NSURLRequest(URL: NSURL(string: "https://www.bing.com")!)) 

注意點: 1. ios9默認不能加載http請求,須要聲明 2. uiwebView網絡請求會進入ternal func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool 委託,委託中若return false,也不會繼續加載web

app調js方法

app調用js方法使用的是 webView.stringByEvaluatingJavaScriptFromString() 這個方法。它能夠直接執行一段js代碼json

//調用js無參數的方法 webView.stringByEvaluatingJavaScriptFromString("hi()") //調用js有參數的方法hello(msg) let js = String(format: "hello('%@')", "liuyanwei") webView.stringByEvaluatingJavaScriptFromString(js) //調用js的參數爲json對象 let js = String(format: "hello(%@)", "{'obj':'liuyanwei'}") webView.stringByEvaluatingJavaScriptFromString(js) //從文件中加載一段js代碼而後執行 do{ let jsString = try String(contentsOfFile: NSBundle.mainBundle().pathForResource("test", ofType: "js")!, encoding: NSUTF8StringEncoding) self.webView.stringByEvaluatingJavaScriptFromString(jsString) } catch{} //直接執行alert webView.stringByEvaluatingJavaScriptFromString("alert('hi')") //執行有返回值的js函數 NSLog("%@", webView.stringByEvaluatingJavaScriptFromString("getName()")!) 

相關的js代碼swift

var hi = function(){ alert("hello") $(".info").html("hi"); } var hello = function(msg){ alert("hello " + msg) if(msg.obj != undefined) alert(msg.obj) } var getName = function(){ return "liuyanwei" } 

js調app方法

不少人以爲,爲何UIWebView中,js調用app的方式怎麼那麼奇怪,其實應該這樣說,UIWebView沒有辦法直接使用js調用app,可是能夠經過攔截request的方式間接實現js調用app方法。

既然是攔截url,那你就能夠任意方式去規定想要調用的url的路徑和app中方法轉換的方式。我這裏使用協議和路徑的方式,例如我攔截到的url是 「hello://hello_liuyanwei」 ,我就把hello當作想調用的方法,路徑當作參數。這種方式不必定好,可是使用起來仍是挺方便的。

js中調用app的方法以下:

//這段代碼是原生js代碼,在js中的做用是作頁面跳轉 //webView經過攔截url請求方式攔截到request,經過解析從而調用 ios hello方法,參數是hello_liuyanwei document.location = "hello://hello_liuyanwei"; 
//webView 須要實現UIWebViewDelegate委託方法 // class ViewController: UIViewController,UIWebViewDelegate .... // webView.delegate = self func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool{ //若是請求協議是hello 這裏的hello來自js的調用,在js中設爲 document.location = "hello://liuyanwei 你好"; //scheme:hello ,msg:liuyanwei 你好 //經過url攔截的方式,做爲對ios原生方法的呼叫 if request.URL?.scheme == "hello"{ let method:String = request.URL?.scheme as String! let sel = Selector(method+":") self.performSelector(sel, withObject:request.URL?.host) request.URL?.path //若是return true ,頁面加載request,咱們只是當作協議使用因此不能頁面跳轉 return false } return true } 

最後說一下關於js調用app的返回值。app調js能夠有返回值,可是js調app是經過間接的攔截request方式實現,它根本就不算方法調用,因此應該是不存在能夠直接產生返回值的(若是不對歡迎指正)。固然若是須要app對js的調用有所響應,能夠經過回叫函數的方式迴應js。能夠在調用app的時候增長一個js回叫函數名 app在處理完以後能夠呼叫回叫函數並把須要的參數經過回叫函數的方式進行傳遞。

WKWebView的使用


WKWebVIew是UIWebView的代替品,新的WebKit框架把原來的功能拆分紅許多小類。本例中主要用到了WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler三個委託和配置類WKWebViewConfiguration去實現webView的request控制,界面控制,js交互,alert重寫等功能。 使用WKWebView須要引入#import <WebKit/WebKit.h>

加載頁面,配置委託和手勢等

//加載頁面 config = WKWebViewConfiguration() //設置位置和委託 webView = WKWebView(frame: self.webWrap.frame, configuration: config) webView.navigationDelegate = self webView.UIDelegate = self self.webWrap.addSubview(webView) //加載網頁 //webView.loadRequest(NSURLRequest(URL: NSURL(string: "https://www.bing.com")!)) //加載本地頁面 webView.loadRequest(NSURLRequest(URL: NSURL.fileURLWithPath(NSBundle.mainBundle().pathForResource("index", ofType: "html")!))) //容許手勢,後退前進等操做 webView.allowsBackForwardNavigationGestures = true 

前進,後退,刷新,進度條

//前進 webView.goBack() //後退 webView.goForward() //刷新 let request = NSURLRequest(URL:webView.URL!) webView.loadRequest(request) //監聽是否能夠前進後退,修改btn.enable屬性 webView.addObserver(self, forKeyPath: "loading", options: .New, context: nil) //監聽加載進度 webView.addObserver(self, forKeyPath: "estimatedProgress", options: .New, context: nil) //重寫self的kvo方法 override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { if (keyPath == "loading") { gobackBtn.enabled = webView.canGoBack forwardBtn.enabled = webView.canGoForward } if (keyPath == "estimatedProgress") { //progress是UIProgressView progress.hidden = webView.estimatedProgress==1 progress.setProgress(Float(webView.estimatedProgress), animated: true) } } 

js中alert的攔截

在WKWebview中,js的alert是不會出現任何內容的,你必須重寫WKUIDelegate委託的runJavaScriptAlertPanelWithMessage message方法,本身處理alert。相似的還有Confirm和prompt也和alert相似,這裏我只以alert爲例。

//alert捕獲 func webView(webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: () -> Void) { // completionHandler() let alert = UIAlertController(title: "ios-alert", message: "\(message)", preferredStyle: .Alert) alert.addAction(UIAlertAction(title: "ok", style: .Default, handler:nil)) alert.addAction(UIAlertAction(title: "cancel", style: .Cancel, handler: nil)) self.presentViewController(alert, animated: true, completion: nil) } 

app調js方法

WKWebView調用js方法和UIWebView相似,一個是evaluateJavaScript,一個是stringByEvaluatingJavaScriptFromString。獲取返回值的方式不一樣,WKWebView用的是回叫函數獲取返回值

//直接調用js webView.evaluateJavaScript("hi()", completionHandler: nil) //調用js帶參數 webView.evaluateJavaScript("hello('liuyanwei')", completionHandler: nil) //調用js獲取返回值 webView.evaluateJavaScript("getName()") { (any,error) -> Void in NSLog("%@", any as! String) } 

js調app方法

UIwebView沒有js調app的方法主要有2種實現,一種是經過攔截request的方式間接實現,另外一種是使用JavaScriptCore的jsContext註冊objc對象或使用JSExport協議導出Native對象的方式。本文主要介紹第一種實現,第二種實現方式參考後續文章 JavaScriptCore的使用教程

1:註冊handler須要在webView初始化以前,如示例,註冊了一個webViewApp的handler

config = WKWebViewConfiguration() //註冊js方法 config.userContentController.addScriptMessageHandler(self, name: "webViewApp") webView = WKWebView(frame: self.webWrap.frame, configuration: config) 

2:處理handler委託。ViewController實現WKScriptMessageHandler委託的func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage)方法

//實現WKScriptMessageHandler委託 class ViewControllerWKScriptMessageHandler //實現js調用ios的handle委託 func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) { //接受傳過來的消息從而決定app調用的方法 let dict = message.body as! Dictionary<String,String> let method:String = dict["method"]! let param1:String = dict["param1"]! if method=="hello"{ hello(param1) } } 

3:js調用

經過 window.webkit.messageHandlers.webViewApp找到以前註冊的handler對象,而後調用postMessage方法把數據傳到app,app經過上一步的方法解析方法名和參數

var message = { 'method' : 'hello', 'param1' : 'liuyanwei', }; window.webkit.messageHandlers.webViewApp.postMessage(message); 

若是須要app對js的調用有所響應,能夠經過回叫函數的方式迴應js。能夠在調用app的時候增長一個js回叫函數名 app在處理完以後能夠呼叫回叫函數並把須要的參數經過回叫函數的方式進行傳遞

webView生命週期和跳轉代理

該代理提供的方法,能夠用來追蹤加載過程(頁面開始加載、加載完成、加載失敗)、決定是否執行跳轉

// 頁面開始加載時調用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 當內容開始返回時調用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 頁面加載完成以後調用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 頁面加載失敗時調用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;

// 接收到服務器跳轉請求以後調用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 在收到響應後,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 在發送請求以前,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

web頁面


隨便說兩句web頁面,demo中的web前段用了jquery去操做dom,btn是在js中添加的,btn的點擊事件也在js中。另外隨便寫了幾個css讓頁面稍微美觀一些。web頁面都在項目文件夾下的web文件夾中。你們其實也沒必要看,由於調用的app的js代碼在文中都有單獨貼出。

參考文章


本文也只是用了一些基本的用法,你們想了解更多,能夠看看下面的三篇文章作補充閱讀。可是如今也沒發現有把webView這塊寫的很全很詳細的文章,從此要是看見我會繼續補充在這裏。

demo


我博客中大部分示例代碼都上傳到了github,地址是:https://github.com/coolnameismy/demo,點擊跳轉代碼下載地址

本文代碼存放目錄是ios-WebView,本demo沒作界面自適應,爲了保證效果請用iPhone6及以上模擬器打開

感謝收看,若是對你們有幫助,請github上follow和star,本文發佈在劉彥瑋的技術博客,轉載請註明出處

dome截圖

相關文章
相關標籤/搜索