隨着IOS開發的應用,對於網頁嵌入也愈來愈多了,在IOS 8以前咱們使用UIWebView展現詳情頁,自從IOS 8以後就出現了WKWebView,相比UIWebView,WKWebView優化了較多的體驗。下面將講述WKWebView的知識點以及運用,大概須要花費10-20分鐘時間,但願對你們有所幫助!!!java
WKWebView採用跨進程方案,Nitro JS解析器,高達60fps的刷新率,理論上性能和Safari比肩,並且對H5也實現了高度支持。web
1.WKWebView的內存開銷比UIWebView小不少json
2.內置手勢服務器
3.支持了更多的HTML5特性weex
4.有Safari相同的JavaScript引擎app
5.提供經常使用的屬性,如加載網頁進度的estimatedProgress屬性dom
下面來對比UIWebView和WKWebView的流程區別(左邊是UIWebView,右邊是WKWebView)ide
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細:post
#請求數據時發生的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;
- (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初始化進行設置。
#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); }
有時候咱們加載的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 }
獲取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>
有時候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的信息。
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注入。
動態注入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的理解有所提升,謝謝!!!