WKWebView是苹果在iOS 8之后推出的框架WebKit中的浏览器控件,其加载速度比UIWebView快了许多, 但内存占用率却下降很多,也解决了加载网页时的内存泄露问题。现在的项目大多数只需适配到iOS 8, 所以用WKWebView来替换项目中的UIWebView是很有必要的。
WKWebView常用到的几个类:
- WKWebView
- WKWebViewConfiguration
- WKUserScript
- WKUserContentController
- WKWebsiteDataStore:清理缓存
以及3个代理:
- WKUIDelegate
- WKNavigationDelegate
- WKScriptMessageHandler
WKWebView常用属性
WKUIDelegate 代理方法
这个代理方法, 主要是用来处理使用系统的弹框来替换JS中的一些弹框的,比如: 警告框, 选择框, 输入框, 主要使用的是下面三个代理方法:
// 创建一个新的WebView(标签带有 target='_blank' 时,导致WKWebView无法加载点击后的网页的问题。) - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { // 接口的作用是打开新窗口委托 WKFrameInfo *frameInfo = navigationAction.targetFrame; if (![frameInfo isMainFrame]) { [webView loadRequest:navigationAction.request]; } return nil; }
/** 警告框 在JS端调用alert函数时,会触发此代理方法。 JS端调用alert时所传的数据可以通过message拿到 在原生得到结果后,需要回调JS,是通过completionHandler回调 */ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { NSLog(@"%s", __FUNCTION__); UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:@"JS调用alert" preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(); }]]; [self presentViewController:alert animated:YES completion:NULL]; NSLog(@"%@", message); }
/** 确认框 JS端调用confirm函数时,会触发此方法 通过message可以拿到JS端所传的数据 在iOS端显示原生alert得到YES/NO后 通过completionHandler回调给JS端 */ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler { NSLog(@"%s", __FUNCTION__); UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"confirm" message:@"JS调用confirm" preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(YES); }]]; [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { completionHandler(NO); }]]; [self presentViewController:alert animated:YES completion:NULL]; NSLog(@"%@", message); }
/** 输入框 JS端调用prompt函数时,会触发此方法 要求输入一段文本 在原生输入得到文本内容后,通过completionHandler回调给JS */ - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler { NSLog(@"%s", __FUNCTION__); NSLog(@"%@", prompt); UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"textinput" message:@"JS调用输入框" preferredStyle:UIAlertControllerStyleAlert]; [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { textField.textColor = [UIColor redColor]; }]; [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler([[alert.textFields lastObject] text]); }]]; [self presentViewController:alert animated:YES completion:NULL]; }
WKNavigationDelegate代理方法
WKNavigationDelegate:最常用,和UIWebViewDelegate功能类似,该代理提供的方法,可以用来追踪加载过程(页面开始加载、加载完成、加载失败)、决定是否执行跳转。下面会对函数做简单的说明,并用数字标出调用的先后次序:11-22-33-44-55。
用来追踪加载过程(页面开始加载、加载完成、加载失败)的方法:
/**
* 22,页面开始加载时调用
*
* @param webView 实现该代理的webview
* @param navigation 当前navigation
*/
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
NSLog(@"%s", __FUNCTION__);
}
/**
* 44,当内容开始返回时调用
*
* @param webView 实现该代理的webview
* @param navigation 当前navigation
*/
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
NSLog(@"%s", __FUNCTION__);
}
/**
* 55,页面加载完成之后调用
*
* @param webView 实现该代理的webview
* @param navigation 当前navigation
*/
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
NSLog(@"%s", __FUNCTION__);
}
/**
* 4,加载失败时调用
*
* @param webView 实现该代理的webview
* @param navigation 当前navigation
* @param error 错误
*/
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"%s", __FUNCTION__);
}
页面跳转的代理方法:
/**
* 1,接收到服务器跳转请求之后调用
*
* @param webView 实现该代理的webview
* @param navigation 当前navigation
*/
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
NSLog(@"%s", __FUNCTION__);
}
/**
* 11,在发送请求之前,决定是否跳转
*
* @param webView 实现该代理的
* @param navigationAction 当前navigation
* @param decisionHandler 是否调转block
*/
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString *hostname = navigationAction.request.URL.host.lowercaseString;
if (navigationAction.navigationType == WKNavigationTypeLinkActivated
&& ![hostname containsString:@".baidu.com"]) {
// 对于跨域,需要手动跳转
[[UIApplication sharedApplication] openURL:navigationAction.request.URL];
// 不允许web内跳转
decisionHandler(WKNavigationActionPolicyCancel);
} else {
self.progressView.alpha = 1.0;
decisionHandler(WKNavigationActionPolicyAllow);
}
NSLog(@"%s", __FUNCTION__);
}
/**
* 33,在收到响应后,决定是否跳转。在收到服务器的响应头,根据response相关信息,决定是否跳转。decisionHandler必须调用,来决定是否跳转,参数WKNavigationActionPolicyCancel取消跳转,WKNavigationActionPolicyAllow允许跳转。
*
* @param webView 实现该代理的webview
* @param navigationResponse 当前navigation
* @param decisionHandler 是否跳转block
*/
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
// NSLog(@"%@",navigationResponse.response);
// 如果响应的地址是百度,则允许跳转
// if ([navigationResponse.response.URL.host.lowercaseString isEqual:@"www.baidu.com"]) {
//
// // 允许跳转
// decisionHandler(WKNavigationResponsePolicyAllow);
// return;
// }
// // 不允许跳转
// decisionHandler(WKNavigationResponsePolicyCancel);
decisionHandler(WKNavigationResponsePolicyAllow);
}
其他一些方法
// 当main frame最后下载数据失败时,会回调
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
}
// 用于授权验证的API,与AFN、UIWebView的授权验证API是一样的
// 对于HTTPS的都会触发此代理,如果不要求验证,传默认就行
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler{
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling ,nil);
}
/**
9.0才能使用,web内容处理中断时会触发
当web content处理完成时,会回调
在 UIWebView 上当内存占用太大的时候,App Process 会 crash,在 WKWebView 上当总体的内存占用比较大的时候,WebContent Process 会 crash,从而出现白屏现象。
iOS 9以后 WKNavigtionDelegate 新增了一个回调函数:
*/
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
[webView reload];
NSLog(@"进程终止");
}
WKWebView 执行JS代码
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSString *jsString = [NSString stringWithFormat:@"showSatisfaction()"]; [self.wkWebView evaluateJavaScript:jsString completionHandler:^(id response, NSError * error) { NSLog(@"error: %@", error.description); NSLog(@"response: %@", response); //js返回0:js控制跳出弹框 1:返回上级页面 if ([response intValue] == 1) { [self.navigationController popViewControllerAnimated:YES]; } }]; });
WKScriptMessageHandler代理方法
WKScriptMessage有两个关键属性name 和 body。因为我们给每一个OC方法取了一个name,所以就可以根据name 来区分执行不同的方法。body 中存着JS 要给OC 传的参数。// 从web界面中接收到一个脚本时调用 -(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ //JS调用OC方法 //message.boby就是JS里传过来的参数 NSLog(@"body:%@",message.body); if ([message.name isEqualToString:@"Share"]) { [self ShareWithInformation:message.body]; } else if ([message.name isEqualToString:@"Camera"]) { [self camera]; } }
-(void)ShareWithInformation:(NSDictionary *)dic{ if (![dic isKindOfClass:[NSDictionary class]]) { return; } NSString *title = [dic objectForKey:@"title"]; NSString *content = [dic objectForKey:@"content"]; NSString *url = [dic objectForKey:@"url"]; //在这里写分享操作的代码 NSLog(@"要分享了哦?"); //OC反馈给JS分享结果 NSString *JSResult = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url]; //OC调用JS [self.webView evaluateJavaScript:JSResult completionHandler:^(id _Nullable result, NSError * _Nullable error) { NSLog(@"%@", error); }]; }
相关文章
[iOS]WKWebView的使用--API篇
UIWebView、WKWebView使用详解及性能分析
iOS WKWebView的基本使用
iOS WKWebView(清理缓存)
iOS OC与JS的交互(WKWebview-MessageHandler实现)