iOS WKWebView的使用

  • WKWebView需要iOS9或更高版本

優(yōu)點(diǎn)

1.多進(jìn)程,在app的主進(jìn)程之外執(zhí)行
2.使用更快的Nitro JavaScript引擎
3.異步執(zhí)行處理JavaScript
4.消除某些觸摸延遲
5.支持服務(wù)端的身份校驗(yàn)
6.支持對錯(cuò)誤的自簽名安全證書和證書進(jìn)行身份驗(yàn)證

問題

1.需要iOS9或更高版本(WKWebView在iOS8引入,但是很多功能,支持比較全面在iOS9以后的版本)
2.不支持通過AJAX請求本地存儲(chǔ)的文件
3.不支持"Accept Cookies"的設(shè)置
4.不支持"Advanced Cache Settings"(高級緩存設(shè)置)
5.App退出會(huì)清除HTML5的本地存儲(chǔ)的數(shù)據(jù)
6.不支持記錄WebKit的請求
7.不能進(jìn)行截屏操作

具體翻譯文參考:WKWebView相比于UIWebView瀏覽器之間內(nèi)核引擎的區(qū)別
原文 WKWebView: Differences from UIWebView browsing engine

一、WKWebView的基本初始化

  • 需要引入 #import <WebKit/WebKit.h>
- (WKWebView *)wkWebview
{
    if (!_wkWebview) {
        // 0.網(wǎng)頁配置對象
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        // 1.原生與JS交互管理
        WKUserContentController *userContentController = [[WKUserContentController alloc] init];
        
        /// 解決循環(huán)引用
        // 0.在viewdisAppear方法中
//        [self.wkWebview.configuration.userContentController removeScriptMessageHandlerForName:@"ScanAction"];
//        [userContentController addScriptMessageHandler:self name:@"ScanAction"];
        
        // 1.繼承系統(tǒng)的NSProxy
//        [userContentController addScriptMessageHandler:(id<WKScriptMessageHandler>)[XPZ_Proxy proxyWithTarget:self] name:@"ScanAction"];
        
        // 2.自定義NSProxy
//        [userContentController addScriptMessageHandler:(id<WKScriptMessageHandler>)[XPZ_CustomProxy proxyWithTarget:self] name:@"ScanAction"];
        
        // 3.自定義WKScriptMessageHandler
        XPZ_WKWeakScriptMessageHandler *scriptMessageHandle = [[XPZ_WKWeakScriptMessageHandler alloc] initWithScriptMessageHandlerWith:self];
        [userContentController addScriptMessageHandler:scriptMessageHandle name:@"ScanAction"];
        // 添加
        config.userContentController = userContentController;
        
        // 3.WKWebview設(shè)置
        WKPreferences *prefer = [[WKPreferences alloc] init];
        //設(shè)置是否支持javaScript 默認(rèn)是支持的
        prefer.javaScriptEnabled = true;
        // /最小字體大小
        prefer.minimumFontSize = 40.0;
        // // 在iOS上默認(rèn)為NO,表示是否允許不經(jīng)過用戶交互由javaScript自動(dòng)打開窗口
        prefer.javaScriptCanOpenWindowsAutomatically = true;
        // 添加
        config.preferences = prefer;
        
        // 是使用h5的視頻播放器在線播放, 還是使用原生播放器全屏播放
        config.allowsInlineMediaPlayback = YES;
        //設(shè)置視頻是否需要用戶手動(dòng)播放  設(shè)置為NO則會(huì)允許自動(dòng)播放
        config.mediaTypesRequiringUserActionForPlayback = YES;
        //設(shè)置是否允許畫中畫技術(shù) 在特定設(shè)備上有效
        config.allowsPictureInPictureMediaPlayback = YES;
        //設(shè)置請求的User-Agent信息中應(yīng)用程序名稱 iOS9后可用
        config.applicationNameForUserAgent = @"ChinaDailyForiPad";
        
        _wkWebview = [[WKWebView alloc] initWithFrame:self.view.frame configuration:config];
        _wkWebview.UIDelegate = self;
        _wkWebview.navigationDelegate = self;
        // 是否允許手勢左滑返回上一級, 類似導(dǎo)航控制的左滑返回
        _wkWebview.allowsBackForwardNavigationGestures = YES;
        //可返回的頁面列表, 存儲(chǔ)已打開過的網(wǎng)頁
        WKBackForwardList * backForwardList = [_wkWebview backForwardList];
        //頁面后退
        [_wkWebview goBack];
        //頁面前進(jìn)
        [_wkWebview goForward];
        //刷新當(dāng)前頁面
        [_wkWebview reload];
        
        [self.view addSubview:_wkWebview];
    }
    return _wkWebview;
}
  • 主要說下WKUserContentController:這個(gè)類主要用來做native與JavaScript的交互管理,依靠 WKScriptMessageHandler 協(xié)議
  • 主要用到以下方法
// 添加腳本信息
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
// 移除腳本信息
- (void)removeScriptMessageHandlerForName:(NSString *)name;
// 例:
[userContentController addScriptMessageHandler:scriptMessageHandle name:@"ScanAction"];
  • JS中代碼
function scanClick() {
                window.webkit.messageHandlers.ScanAction.postMessage(null);
            }
  • 對應(yīng)的協(xié)議方法,專門用來處理監(jiān)聽JavaScript方法從而調(diào)用原生OC方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
//    message.body  --  Allowed types are NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull.
//    NSDictionary *bodyParam = (NSDictionary*)message.body;
//    NSString *func = [bodyParam objectForKey:@"function"];
//    
//    NSLog(@"MessageHandler Name:%@", message.name);
//    NSLog(@"MessageHandler Body:%@", message.body);
//    NSLog(@"MessageHandler Function:%@",func);
    if ([message.name isEqualToString:@"ScanAction"]) {
        NSLog(@"掃一掃");
    }
    
    // 將結(jié)果返回給js
    NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"廣東省深圳市南山區(qū)學(xué)府路XXXX號"];
    [self.wkWebview evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@----%@",result, error);
    }];
}
  • ??在使用 addScriptMessageHandler: 方法時(shí)會(huì)造成內(nèi)存泄漏

[configuration.userContentController addScriptMessageHandler:self name:name]

這里 userContentController 持有了self ,然后 userContentController 又被configuration持有,最終被wkwebview持有,然后wkwebview是self的一個(gè)成員變量,所以self也持有 self,所以就造成了循環(huán)引用,導(dǎo)致界面不會(huì)被釋放

  • 解決辦法, 以下提供了4種方案
  1. 使用 removeScriptMessageHandlerForName:
- (void)viewWillDisappear:(BOOL)animated
{
   [super viewWillDisappear:animated];
   [self.wkWebview.configuration.userContentController removeScriptMessageHandlerForName:@"ScanAction"];
}
  1. 創(chuàng)建繼承系統(tǒng)的NSProxy的類
   [userContentController addScriptMessageHandler:(id<WKScriptMessageHandler>)[XPZ_Proxy proxyWithTarget:self] name:@"ScanAction"];

   // NSProxy類中的代碼

  // XPZ_Proxy.h 中代碼
@interface XPZ_Proxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
   
  // XPZ_Proxy.m 中代碼
@implementation XPZ_Proxy
+ (instancetype)proxyWithTarget:(id)target
{
   // NSProxy對象不需要調(diào)用init,因?yàn)樗緛砭蜎]有init方法
   XPZ_Proxy *proxy = [XPZ_Proxy alloc];
   proxy.target = target;
   return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
   return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
   [invocation invokeWithTarget:self.target];
}
@end

  1. 創(chuàng)建自定義的NSProxy類
 // XPZ_CustomProxy.h 中代碼
@interface XPZ_CustomProxy : NSObject
@property (nonatomic, weak) id target;
+ (instancetype)proxyWithTarget:(id)target;
@end

// XPZ_CustomProxy.m 中代碼
@implementation XPZ_CustomProxy
+ (instancetype)proxyWithTarget:(id)target
{
    XPZ_CustomProxy *proxy = [[XPZ_CustomProxy alloc] init];
    proxy.target = target;
    return proxy;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return self.target;
}
@end

  1. 自定義WKScriptMessageHandler
//XPZ_WKWeakScriptMessageHandler.h 中代碼
@interface XPZ_WKWeakScriptMessageHandler : NSObject <WKScriptMessageHandler>

- (instancetype)initWithScriptMessageHandlerWith:(id<WKScriptMessageHandler>)scriptMessageHandler;

@property (nonatomic, weak, readonly) id<WKScriptMessageHandler> scriptMessageHandler;
@end
  
// XPZ_WKWeakScriptMessageHandler.m 中代碼
- (instancetype)initWithScriptMessageHandlerWith:(id<WKScriptMessageHandler>)scriptMessageHandler
{
    self = [super init];
    if (self) {
        _scriptMessageHandler = scriptMessageHandler;
    }
    return self;
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    [self userContentController:userContentController didReceiveScriptMessage:message];
}
@end

二、WKWebView的代理方法

1.WKUIDelegate

#pragma mark WKUIDelegate
// 警告
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction * action = [UIAlertAction actionWithTitle:@"確認(rèn)" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }];
    [alert addAction:action];
    
    [self presentViewController:alert animated:YES completion:nil];
}
// 確認(rèn)框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
    //    DLOG(@"msg = %@ frmae = %@",message,frame);
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }])];
    [alertController addAction:([UIAlertAction actionWithTitle:@"確認(rèn)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}
// 輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = defaultText;
    }];
    [alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(alertController.textFields[0].text?:@"");
    }])];


    [self presentViewController:alertController animated:YES completion:nil];
}
// 創(chuàng)建一個(gè)新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{
    return [[WKWebView alloc]init];
}

2.WKNavigationDelegate

#pragma mark - WKNavigationDelegate
/*
 WKNavigationDelegate主要處理一些跳轉(zhuǎn)、加載處理操作,WKUIDelegate主要處理JS腳本,確認(rèn)框,警告框等
 */

// 頁面開始加載時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
}

// 頁面加載失敗時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    [self.progressView setProgress:0.0f animated:NO];
}

// 當(dāng)內(nèi)容開始返回時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    
}

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

    // 設(shè)置字體
//    NSString *fontFamilyStr = @"document.getElementsByTagName('body')[0].style.fontFamily='Arial';";
//    [webView evaluateJavaScript:fontFamilyStr completionHandler:nil];
//    //設(shè)置顏色
//    [ webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextFillColor= '#9098b8'" completionHandler:nil];
//    //修改字體大小
//    [ webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '200%'"completionHandler:nil];
}

//提交發(fā)生錯(cuò)誤時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    [self.progressView setProgress:0.0f animated:NO];
}

// 接收到服務(wù)器跳轉(zhuǎn)請求即服務(wù)重定向時(shí)之后調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    
}
// 在發(fā)送請求之前,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

    NSString *href = navigationAction.request.URL.absoluteString;
    if ([href hasPrefix:@"http"]||[href hasPrefix:@"https"]) {
        
    }
    if ([href hasPrefix:@"config:"]) {
        // parse and get json, then ...
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

3.WKScriptMessageHandler

#pragma mark WKScriptMessageHandler
// 用來處理監(jiān)聽JavaScript方法從而調(diào)用原生OC方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
//    message.body  --  Allowed types are NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull.
//    NSDictionary *bodyParam = (NSDictionary*)message.body;
//    NSString *func = [bodyParam objectForKey:@"function"];
//    
//    NSLog(@"MessageHandler Name:%@", message.name);
//    NSLog(@"MessageHandler Body:%@", message.body);
//    NSLog(@"MessageHandler Function:%@",func);
    if ([message.name isEqualToString:@"ScanAction"]) {
        NSLog(@"掃一掃");
    }
    
    // 將結(jié)果返回給js
    NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"廣東省深圳市南山區(qū)學(xué)府路XXXX號"];
    [self.wkWebview evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@----%@",result, error);
    }];
}

三、JS與OC的交互

1.JavaScriptCore

  • UIWebView
// 創(chuàng)建JSContext
    JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    self.context = context;
    // 調(diào)用系統(tǒng)相機(jī)
    context[@"iOSCamera"] = ^(){
        dispatch_async(dispatch_get_main_queue(), ^{
            
     });
        return @"調(diào)用相機(jī)";
    };
    
    // callWithArguments:
    JSValue *labelAction = self.context[@"picCallback"];
    [labelAction callWithArguments:@[@"參數(shù)"]];

在從UIWebView過度到WkWebView,我們還向之前使用UIWebView那樣,在頁面加載完成后,獲取JSContext上下文
會(huì)發(fā)現(xiàn)在 self.jsContext = [_wkWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; 這里崩了,原因就是 WKWebView 不支持 JavaScriptCore 的方式, 但提供 messagehandler 的方式為JavaScript與OC通信;
到這里我們會(huì)想如何拿到WKWebView JsContext上下文,可是很遺憾我們無法獲取上下文,因?yàn)椴季趾蚃avaScript是在另一個(gè)進(jìn)程上處理的。

2.MessageHandler

這個(gè)方法上面提到過,主要是依靠WKScriptMessageHandler協(xié)議類和WKUserContentController兩個(gè)類:WKUserContentController對象負(fù)責(zé)注冊JS方法,設(shè)置處理接收J(rèn)S方法的代理,代理遵守WKScriptMessageHandler,實(shí)現(xiàn)捕捉到JS消息的回調(diào)方法。

  • 上面是JS調(diào)用OC,補(bǔ)充一下OC調(diào)用JS方法
    // 將結(jié)果返回給js
    NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"廣東省深圳市南山區(qū)學(xué)府路XXXX號"];
    [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@----%@",result, error);
    }];
  • JS中代碼
 function setLocation(location) {
                asyncAlert(location);
                document.getElementById("returnValue").value = location;
            }

3.WebViewJavascriptBridge

通過CocoaPods集成
WebViewJavascriptBridge
在工程的Podfile里面添加以下代碼:

   pod 'WebViewJavascriptBridge'
  1. 引入頭文件
    #import <WKWebViewJavascriptBridge.h>
  1. 初始化 WKWebViewJavascriptBridge
