WKWebView講解與使用


隨着IOS開發的應用,對於網頁嵌入也越來越多了,在IOS 8之前我們使用UIWebView展示詳情頁,自從IOS 8之后就出現了WKWebView,相比UIWebView,WKWebView優化了較多的體驗。下面將講述WKWebView的知識點以及運用,大概需要花費10-20分鍾時間,希望對大家有所幫助!!!

 

一、WKWebView優點

WKWebView采用跨進程方案,Nitro JS解析器,高達60fps的刷新率,理論上性能和Safari比肩,而且對H5也實現了高度支持。

1.WKWebView的內存開銷比UIWebView小很多

2.內置手勢

3.支持了更多的HTML5特性

4.有Safari相同的JavaScript引擎

5.提供常用的屬性,如加載網頁進度的estimatedProgress屬性

 

下面來對比UIWebView和WKWebView的流程區別(左邊是UIWebView,右邊是WKWebView)

WKWebView的流程粒度更加細致,不但在不但在請求的時候會詢問WKWebView是否請求數據,還會在返回數據之后詢問WKWebView是否加載數據。

#請求數據的時候詢問
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
#返回數據的時候詢問
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;

 

在流程中,WKWebView返回的錯誤粒度也比UIWebView細:

#請求數據時發生的error
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
#請求之后加載H5發生的error
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;

二、WKWebView基本使用

2.1 基本使用

2.1.1 使用WKWebView引用頭文件

- (void)setupWebview{
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    config.selectionGranularity = WKSelectionGranularityDynamic;
    config.allowsInlineMediaPlayback = YES;
    WKPreferences *preferences = [WKPreferences new];
    //是否支持JavaScript
    preferences.javaScriptEnabled = YES;
    //不通過用戶交互,是否可以打開窗口
    preferences.javaScriptCanOpenWindowsAutomatically = YES;
    config.preferences = preferences;
    WKWebView *webview = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, KScreenHeight - 64) configuration:config];
    [self.view addSubview:webview];
     
    /* 加載服務器url的方法*/
    NSString *url = @"https://www.baidu.com";
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    [webview loadRequest:request];
     
    webview.navigationDelegate = self;
    webview.UIDelegate = self;
}

WKWebViewConfiguration和WKPreferences中很多屬性對WebView初始化進行設置。

2.1.2 下面遵循協議和實現的協議方法:

