WKWebView讲解与使用
本人已迁移博客至掘进,以后会在掘进平台更新最新的文章也会有更多的干货,欢迎大家关注!!!https://juejin.im/user/588993965333309
随着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的理解有所提高,谢谢!!!