_webViewBridge = [WKWebViewJavascriptBridge bridgeForWebView:_wkWebview];
[_webViewBridge setWebViewDelegate:self];
  1. 注冊并調(diào)用js方法
 // js調(diào)用原生
    [_webViewBridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSLog(@"data : %@",data);
        responseCallback(@"12345678");
    }];
    
    // 原生調(diào)用js方法
     //    // 如果不需要參數(shù),不需要回調(diào),使用這個(gè)
    //    [_webViewBridge callHandler:@"testJSFunction"];
    //    // 如果需要參數(shù),不需要回調(diào),使用這個(gè)
    //    [_webViewBridge callHandler:@"testJSFunction" data:@"一個(gè)字符串"];
    // 如果既需要參數(shù),又需要回調(diào),使用這個(gè)
    [_webViewBridge callHandler:@"testJSFunction" data:@"一個(gè)字符串" responseCallback:^(id responseData) {
        NSLog(@"調(diào)用完JS后的回調(diào):%@",responseData);
    }];
  1. 復(fù)制并粘貼到您的 JS 中:setupWebViewJavascriptBridge
   function setupWebViewJavascriptBridge(callback) {
   if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
   if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
   window.WVJBCallbacks = [callback];
   var WVJBIframe = document.createElement('iframe');
   WVJBIframe.style.display = 'none';
   WVJBIframe.src = 'https://__bridge_loaded__';
   document.documentElement.appendChild(WVJBIframe);
   setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
  1. 最后調(diào)用 setupWebViewJavascriptBridge
    WebViewJavascriptBridge.callHandler('scanClick', {'foo': 'bar'}, function(response) {
                    alert('掃描結(jié)果:' + response);
                    document.getElementById("returnValue").value = response;
                })
                
    setupWebViewJavascriptBridge(function(bridge) {
                 bridge.registerHandler('testJSFunction', function(data, responseCallback) {
                    alert('JS方法被調(diào)用:'+data);
                    responseCallback('js執(zhí)行過了');
                 })
            })

4.攔截URL

#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSURL *URL = navigationAction.request.URL;
    NSString *scheme = [URL scheme];
    if ([scheme isEqualToString:@""]) {
        
        // 在這里解析URL
        // 需要調(diào)用js方法 還可以通過以下這種方法插入js例:
        // 將結(jié)果返回給js
    // NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"廣東省深圳市南山區(qū)學(xué)府路XXXX號"];
    // [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
//        NSLog(@"%@----%@",result, error);
//    }];
                
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

四.加載進(jìn)度條和title的監(jiān)聽

注意:
iOS9之前,被觀察這對觀察者之間是unsafe_unretain引用,觀察者釋放之后會(huì)造成野指針
而iOS9 之后是weak引用關(guān)系,對象釋放之后,指針也釋放,不會(huì)崩潰

通知NSNotification在注冊者被回收時(shí)需要手動(dòng)移除,是一直以來的使用準(zhǔn)則。原因是在MRC時(shí)代,通知中心持有的是注冊者的unsafe_unretained指針,在注冊者被回收時(shí)若不對通知進(jìn)行手動(dòng)移除,則指針指向被回收的內(nèi)存區(qū)域,成為野指針。這時(shí)再發(fā)送通知,便會(huì)造成crash。而在iOS 9以后,通知中心持有的是注冊者的weak指針,這時(shí)即使不對通知進(jìn)行手動(dòng)移除,指針也會(huì)在注冊者被回收后自動(dòng)置空。我們知道,向空指針發(fā)送消息是不會(huì)有問題的。

?? 但是有一個(gè)例外。如果用- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));這個(gè)API來注冊通知,可以直接傳入block類型參數(shù)。使用這個(gè)API會(huì)導(dǎo)致注冊者被系統(tǒng)retain,因此仍然需要像以前一樣手動(dòng)移除通知,同時(shí)這個(gè)block類型參數(shù)也需注意避免循環(huán)引用。

  • 所以不再需要寫移除觀察者方法

    [self.wkWebview removeObserver:self
                    forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
    [self.wkWebview removeObserver:self
                  forKeyPath:NSStringFromSelector(@selector(title))];
    
  • 添加監(jiān)測網(wǎng)頁加載進(jìn)度的觀察者

    [self.wkWebview addObserver:self
                   forKeyPath:@"estimatedProgress"
                      options:0
                      context:nil];
    //添加監(jiān)測網(wǎng)頁標(biāo)題title的觀察者
    [self.wkWebview addObserver:self
                   forKeyPath:@"title"
                      options:NSKeyValueObservingOptionNew
                      context:nil];
                      
    #pragma mark kvo 監(jiān)聽進(jìn)度 必須實(shí)現(xiàn)此方法
    -(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary<NSKeyValueChangeKey,id>     *)change
                      context:(void *)context{
    if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))]
        && object == _wkWebview) {
        NSLog(@"網(wǎng)頁加載進(jìn)度 = %f",_wkWebview.estimatedProgress);
        self.progressView.progress = _wkWebview.estimatedProgress;
        if (_wkWebview.estimatedProgress >= 1.0f) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                self.progressView.progress = 0;
            });
        }
    }else if([keyPath isEqualToString:@"title"]
             && object == _wkWebview){
        self.navigationItem.title = _wkWebview.title;
    }else{
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                              context:context];
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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