通過本篇文章,至少可以學(xué)習(xí)到:
OC如何給JS注入對(duì)象及JS如何給IOS發(fā)送數(shù)據(jù)
JS調(diào)用alert给郊、confirm淆九、prompt時(shí)炭庙,不采用JS原生提示煌寇,而是使用iOS原生來實(shí)現(xiàn)
如何監(jiān)聽web內(nèi)容加載進(jìn)度阀溶、是否加載完成
如何處理去跨域問題
創(chuàng)建配置類
在創(chuàng)建WKWebView之前银锻,需要先創(chuàng)建配置對(duì)象,用于做一些配置:
WKWebViewConfiguration*config=[[WKWebViewConfigurationalloc]init];
配置偏好設(shè)置
偏好設(shè)置也沒有必須去修改它腐碱,都使用默認(rèn)的就可以了症见,除非你真的需要修改它:
// 設(shè)置偏好設(shè)置
config.preferences=[[WKPreferencesalloc]init];
// 默認(rèn)為0
config.preferences.minimumFontSize=10;
// 默認(rèn)認(rèn)為YES
config.preferences.javaScriptEnabled=YES;
// 在iOS上默認(rèn)為NO谋作,表示不能自動(dòng)通過窗口打開
config.preferences.javaScriptCanOpenWindowsAutomatically=NO;
配置web內(nèi)容處理池
其實(shí)我們沒有必要去創(chuàng)建它遵蚜,因?yàn)樗緵]有屬性和方法:
// web內(nèi)容處理池吭净,由于沒有屬性可以設(shè)置肴甸,也沒有方法可以調(diào)用原在,不用手動(dòng)創(chuàng)建
config.processPool=[[WKProcessPoolalloc]init];
配置Js與Web內(nèi)容交互
WKUserContentController是用于給JS注入對(duì)象的,注入對(duì)象后秽浇,JS端就可以使用:
window.webkit.messageHandlers..postMessage()
來調(diào)用發(fā)送數(shù)據(jù)給iOS端柬焕,比如:
window.webkit.messageHandlers.AppModel.postMessage({body:'傳數(shù)據(jù)'});
AppModel就是我們要注入的名稱击喂,注入以后懂昂,就可以在JS端調(diào)用了没宾,傳數(shù)據(jù)統(tǒng)一通過body傳循衰,可以是多種類型,只支持NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull類型伐蒋。
下面我們配置給JS的main frame注入AppModel名稱迁酸,對(duì)于JS端可就是對(duì)象了:
// 通過JS與webview內(nèi)容交互
config.userContentController=[[WKUserContentControlleralloc]init];
// 注入JS對(duì)象名稱AppModel奸鬓,當(dāng)JS通過AppModel來調(diào)用時(shí)串远,
// 我們可以在WKScriptMessageHandler代理中接收到
[config.userContentControlleraddScriptMessageHandler:selfname:@"AppModel"];
當(dāng)JS通過AppModel發(fā)送數(shù)據(jù)到iOS端時(shí)伸但,會(huì)在代理中收到:
#pragma mark - WKScriptMessageHandler
-(void)userContentController:(WKUserContentController*)userContentController
didReceiveScriptMessage:(WKScriptMessage*)message{
if([message.nameisEqualToString:@"AppModel"]){
// 打印所傳過來的參數(shù)留搔,只支持NSNumber, NSString, NSDate, NSArray,
// NSDictionary, and NSNull類型
NSLog(@"%@",message.body);
}
}
所有JS調(diào)用iOS的部分函喉,都只可以在此處使用哦管呵。當(dāng)然我們也可以注入多個(gè)名稱(JS對(duì)象)捐下,用于區(qū)分功能坷襟。
創(chuàng)建WKWebView
通過唯一的默認(rèn)構(gòu)造器來創(chuàng)建對(duì)象:
self.webView=[[WKWebViewalloc]initWithFrame:self.view.bounds
configuration:config];
[self.viewaddSubview:self.webView];
加載H5頁(yè)面
NSURL*path=[[NSBundlemainBundle]URLForResource:@"test"withExtension:@"html"];
[self.webViewloadRequest:[NSURLRequestrequestWithURL:path]];
配置代理
如果需要處理web導(dǎo)航條上的代理處理婴程,比如鏈接是否可以跳轉(zhuǎn)或者如何跳轉(zhuǎn)档叔,需要設(shè)置代理衙四;而如果需要與在JS調(diào)用alert传蹈、confirm惦界、prompt函數(shù)時(shí)表锻,通過JS原生來處理瞬逊,而不是調(diào)用JS的alert仪或、confirm范删、prompt函數(shù)旨巷,那么需要設(shè)置UIDelegate,在得到響應(yīng)后可以將結(jié)果反饋到JS端:
// 導(dǎo)航代理
self.webView.navigationDelegate=self;
// 與webview UI交互代理
self.webView.UIDelegate=self;
添加對(duì)WKWebView屬性的監(jiān)聽
WKWebView有好多個(gè)支持KVO的屬性若锁,這里只是監(jiān)聽loading又固、title仰冠、estimatedProgress屬性洋只,分別用于判斷是否正在加載、獲取頁(yè)面標(biāo)題舷礼、當(dāng)前頁(yè)面載入進(jìn)度:
// 添加KVO監(jiān)聽
[self.webViewaddObserver:self
forKeyPath:@"loading"
options:NSKeyValueObservingOptionNew
context:nil];
[self.webViewaddObserver:self
forKeyPath:@"title"
options:NSKeyValueObservingOptionNew
context:nil];
[self.webViewaddObserver:self
forKeyPath:@"estimatedProgress"
options:NSKeyValueObservingOptionNew
context:nil];
然后我們就可以實(shí)現(xiàn)KVO處理方法妻献,在loading完成時(shí)育拨,可以注入一些JS到web中熬丧。這里只是簡(jiǎn)單地執(zhí)行一段web中的JS函數(shù):
#pragma mark - KVO
-(void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context{
if([keyPathisEqualToString:@"loading"]){
NSLog(@"loading");
}elseif([keyPathisEqualToString:@"title"]){
self.title=self.webView.title;
}elseif([keyPathisEqualToString:@"estimatedProgress"]){
NSLog(@"progress: %f",self.webView.estimatedProgress);
self.progressView.progress=self.webView.estimatedProgress;
}
// 加載完成
if(!self.webView.loading){
// 手動(dòng)調(diào)用JS代碼
// 每次頁(yè)面完成都彈出來,大家可以在測(cè)試時(shí)再打開
NSString*js=@"callJsAlert()";
[self.webViewevaluateJavaScript:jscompletionHandler:^(id_Nullableresponse,NSError*_Nullableerror){
NSLog(@"response: %@ error: %@",response,error);
NSLog(@"call js alert by native");
}];
[UIViewanimateWithDuration:0.5animations:^{
self.progressView.alpha=0;
}];
}
}
WKUIDelegate
與JS原生的alert怀挠、confirm析蝴、prompt交互,將彈出來的實(shí)際上是我們?cè)拇翱诼塘埽皇荍S的闷畸。在得到數(shù)據(jù)后,由原生傳回到JS:
#pragma mark - WKUIDelegate
-(void)webViewDidClose:(WKWebView*)webView{
NSLog(@"%s",__FUNCTION__);
}
// 在JS端調(diào)用alert函數(shù)時(shí)吞滞,會(huì)觸發(fā)此代理方法佑菩。
// JS端調(diào)用alert時(shí)所傳的數(shù)據(jù)可以通過message拿到
// 在原生得到結(jié)果后盾沫,需要回調(diào)JS,是通過completionHandler回調(diào)
-(void)webView:(WKWebView*)webViewrunJavaScriptAlertPanelWithMessage:(NSString*)messageinitiatedByFrame:(WKFrameInfo*)framecompletionHandler:(void(^)(void))completionHandler{
NSLog(@"%s",__FUNCTION__);
UIAlertController*alert=[UIAlertControlleralertControllerWithTitle:@"alert"message:@"JS調(diào)用alert"preferredStyle:UIAlertControllerStyleAlert];
[alertaddAction:[UIAlertActionactionWithTitle:@"確定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction*_Nonnullaction){
completionHandler();
}]];
[selfpresentViewController:alertanimated:YEScompletion:NULL];
NSLog(@"%@",message);
}
// JS端調(diào)用confirm函數(shù)時(shí),會(huì)觸發(fā)此方法
// 通過message可以拿到JS端所傳的數(shù)據(jù)
// 在iOS端顯示原生alert得到Y(jié)ES/NO后
// 通過completionHandler回調(diào)給JS端
-(void)webView:(WKWebView*)webViewrunJavaScriptConfirmPanelWithMessage:(NSString*)messageinitiatedByFrame:(WKFrameInfo*)framecompletionHandler:(void(^)(BOOLresult))completionHandler{
NSLog(@"%s",__FUNCTION__);
UIAlertController*alert=[UIAlertControlleralertControllerWithTitle:@"confirm"message:@"JS調(diào)用confirm"preferredStyle:UIAlertControllerStyleAlert];
[alertaddAction:[UIAlertActionactionWithTitle:@"確定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction*_Nonnullaction){
completionHandler(YES);
}]];
[alertaddAction:[UIAlertActionactionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:^(UIAlertAction*_Nonnullaction){
completionHandler(NO);
}]];
[selfpresentViewController:alertanimated:YEScompletion:NULL];
NSLog(@"%@",message);
}
// JS端調(diào)用prompt函數(shù)時(shí),會(huì)觸發(fā)此方法
// 要求輸入一段文本
// 在原生輸入得到文本內(nèi)容后,通過completionHandler回調(diào)給JS
-(void)webView:(WKWebView*)webViewrunJavaScriptTextInputPanelWithPrompt:(NSString*)promptdefaultText:(nullableNSString*)defaultTextinitiatedByFrame:(WKFrameInfo*)framecompletionHandler:(void(^)(NSString*__nullableresult))completionHandler{
NSLog(@"%s",__FUNCTION__);
NSLog(@"%@",prompt);
UIAlertController*alert=[UIAlertControlleralertControllerWithTitle:@"textinput"message:@"JS調(diào)用輸入框"preferredStyle:UIAlertControllerStyleAlert];
[alertaddTextFieldWithConfigurationHandler:^(UITextField*_NonnulltextField){
textField.textColor=[UIColorredColor];
}];
[alertaddAction:[UIAlertActionactionWithTitle:@"確定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction*_Nonnullaction){
completionHandler([[alert.textFieldslastObject]text]);
}]];
[selfpresentViewController:alertanimated:YEScompletion:NULL];
}
WKNavigationDelegate
如果需要處理web導(dǎo)航操作,比如鏈接跳轉(zhuǎn)、接收響應(yīng)、在導(dǎo)航開始、成功畏浆、失敗等時(shí)要做些處理蝎毡,就可以通過實(shí)現(xiàn)相關(guān)的代理方法:
#pragma mark - WKNavigationDelegate
// 請(qǐng)求開始前挑胸,會(huì)先調(diào)用此代理方法
// 與UIWebView的
// - (BOOL)webView:(UIWebView *)webView
// shouldStartLoadWithRequest:(NSURLRequest *)request
// navigationType:(UIWebViewNavigationType)navigationType;
// 類型,在請(qǐng)求先判斷能不能跳轉(zhuǎn)(請(qǐng)求)
-(void)webView:(WKWebView*)webViewdecidePolicyForNavigationAction:(WKNavigationAction*)navigationActiondecisionHandler:(void(^)(WKNavigationActionPolicy))decisionHandler{
NSString*hostname=navigationAction.request.URL.host.lowercaseString;
if(navigationAction.navigationType==WKNavigationTypeLinkActivated
&&![hostnamecontainsString:@".baidu.com"]){
// 對(duì)于跨域,需要手動(dòng)跳轉(zhuǎn)
[[UIApplicationsharedApplication]openURL:navigationAction.request.URL];
// 不允許web內(nèi)跳轉(zhuǎn)
decisionHandler(WKNavigationActionPolicyCancel);
}else{
self.progressView.alpha=1.0;
decisionHandler(WKNavigationActionPolicyAllow);
}
NSLog(@"%s",__FUNCTION__);
}
// 在響應(yīng)完成時(shí)喘先,會(huì)回調(diào)此方法
// 如果設(shè)置為不允許響應(yīng),web內(nèi)容就不會(huì)傳過來
-(void)webView:(WKWebView*)webViewdecidePolicyForNavigationResponse:(WKNavigationResponse*)navigationResponsedecisionHandler:(void(^)(WKNavigationResponsePolicy))decisionHandler{
decisionHandler(WKNavigationResponsePolicyAllow);
NSLog(@"%s",__FUNCTION__);
}
// 開始導(dǎo)航跳轉(zhuǎn)時(shí)會(huì)回調(diào)
-(void)webView:(WKWebView*)webViewdidStartProvisionalNavigation:(null_unspecifiedWKNavigation*)navigation{
NSLog(@"%s",__FUNCTION__);
}
// 接收到重定向時(shí)會(huì)回調(diào)
-(void)webView:(WKWebView*)webViewdidReceiveServerRedirectForProvisionalNavigation:(null_unspecifiedWKNavigation*)navigation{
NSLog(@"%s",__FUNCTION__);
}
// 導(dǎo)航失敗時(shí)會(huì)回調(diào)
-(void)webView:(WKWebView*)webViewdidFailProvisionalNavigation:(null_unspecifiedWKNavigation*)navigationwithError:(NSError*)error{
NSLog(@"%s",__FUNCTION__);
}
// 頁(yè)面內(nèi)容到達(dá)main frame時(shí)回調(diào)
-(void)webView:(WKWebView*)webViewdidCommitNavigation:(null_unspecifiedWKNavigation*)navigation{
NSLog(@"%s",__FUNCTION__);
}
// 導(dǎo)航完成時(shí),會(huì)回調(diào)(也就是頁(yè)面載入完成了)
-(void)webView:(WKWebView*)webViewdidFinishNavigation:(null_unspecifiedWKNavigation*)navigation{
NSLog(@"%s",__FUNCTION__);
}
// 導(dǎo)航失敗時(shí)會(huì)回調(diào)
-(void)webView:(WKWebView*)webViewdidFailNavigation:(null_unspecifiedWKNavigation*)navigationwithError:(NSError*)error{
}
// 對(duì)于HTTPS的都會(huì)觸發(fā)此代理,如果不要求驗(yàn)證庄呈,傳默認(rèn)就行
// 如果需要證書驗(yàn)證文兑,與使用AFN進(jìn)行HTTPS證書驗(yàn)證是一樣的
-(void)webView:(WKWebView*)webViewdidReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challengecompletionHandler:(void(^)(NSURLSessionAuthChallengeDispositiondisposition,NSURLCredential*__nullablecredential))completionHandler{
NSLog(@"%s",__FUNCTION__);
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling,nil);
}
// 9.0才能使用籍铁,web內(nèi)容處理中斷時(shí)會(huì)觸發(fā)
-(void)webViewWebContentProcessDidTerminate:(WKWebView*)webView{
NSLog(@"%s",__FUNCTION__);
}
JS端代碼
iOSandJs
*{
font-size:40px;
}
Testhowtouseobjective-ccalljs
Clickmehere:JumptoBaidu
functioncallJsAlert(){
alert('Objective-C call js to show alert');
window.webkit.messageHandlers.AppModel.postMessage({body:'call js alert in js'});
}
functioncallJsConfirm(){
if(confirm('confirm','Objective-C call js to show confirm')){
document.getElementById('jsParamFuncSpan').innerHTML
='true';
}else{
document.getElementById('jsParamFuncSpan').innerHTML
='false';
}
// AppModel是我們所注入的對(duì)象
window.webkit.messageHandlers.AppModel.postMessage({body:'call js confirm in js'});
}
functioncallJsInput(){
varresponse=prompt('Hello','Please input your name:');
document.getElementById('jsParamFuncSpan').innerHTML=response;
// AppModel是我們所注入的對(duì)象
window.webkit.messageHandlers.AppModel.postMessage({body:response});
}
停下休息的時(shí)候不要忘記別人在奔跑!