#pragma mark - WKNavigationDelegate
/* 頁面開始加載 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
}
/* 開始返回內容 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
     
}
/* 頁面加載完成 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
     
}
/* 頁面加載失敗 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
     
}
/* 在發送請求之前,決定是否跳轉 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    //允許跳轉
    decisionHandler(WKNavigationActionPolicyAllow);
    //不允許跳轉
    //decisionHandler(WKNavigationActionPolicyCancel);
}
/* 在收到響應后,決定是否跳轉 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
     
    NSLog(@"%@",navigationResponse.response.URL.absoluteString);
    //允許跳轉
    decisionHandler(WKNavigationResponsePolicyAllow);
    //不允許跳轉
    //decisionHandler(WKNavigationResponsePolicyCancel);
}

 

三、WKWebView開發細節

3.1 url 中文處理

有時候我們加載的url中出現了中文,需要我們手動轉碼,但是同時又要保證URL中的特殊字符保持不變,那么我們可以使用下面的方法(方法)

- (NSURL *)url{
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
    return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))];
#pragma clang diagnostic pop
}

 

3.2 獲取h5中的標題以及添加進度條

獲取h5中的標題和添加進度條放在一起展示看起來更明朗一點,在初始化webView,添加兩個觀察者分別用來監聽webView的estimateProgress和title屬性

webview.navigationDelegate = self;
webview.UIDelegate = self;
     
[webview addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
[webview addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];

添加創建進度條,並添加進度條圖層屬性:

@property (nonatomic,weak) CALayer *progressLayer;

-(void)setupProgress{
    UIView *progress = [[UIView alloc]init];
    progress.frame = CGRectMake(0, 0, KScreenWidth, 3);
    progress.backgroundColor = [UIColor  clearColor];
    [self.view addSubview:progress];
     
    CALayer *layer = [CALayer layer];
    layer.frame = CGRectMake(0, 0, 0, 3);
    layer.backgroundColor = [UIColor greenColor].CGColor;
    [progress.layer addSublayer:layer];
    self.progressLayer = layer;
}

實現觀察者的回調方法:

#pragma mark - KVO回饋
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<nskeyvaluechangekey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"estimatedProgress"]) {
        self.progressLayer.opacity = 1;
        if ([change[@"new"] floatValue] <[change[@"old"] floatValue]) {
            return;
        }
        self.progressLayer.frame = CGRectMake(0, 0, KScreenWidth*[change[@"new"] floatValue], 3);
        if ([change[@"new"]floatValue] == 1.0) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                self.progressLayer.opacity = 0;
                self.progressLayer.frame = CGRectMake(0, 0, 0, 3);
            });
        }
    }else if ([keyPath isEqualToString:@"title"]){
        self.title = change[@"new"];
    }
}</nskeyvaluechangekey,id>

 

3.3 添加userAgent信息

有時候h5的歐版需要我們為WebView的請求添加userAgent,用來識別操作系統等一下信息,但是如果每次用到webView都要添加一次的話會比較麻煩,下面是一種解決問題的辦法

在Appdelegate中添加一個WKWebView的屬性,啟動app時直接為該屬性添加userAgent:

- (void)setUserAgent {
    _webView = [[WKWebView alloc] initWithFrame:CGRectZero];
    [_webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
        if (error) { return; }
        NSString *userAgent = result;
        if (![userAgent containsString:@"/mobile-iOS"]) {
            userAgent = [userAgent stringByAppendingString:@"/mobile-iOS"];
            NSDictionary *dict = @{@"UserAgent": userAgent};
            [TKUserDefaults registerDefaults:dict];
        }
    }];
}

這樣一來,在app創建webView時存在了我們添加的userAgent的信息。

 

3.4 JS調用OC

js會通過以下方法調用原生方法

window.webkit.messageHandlers.<#對象名#>.postMessage(<#參數#>)

在原生中我們只要實現WKScriptMessageHandler的代理方法就可以了,值得注意的是參數name需要與上述代碼中對象名一致。

// 添加scriptMessageHandler
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

最后在

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

在這個方法中獲取做下判斷,響應對應的方法即可:

// 初始化WKWebView,在實例化WKWebViewConfiguration對象的時候我們同時添加scriptMessageHandler
 //進行配置控制器
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
        //實例化對象
        configuration.userContentController = [WKUserContentController new];
        //調用JS方法
        [configuration.userContentController addScriptMessageHandler:self name:@"btnClick"];

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:@"btnClick"]) {
        NSDictionary *jsData = message.body;
        NSLog(@"%@", message.name, jsData);
        //讀取js function的字符串
        NSString *jsFunctionString = jsData[@"result"];
        //拼接調用該方法的js字符串(convertDictionaryToJson:方法將NSDictionary轉成JSON格式的字符串)
        NSString *jsonString = [NSDictionary convertDictionaryToJson:@{@"test":@"123", @"data":@"666"}];
        NSString *jsCallBack = [NSString stringWithFormat:@"(%@)(%@);", jsFunctionString, jsonString];
        //執行回調
        [self.weWebView evaluateJavaScript:jsCallBack completionHandler:^(id _Nullable result, NSError * _Nullable error) {
            if (error) {
                NSLog(@"err is %@", error.domain);
            }
        }];
    }
}

以上需要注意的是,由於message的body只能是NSNumber,NSString,NSDate,NSArray,NSDictionary,NSNull這幾種類型,我們無法將js函數直接原生,在需要進行回調的環境下,我們將js回調函數轉為String后再傳給原生,再由原生獲取后進行回調操作,實際上這是已經進行了動態js注入。

 

3.5 OC調用JS

動態注入js方法就比較簡單了,我們只要實現相應的方法就可以。

- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

下面有一段示例代碼

// 此處是設置需要調用的js方法以及將對應的參數傳入,需要以字符串的形式
NSString *jsFounction = [NSString stringWithFormat:@"getAppConfig('%@')", APP_CHANNEL_ID];
// 調用API方法
    [self.weexWebView evaluateJavaScript:jsFounction completionHandler:^(id object, NSError * _Nullable error) {
        NSLog(@"obj:%@---error:%@", object, error);
    }];

 

以上就是WKWebView的基本使用,希望大家對WKWebView的理解有所提高,謝謝!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM