iOS WKWebView的使用
在开发过程中,iOS 中实现加载 web 页面主要有两种控件,UIWebView 和 WKWebview,两种控件对应具体的实现方法不同。WKWebView 是苹果在iOS 8中引入的新组件,目的是提供一个现代的支持最新Webkit功能的网页浏览控件,摆脱过去 UIWebView的老、旧、笨,特别是内存占用量巨大的问题。它使用与Safari中一样的Nitro JavaScript引擎,大大提高了页面js执行速度。
相比于UIWebView的优势:
在性能、稳定性、占用内存方面有很大提升;
允许JavaScript的Nitro库加载并使用(UIWebView中限制)
增加加载进度属性:estimatedProgress,不用在自己写假进度条了
支持了更多的HTML的属性。
使用方法:
1、导入头文件:#import <WebKit/WebKit.h>
2、创建WKWebView:
@property (nonatomic, strong) WKWebView *webView;
#pragma mark - webView
- (WKWebView *)webView {
if (!_webView) {
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
_webView.backgroundColor = [UIColor whiteColor];
_webView.navigationDelegate = self;
_webView.UIDelegate = self;
self.webView.allowsBackForwardNavigationGestures = YES;
}
return _webView;
}
3、设置代理<WKNavigationDelegate, WKUIDelegate>
// 页面开始加载时调用
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
self.progressView.hidden = NO;
}
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
}
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{//这里修改导航栏的标题,动态改变
self.navigationTitleLab.text = webView.title;
self.progressView.hidden = YES;
if (webView.backForwardList.backList.count > 0 && self.closeButton.hidden == YES) {
//作判断,显示与隐藏关闭按钮
self.closeButton.hidden = NO;
}
// //获取网页中的图片
// static NSString * const jsGetImages = @"function getImages(){\
// var objs = document.getElementsByTagName(\"img\");\
// var imgScr = '';\
// for(var i=0;i<objs.length;i++){\
// imgScr = imgScr + objs[i].src + '+';\
// };\
// return imgScr;\
// };";
// [webView evaluateJavaScript:jsGetImages completionHandler:nil];
// [webView evaluateJavaScript:@"getImages()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
// NSArray *urlArray = [NSMutableArray arrayWithArray:[result componentsSeparatedByString:@"+"]];
// //urlResurlt 就是获取到得所有图片的url的拼接;mUrlArray就是所有Url的数组
// NSLog(@"--%@",[urlArray firstObject]);
// }];
}
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
self.progressView.hidden = YES;
}
// 接收到服务器跳转请求之后再执行
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
}
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
WKNavigationResponsePolicy actionPolicy = WKNavigationResponsePolicyAllow;
//这句是必须加上的,不然会异常
decisionHandler(actionPolicy);
}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
//调起微信等(有些网页点击某个按钮会在微信、QQ或者appStore中打开,在这里监听跳转)
NSString *url = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];
if ([url containsString:@"weixin://"] ||
[url containsString:@"itms-appss://"] ||
[url containsString:@"mqq://"]) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
}
//需要判断targetFrame是否为nil,如果为空则重新请求
if (navigationAction.targetFrame == nil) {
[webView loadRequest:navigationAction.request];
}
WKNavigationActionPolicy actionPolicy = WKNavigationActionPolicyAllow;
if (navigationAction.navigationType == WKNavigationTypeBackForward) {//判断是返回类型
//同时设置返回按钮和关闭按钮为导航栏左边的按钮 这里可以监听左滑返回事件,仿微信添加关闭按钮。
//可以在这里找到指定的历史页面做跳转
}
//这句是必须加上的,不然会异常
decisionHandler(actionPolicy);
}
4、JS和OC的互相调用
设置代理:<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>
4.1、JS调用OC方法
配置环境:
WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init];
userContentController =[[WKUserContentController alloc]init];
configuration.userContentController = userContentController;
//注册方法
WKDelegateController * delegateController = [[WKDelegateController alloc]init];
delegateController.delegate = self;
[userContentController addScriptMessageHandler:delegateController name:@"sayhello"];
webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:configuration];
[self.view addSubview:webView];
webView.UIDelegate = self;
webView.navigationDelegate = self;
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.test.com"]]];
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (_webView) {
//这里需要注意,前面增加过的方法一定要remove掉。
[_webView.configuration.userContentController removeScriptMessageHandlerForName:@"sayhello"];
_webView.UIDelegate = nil;
_webView.navigationDelegate = nil;
[_webView removeFromSuperview];
_webView = nil;
}
}
- (void)dealloc{
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
// 这里实现原生的方法
if ([message.name isEqualToString:@"sayhello"]) {
// message.body就是h5传递给原生的参数,这里可以是nil,可以是字符串,可以是数组,可以是字典
}
}
message.body的类型(h5传值类型):
PS:若参数为空的话,h5调用原生的方法里面一定要传一个‘null’,不然,调不到原生方法。
4.2、OC调用JS方法
- (void)webView:(WKWebView *)tmpWebView didFinishNavigation:(WKNavigation *)navigation{
//test()是JS方法名,completionHandler是异步回调block
[webView evaluateJavaScript:@"test()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@",result);
}];
}
5、使用过程中遇到的问题
5.1、当用户点击webView上的链接无反应。(在浏览器中点击该网页上的链接,需要打开一个新的页面)
分析:在- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler方法中,参数 WKNavigationAction 中有两个属性:sourceFrame和targetFrame,分别代表这个action的出处和目标。类型是 WKFrameInfo 。WKFrameInfo有一个 mainFrame 的属性,正是这个属性标记着这个frame是在主frame里还是新开一个frame。
如果 targetFrame 的 mainFrame 属性为NO,表明这个 WKNavigationAction 将会新开一个页面。
解决方法:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//需要判断targetFrame是否为nil,如果为空则重新请求
if (navigationAction.targetFrame == nil) {
[webView loadRequest:navigationAction.request];
}
WKNavigationActionPolicy actionPolicy = WKNavigationActionPolicyAllow;
//这句是必须加上的,不然会异常
decisionHandler(actionPolicy);
}