WKWebView與js交互 實戰(zhàn)

demo動效GIF.gif

WKWebView介紹

  • WKWebView 是蘋果在 iOS 8 中引入的新組件,目的是給出一個新的高性能的 Web View 解決方案,擺脫過去 UIWebView 的老舊笨重特別是內(nèi)存占用量巨大的問題。
  • WKWebView 的優(yōu)點
    • 1.將瀏覽器內(nèi)核渲染進程提取出 App,由系統(tǒng)進行統(tǒng)一管理,這減少了相當一部分的性能損失。
    • 2.js 可以直接使用已經(jīng)事先注入 js runtime 的 js 接口給 Native 層傳值,不必再通過苦逼的 iframe 制造頁面刷新再解析自定義協(xié)議的奇怪方式。
    • 3.支持高達 60 fps 的滾動刷新率,內(nèi)置了手勢探測
    • 4.更優(yōu)雅的與JS的交互方式

常用的新增加屬性

1.estimatedProgress

  • 有了它 你不在需要苦逼的用UIwebView做假的進度條了

2.title

  • 和UIwebView原來的標題一樣,只不過不再是通過js去獲取了

3.WKWebViewConfiguration

  • 這個是配置js與wk的核心配置對象,后面會提及。

以上三個屬性 除了WKWebViewConfiguration它以外都是用KVO的形式去監(jiān)聽

    [self.wkWebView addObserver:self
                     forKeyPath:@"estimatedProgress"
                        options:NSKeyValueObservingOptionNew
                        context:nil];
    [self.wkWebView addObserver:self
                     forKeyPath:@"title"
                        options:NSKeyValueObservingOptionNew
                        context:nil];
    [self.wkWebView addObserver:self
                   forKeyPath:@"loading"
                      options:NSKeyValueObservingOptionNew
                      context:nil];

然后我們?nèi)VO處理里面去做一些事

#pragma mark - 觀察者監(jiān)聽
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"estimatedProgress"]) {
        self.progresslayer.opacity = 1;
        self.progresslayer.frame = CGRectMake(0, 0, kScreenWidth*[change[@"new"] floatValue], estimatedProgressHeight);
        NSLog(@"%@",change[@"new"]);
        if ([change[@"new"] floatValue] == 1) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                self.progresslayer.opacity = 0;

                NSString *js = @"window.alert('歡迎體驗WkWebView!');";
                [self.wkWebView evaluateJavaScript:js completionHandler:nil];

            });
        }
    }else if([keyPath isEqualToString:@"title"]){
        NSString *title = change[@"new"];
        self.titleLabel.text = title;
    }else if([keyPath isEqualToString:@"loading"]){
        //做一些加載的事
    }else{
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }

    if(!self.wkWebView.loading){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            self.progresslayer.opacity = 0;
        });
    }
}

新的代理方法

1.WKUIDelegate
與JS原生的alert、confirm、prompt交互,將彈出來的實際上是我們原生的窗口,并且要強調(diào)的是在代理里拿到數(shù)據(jù)后,必須又原生傳回給JS

#pragma mark - WKUIDelegate
// 在JS端調(diào)用alert函數(shù)時,會觸發(fā)此代理方法。
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }]];
    [self presentViewController:alert animated:YES completion:NULL];
}

// JS端調(diào)用confirm函數(shù)時,會觸發(fā)此方法
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{

}
// JS端調(diào)用prompt函數(shù)時,會觸發(fā)此方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{
}
/** webview關(guān)閉事件 */
- (void)webViewDidClose:(WKWebView *)webView;
{

}

2.WKNavigationDelegate
這里處理web導航操作

/** 在發(fā)送請求之前,決定是否跳轉(zhuǎn)
 *  與UIWebView的- (BOOL)webView:(UIWebView *)webView
 shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType相當;
 */


- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

    NSString *urlString = [[navigationAction.request URL] absoluteString];

    // urlString = [urlString stringByRemovingPercentEncoding];

    /**這這去做圖片顯示的操作
     *  網(wǎng)頁沒請求完不會進這個方法的
     *  這里還有一個小問題,就是測試了下百度的鏈接,有些圖片是進下個層級的鏈接,它也截取到了這里需要改進下
     */
    if ([urlString hasPrefix:@"myweb:imageClick:"]) {
        NSString *imageUrl = [urlString substringFromIndex:@"myweb:imageClick:".length];
        NSLog(@"imageUrl%@",imageUrl);
        if ([_images containsObject:imageUrl]) {
            NSInteger imageIndex =   [_images indexOfObject:imageUrl];

            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:[NSString stringWithFormat:@"你點了第%ld張照片",imageIndex] preferredStyle:UIAlertControllerStyleAlert];
            [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            }]];
            [self presentViewController:alert animated:YES completion:NULL];
        }
        decisionHandler(WKNavigationActionPolicyCancel);
    }else{
        decisionHandler(WKNavigationActionPolicyAllow);
    }

}
/// 網(wǎng)頁加載完成之后調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {

    [self getImageUrlByJS:webView Imageblock:^(NSArray *images) {
        NSLog(@"獲得的圖片總數(shù):%@",images);
        _images = images;
    }];
    [self refreshbackBottomState];
}

/** 失敗*/
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
{
    [self refreshbackBottomState];
}

/** 在收到響應后,決定是否跳轉(zhuǎn) */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
{
    decisionHandler(WKNavigationResponsePolicyAllow);
}

/** 開始導航跳轉(zhuǎn)時會回調(diào)*/
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}

/** 頁面內(nèi)容到達main frame時回調(diào)*/
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}

// 對于HTTPS的都會觸發(fā)此代理,如果不要求驗證,傳默認就行
// 如果需要證書驗證,與使用AFN進行HTTPS證書驗證是一樣的
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler {
    NSLog(@"%s", __FUNCTION__);
  completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

// 9.0才能使用,web內(nèi)容處理中斷時會觸發(fā)
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    NSLog(@"%s", __FUNCTION__);
}

3.WKScriptMessageHandler
這就是js與OC交互的處理位置的地方

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
{
    if ([message.name isEqualToString:@"Xxx"]) {
        /**
         *打印所傳過來的參數(shù),只支持NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull類型
         */
        NSLog(@"%@", message.body);
    }
}

js與wk內(nèi)容的交互

  • 在創(chuàng)建WKWebView之前,需要先創(chuàng)建配置對象,用來做一些配置
  • 簡單總結(jié)就是幾點:
    WK不再需要像webView那樣去截取Url了
    • 配置 WKWebViewConfiguration
    • 注入js對象
    • 去WKScriptMessageHandler代理中接收,然后處理JS傳遞過來的事件和參數(shù)
/** 偏好設(shè)置也沒有必須去修改它,都使用默認的就可以了,除非你真的需要修改
 *   WKUserContentController是用于給JS注入對象的,注入對象后,JS端就可以使用:
 *   window.webkit.messageHandlers.<name>.postMessage(<messageBody>)來調(diào)用發(fā)送數(shù)據(jù)給iOS端
 *   比如:window.webkit.messageHandlers.Xxx.postMessage({body: '傳數(shù)據(jù)'});
 */
- (WKWebViewConfiguration *)config
{
    if (_config == nil) {
        WKWebViewConfiguration *config =[[WKWebViewConfiguration alloc]init];
        // 設(shè)置偏好設(shè)置
        config.preferences = [[WKPreferences alloc] init];
        // 默認為0
        config.preferences.minimumFontSize = 10;
        // 默認認為YES
        config.preferences.javaScriptEnabled = YES;
        // 在iOS上默認為NO,表示不能自動通過窗口打開
        config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
        /**
         *  這個name注入JS對象名稱Xxxx,當JS通過Xxx來調(diào)用時,
         *  JS對象可以注入多個,用于不同的功能
         *  我們可以在WKScriptMessageHandler代理中接收到
         */
        [config.userContentController addScriptMessageHandler:self name:@"Xxx"];
        _config = config;
    }
    return _config;
}

wk與js交互

主要就是通過

  • (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler;來實現(xiàn)
WK.png
簡單舉例
                NSString *js = @"window.alert('歡迎體驗WkWebView!');";
                [self.wkWebView evaluateJavaScript:js completionHandler:nil];
                對應的就會觸發(fā)WK alert代理

最后編輯于
?著作權(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)容