iOS 與 JS 交互

推薦使用WKWebView

WKWebView 是蘋果在 iOS 8 中引入的新組件,目的是給出一個新的高性能的 Web View 解決方案,擺脫過去 UIWebView 的老舊笨重特別是內(nèi)存占用量巨大的問題。蘋果將 UIWebViewDelegate 與 UIWebView 重構(gòu)成了 14 個類和 3 個協(xié)議,引入了不少新的功能和接口。

WKWebView準備工作

#import <WebKit/WebKit.h>
@interface ViewController () <WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate>
@end

在創(chuàng)建WKWebView之前,我們先做配置操作

這邊就需要用到WKWebViewConfiguration

 WKWebViewConfiguration *config  =[[WKWebViewConfiguration alloc] init];

交互需要利用WKUserContentController

這個類是用來給JS注入對象的,對象是和網(wǎng)頁端一起約定好的。
config.userContentController = [[WKUserContentController alloc] init];
例如我們現(xiàn)在約定使用sendInfoModel這個對象,那么:

  • OC中,給JS注入對象:
[config.userContentController addScriptMessageHandler:self name:@"sendInfoModel"];
  • JS中,使用對象:
    這是JS的一個傳值操作,通過body來傳值。
window.webkit.messageHandlers.sendInfoModel.postMessage({body: 'JS要傳值'});
  • 當(dāng)JS通過sendInfoModel傳值的時候,在iOS端,我們在下面的這個代理中接受結(jié)果:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
//首先判斷一下是哪個對象(我們可以注入多個不同的對象,來進行不同的操作)

  if ([message.name isEqualToString:@"sendInfoModel"]) {
// 打印所傳過來的參數(shù),只支持NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull類

    NSLog(@%@", message.body);
         
  }
}

下面利用上面的WKWebViewConfiguration *config配置構(gòu)造器來創(chuàng)建KWWebView

self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];

NSURL *path = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];

[self.webView loadRequest:[NSURLRequest requestWithURL:path]];

[self.view addSubview:self.webView];
  • WKWebView的title(標題), loading(BOOL,是否在加載), estimatedProgress(加載進度),可以用KVO來監(jiān)聽,進行一些細節(jié)操作(進度條啥的)。

WKWebView在請求開始前會調(diào)用下面這個代理方法:

-webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler

  • navigationAction決定是否讓一個網(wǎng)頁被加載,我們檢查它的navigationType和request這兩個屬性;
  • navigationType 枚舉值,UIWebViewNavigationTypeLinkClicked為點擊鏈接操作;
  • request用來確定它是否是外部鏈接。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler 

{


NSString *hostname = navigationAction.request.URL.host.lowercaseString;


if (navigationAction.navigationType == WKNavigationTypeLinkActivated
      && ![hostname containsString:@".lanou.com"])

 {
// 對于跨域,需要手動跳轉(zhuǎn)
    [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
    
  // 不允許web內(nèi)跳轉(zhuǎn)

   decisionHandler(WKNavigationActionPolicyCancel);
  
} else 

{
    
  //允許web內(nèi)跳轉(zhuǎn)

  self.progressView.alpha = 1.0;//(顯示進度條)
  
  decisionHandler(WKNavigationActionPolicyAllow);
  }


}

KWWebView完成響應(yīng)

-(void)webView:(WKWebView *)webView
decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
 
{
  //允許響應(yīng)
        
   decisionHandler(WKNavigationResponsePolicyAllow);
  
   //若為不允許響應(yīng),那么web內(nèi)容就傳不過來


}

針對HTTPS協(xié)議的鏈接,加載時都會觸發(fā)以下方法來驗證證書(操作與使用AFN進行HTTPS驗證證書一樣)

-(void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:
(void (^)(NSURLSessionAuthChallengeDisposition disposition,
          NSURLCredential *__nullable credential))completionHandler 

{

  
 //如不需要驗證傳默認的就可以

  completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

WKUIDelegate

與JS原生的alert,confirm,prompt進行交互:

  • alert :
    JS中:
function callJsAlert() {
        alert('這個是OC調(diào)用JS的方法,并且通過Alert()進行顯示出來!');
        
        window.webkit.messageHandlers.sendInfoModel.postMessage({body: '在JS中調(diào)用JS alert中方法'});
      }

OC中: 當(dāng)JS調(diào)用alert時會觸發(fā)此方法

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler 
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:@"JS調(diào)用alert" preferredStyle:UIAlertControllerStyleAlert];
  [alert addAction:[UIAlertAction actionWithTitle:@"確定" style: UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    completionHandler();
  }]];
  
  [self presentViewController:alert animated:YES completion:NULL];
  NSLog(@"%@", message);
}
  • confirm
  • prompt

與alert相似

 JS中:
function callJsConfirm() {
      if (confirm('confirm', 'Objective-C call js to show confirm')) {
        document.getElementById('jsParamFuncSpan').innerHTML
        = 'true';
        // sendInfoModel是我們所注入的對象
        window.webkit.messageHandlers.sendInfoModel.postMessage({body: '我是JS里面的內(nèi)容'});//傳值
      } else {
        document.getElementById('jsParamFuncSpan').innerHTML
        = 'false';
      }
      
    }
    
    function callJsInput() {
      var response = prompt('Hello', '請輸入你的名字:');
      document.getElementById('jsParamFuncSpan').innerHTML = response;
      
       // sendInfoModel是我們所注入的對象
      window.webkit.messageHandlers.sendInfoModel.postMessage({body: response});
    }

OC中:

- (void)webView:(WKWebView *)webView
runJavaScriptConfirmPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(BOOL result))completionHandler

 {
  
  UIAlertController *alert = [UIAlertController alertControllerWithTitle:
                              @"confirm" message:@"JS調(diào)用confirm"
                               preferredStyle:UIAlertControllerStyleAlert];
  [alert addAction:[UIAlertAction actionWithTitle:@"確定"
    style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action){
    completionHandler(YES);
  }]];
  [alert addAction:[UIAlertAction actionWithTitle:@"取消"
  style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    completionHandler(NO);
  }]];
  [self presentViewController:alert animated:YES completion:NULL];
  NSLog(@"%@", message);

}

-(void)webView:(WKWebView *)webView
runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt
    defaultText:(nullable NSString *)defaultText
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(NSString * __nullable result))completionHandler

 {
 
  NSLog(@"%@", prompt);
  UIAlertController *alert = [UIAlertController alertControllerWithTitle:
                              @"textinput" message:@"JS調(diào)用輸入框"
                              preferredStyle:UIAlertControllerStyleAlert];
  [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
    textField.textColor = [UIColor redColor];
  }];
  [alert addAction:[UIAlertAction actionWithTitle:@"確定"
    style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    completionHandler([[alert.textFields lastObject] text]);
  }]];
  
  [self presentViewController:alert animated:YES completion:NULL];

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容