上一篇文章我們使用了JavaScriptCore框架UIWebView的示例醒陆,iOS8蘋果偏愛HTML5溪椎,重構了UIWebVIew氛改,給我們帶來了WKWebView稍刀,使其性能撩独、穩(wěn)定性敞曹、功能大幅度提升,也更好的支持了HTML5的新特性综膀。這篇文章就們就拿WKWebView來小試牛刀澳迫,這也是我的學習過程。
我們先來看一下學習這個WKWebView的主要類別
閱讀目錄
- 一剧劝、WKWebView Framework
- 二橄登、WKWebView中的三個代理方法
- 三、使用WKWebView重寫
- 四讥此、后記
WKWebView的14個類與3個協(xié)議:
WKBackForwardList: 之前訪問過的 web 頁面的列表拢锹,可以通過后退和前進動作來訪問到。
WKBackForwardListItem: webview 中后退列表里的某一個網(wǎng)頁萄喳。
WKFrameInfo: 包含一個網(wǎng)頁的布局信息卒稳。
WKNavigation: 包含一個網(wǎng)頁的加載進度信息。
WKNavigationAction: 包含可能讓網(wǎng)頁導航變化的信息他巨,用于判斷是否做出導航變化展哭。
WKNavigationResponse: 包含可能讓網(wǎng)頁導航變化的返回內(nèi)容信息,用于判斷是否做出導航變化闻蛀。
WKPreferences: 概括一個 webview 的偏好設置。
WKProcessPool: 表示一個 web 內(nèi)容加載池您市。
WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法觉痛。
WKScriptMessage: 包含網(wǎng)頁發(fā)出的信息。
WKUserScript: 表示可以被網(wǎng)頁接受的用戶腳本茵休。
WKWebViewConfiguration: 初始化 webview 的設置薪棒。
WKWindowFeatures: 指定加載新網(wǎng)頁時的窗口屬性。
WKWebsiteDataStore: 包含網(wǎng)頁數(shù)據(jù)存儲和查找榕莺。
WKNavigationDelegate: 提供了追蹤主窗口網(wǎng)頁加載過程和判斷主窗口和子窗口是否進行頁面加載新頁面的相關方法俐芯。
WKUIDelegate: 提供用原生控件顯示網(wǎng)頁的方法回調(diào)。
WKScriptMessageHandler: 提供從網(wǎng)頁中收消息的回調(diào)方法钉鸯。
二吧史、WKWebView中的三個代理方法
- WKNavigationDelegate
該代理提供的方法,可以用來追蹤加載過程(頁面開始加載唠雕、加載完成贸营、加載失敗)岩睁、決定是否執(zhí)行跳轉(zhuǎn)钞脂。
// 頁面開始加載時調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 當內(nèi)容開始返回時調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 頁面加載完成之后調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 頁面加載失敗時調(diào)用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
頁面跳轉(zhuǎn)的代理方法有三種,分為(收到跳轉(zhuǎn)與決定是否跳轉(zhuǎn)兩種)
// 接收到服務器跳轉(zhuǎn)請求之后調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 在收到響應后捕儒,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 在發(fā)送請求之前冰啃,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
- WKUIDelegate
創(chuàng)建一個新的WKWebView
// 創(chuàng)建一個新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
剩下三個代理方法全都是與界面彈出提示框相關的,針對于web界面的三種提示框(警告框、確認框阎毅、輸入框)分別對應三種代理方法焚刚。
// 界面彈出警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler;
// 界面彈出確認框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
// 界面彈出輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
- WKScriptMessageHandler
這個協(xié)議中包含一個必須實現(xiàn)的方法,這個方法是native與web端交互的關鍵净薛,它可以直接將接收到的JS腳本轉(zhuǎn)為OC或Swift對象汪榔。
// 從web界面中接收到一個腳本時調(diào)用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
三、使用WKWebView
我這里加載的是本地的html肃拜,我先貼出html的代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" http-equiv="Content-Type" content="text/html">
<title>小紅帽</title>
<style>
*{
font-size: 50px;
}
.div{
align:"center";
}
.btn{
height:80px; width:80%; padding: 0px 30px 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1em; color: white
}
</style>
<script>
function clear(){
document.getElementById('mobile').innerHTML=''
document.getElementById('name').innerHTML=''
document.getElementById('msg').innerHTML=''
}
//oc調(diào)用js的方法列表
function alertMobile(){
//這里已經(jīng)調(diào)用過來了 但是搞不明白為什么alert方法沒有響應
//alert('我是上面的小黃 手機號是:13300001111')
document.getElementById('mobile').innerHTML='我是上面的小黃 手機號是:13300001111'
}
function alertName(msg){
document.getElementById('name').innerHTML='你好 ' + msg + ', 我也很高興見到你'
}
function alertSendMsg(num,msg){
document.getElementById('msg').innerHTML='這是我的手機號:' + num + ',' + msg + '!!'
}
//JS響應方法列表
function btnClick1(){
window.webkit.messageHandlers.showMobile.postMessage(null)
//window.webkit.messageHandlers.showMobile.postMessage(null)
}
function btnClick2(){
window.webkit.messageHandlers.showName.postMessage('xiao黃')
}
function btnClick3(){
window.webkit.messageHandlers.showSendMsg.postMessage(['13300001111', 'Go Climbing This Weekend !!!'])
}
</script>
</head>
<body>
<br/>
<div>
<label>自己寫html</label>
</div>
<br/>
<div id="mobile"></div>
<div class="div">
<button class="btn" type="button" onclick="btnClick1()">小紅帽的手機號</button>
</div>
<br/>
<div id="name"></div>
<div class="div">
<button class="btn" type="button" onclick="btnClick2()">打電話給小紅帽</button>
</div>
<br/>
<div id="msg"></div>
<div class="div">
<button class="btn" type="button" onclick="btnClick3()">發(fā)短信給小紅帽</button>
</div>
<br/>
</body>
</html>
關于html的內(nèi)容痴腌,我在這里不多加解釋,有興趣的同學可以去學習一下關于h5燃领,css士聪,javascript的相關知識。
WKWebView不支持nib文件猛蔽,所以這里需要使用代碼初始化并加載WebView
/*設置configur對象的WKUserContentController屬性的信息剥悟,也就是設置js可與webview內(nèi)容交互配置
1、通過這個對象可以注入js名稱曼库,在js端通過window.webkit.messageHandlers.自定義的js名稱.postMessage(如果有參數(shù)可以傳遞參數(shù))方法來發(fā)送消息到native区岗;
2、我們需要遵守WKScriptMessageHandler協(xié)議毁枯,設置代理,然后實現(xiàn)對應代理方法(userContentController:didReceiveScriptMessage:);
3慈缔、在上述代理方法里面就可以拿到對應的參數(shù)以及原生的方法名,我們就可以通過NSSelectorFromString包裝成一個SEL种玛,然后performSelector調(diào)用就可以了
4藐鹤、以上內(nèi)容是WKWebview和UIWebview針對JS調(diào)用原生的方法最大的區(qū)別(UIWebview中主要是通過是否允許加載對應url的那個代理方法,通過在js代碼里面寫好特殊的url赂韵,然后攔截到對應的url娱节,進行字符串的匹配以及截取操作,最后包裝成SEL祭示,然后調(diào)用就可以了)
*/
/*
上述是理論說明肄满,結(jié)合下面的實際代碼再做一次解釋,保你一看就明白
1质涛、通過addScriptMessageHandler:name:方法悄窃,我們就可以注入js名稱了,其實這個名稱最好就是跟你的方法名一樣,這樣方便你包裝使用蹂窖,我這里自己寫的就是openBigPicture轧抗,對應js中的代碼就是window.webkit.messageHandlers.openBigPicture.postMessage()
2、因為我的方法是有參數(shù)的瞬测,參數(shù)就是圖片的url横媚,因為點擊網(wǎng)頁中的圖片纠炮,要調(diào)用原生的瀏覽大圖的方法,所以你可以通過字符串拼接的方式給"openBigPicture"拼接成"openBigPicture:"灯蝴,我這里沒有采用這種方式恢口,我傳遞的參數(shù)直接是字典,字典里面放了方法名以及圖片的url穷躁,到時候直接取出來用就可以了
3耕肩、我的js代碼中關于這塊的代碼是
window.webkit.messageHandlers.openBigPicture.postMessage({methodName:"openBigPicture:",imageSrc:imageArray[this.index].src});
4、js和原生交互這塊內(nèi)容離不開
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{}這個代理方法问潭,這個方法以及參數(shù)說明請到下面方法對應處
*/
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
//設置configur對象的preferences屬性的信息
config.preferences.minimumFontSize = 18;
WKWebView *wkView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, 414, 735/2) configuration:config];
wkView.navigationDelegate=self;
[self.view addSubview:wkView];
self.wkwebView = wkView;
NSString *filePath=[[NSBundle mainBundle] pathForResource:@"myindex" ofType:@"html"];
NSURL *baseUrl=[[NSBundle mainBundle] bundleURL];
[self.wkwebView loadHTMLString:[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] baseURL:baseUrl];
WKUserContentController *userController=config.userContentController;
//JS調(diào)用OC 添加處理腳本
[userController addScriptMessageHandler:self name:@"showMobile"];
[userController addScriptMessageHandler:self name:@"showName"];
[userController addScriptMessageHandler:self name:@"showSendMsg"];
在代理方法中處理相關的操作猿诸,js調(diào)用oc的代碼
//js調(diào)用oc,通過這個代理方法進行攔截
/*
1狡忙、js調(diào)用原生的方法就會走這個方法
2梳虽、message參數(shù)里面有2個參數(shù)我們比較有用,name和body灾茁,
2.1 :其中name就是之前已經(jīng)通過addScriptMessageHandler:name:方法注入的js名稱
2.2 :其中body就是我們傳遞的參數(shù)了窜觉,比如說我在js端傳入的是一個字典,所以取出來也是字典
*/
-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
if ([message.name isEqualToString:@"showMobile"]) {
[self alertMessage:@"這是下面的小紅帽 手機號 123333333"];
}
if ([message.name isEqualToString:@"showName"]) {
NSString *info=[NSString stringWithFormat:@"%@",message.body];
[self alertMessage:info];
}
if ([message.name isEqualToString:@"showSendMsg"]) {
NSArray *arr=message.body;
NSString *info=[NSString stringWithFormat:@"%@%@",arr.firstObject,arr.lastObject];
[self alertMessage:info];
}
}
-(void)alertMessage:(NSString *)msg{
UIAlertController *alertController=[UIAlertController alertControllerWithTitle:@"信息" message:msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok=[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}];
[alertController addAction:ok];
[self presentViewController:alertController animated:YES completion:^{
}];
}
下面的是oc調(diào)用js北专,分開寫主要是為了讓大家看清楚每部分的代碼禀挫,已經(jīng)相關的解釋
- (IBAction)clearBtn:(id)sender {
[self.wkwebView evaluateJavaScript:@"clear()" completionHandler:nil];
}
//oc調(diào)用js,通過evaluateJavaScript:注入方法名
- (IBAction)clickBtnItem:(UIButton *)sender {
switch (sender.tag) {
case 100:
{
[self.wkwebView evaluateJavaScript:@"alertMobile()" completionHandler:nil];
}
break;
case 101:
{
[self.wkwebView evaluateJavaScript:@"alertName('小紅毛')" completionHandler:nil];
}
break;
case 102:
{
[self.wkwebView evaluateJavaScript:@"alertSendMsg('18870707070','周末爬山真是件愉快的事情')" completionHandler:nil];
}
break;
default:
break;
}
}
這是我拿出html里面javaScript的代碼拓颓,供大家閱讀语婴,里面我都有標注的注釋。
<script>
function clear(){
document.getElementById('mobile').innerHTML=''
document.getElementById('name').innerHTML=''
document.getElementById('msg').innerHTML=''
}
//oc調(diào)用js的方法列表
function alertMobile(){
//這里已經(jīng)調(diào)用過來了 但是搞不明白為什么alert方法沒有響應
//alert('我是上面的小黃 手機號是:13300001111')
document.getElementById('mobile').innerHTML='我是上面的小黃 手機號是:13300001111'
}
function alertName(msg){
document.getElementById('name').innerHTML='你好 ' + msg + ', 我也很高興見到你'
}
function alertSendMsg(num,msg){
document.getElementById('msg').innerHTML='這是我的手機號:' + num + ',' + msg + '!!'
}
//JS響應方法列表
function btnClick1(){
window.webkit.messageHandlers.showMobile.postMessage(null)
//window.webkit.messageHandlers.showMobile.postMessage(null)
}
function btnClick2(){
window.webkit.messageHandlers.showName.postMessage('xiao黃')
}
function btnClick3(){
window.webkit.messageHandlers.showSendMsg.postMessage(['13300001111', 'Go Climbing This Weekend !!!'])
}
</script>
四录粱、學習總結(jié)
到此,關于js和原生的交互系列的示例已完成画拾,過程中遇到很多坑啥繁,但也很有收獲,關于我的描述不清青抛,或者不妥的地方旗闽,請大家指出。每篇文章都會對知識點進行總結(jié)蜜另,在文章末尾給出相關鏈接和示例DEMO的地址适室,同樣本文的示例也已放在GitHub上,需要的同學取走不謝举瑰。關于這幾篇文章的DEMO捣辆,對比學習,在看的過程中有什么呢疑問此迅,歡迎在下面留言汽畴,若發(fā)現(xiàn)文章中有那些地方?jīng)]有闡述清旧巾,或者沒有提到也可以留言告訴我,我們公司最近一直是h5和原生的app想結(jié)合忍些,所以研究一段時間鲁猩。隨著H5的強大,hybrid app已經(jīng)成為當前互聯(lián)網(wǎng)的大方向罢坝,單純的native app和web app在某些方面顯得就很劣勢廓握。
戳這里:本文的DEMO地址歡迎star
參考資料(戳這里):
http://nshipster.cn/wkwebkit/
http://www.huangyibiao.com/archives/742