记录一下iOS8 之后的新控件WKWebView,用以替代之前的UIWebView,因为需求是在TableView的Cell中放一个WebView。就产生了滑动手势冲突,为了解决这个问题就需要让webView高度自适应
一、新特性
- 在性能、稳定性、功能方面有很大的提升,最明显的就是内存占用降低了很多。
- 允许JavaScript的Nitro库加载并使用(UIWebView中限制)
- 支持了更多的HTML5特性;
- 高达60fps的滚动刷新率以及内置手势(支持右滑返回);
- 将UIWebViewDelegate与UIWebView重构成了14类与3个协议(查看苹果官方文档);
二、初始化
- 首先需要引入WebKit库
#import <WebKit/WebKit.h>
- 采用configuration的方式初始化(可选)
- (WKWebView *)webView {
if (!_webView) {
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
//初始化偏好设置属性:preferences
config.preferences = [WKPreferences new];
//The minimum font size in points default is 0;
config.preferences.minimumFontSize = 10;
//是否支持JavaScript
config.preferences.javaScriptEnabled = YES;
//不通过用户交互,是否可以打开窗口
config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
_webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, self.view.width, self.view.height) configuration:config];
_webView.UIDelegate =self;
_webView.navigationDelegate = self;
// 此处因为高度自适应所以不应该让webview内部可以滚动
_webView.scrollView.scrollEnabled = NO;
}
return _webView;
}
- 加载网页
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://m.baidu.com"]]];
[self.view addSubview:webView];
三、WKWebView代理方法
1.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 didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
2.WKUIDelegate
// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
剩下三个代理方法全都是与界面弹出提示框相关的,针对于web界面的三种提示框(警告框、确认框、输入框)分别对应三种代理方法。下面只举了警告框的例子
/**
* web界面中有弹出警告框时调用
*
* @param webView 实现该代理的webview
* @param message 警告框中的内容
* @param frame 主窗口
* @param completionHandler 警告框消失调用
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler;
3.WKScriptMessageHandler
这个协议中包含一个必须实现的方法,这个方法是提高App与web端交互的关键,它可以直接将接收到的JS脚本转为OC或Swift对象
// 从web界面中接收到一个脚本时调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
四、WKWebView加载JS
// js代码
NSString *js = @" var count = document.images.length;for (var i = 0; i < count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '张图');";
// 根据JS字符串初始化WKUserScript对象
WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
// 根据生成的WKUserScript对象,初始化WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; [config.userContentController addUserScript:script]; _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
// 加载html字符串
[_webView loadHTMLString:@"<head></head>![](http://upload-images.jianshu.io/upload_images/2076247-ccdb3b439e0e0b10.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)"baseURL:nil];
[self.view addSubview:_webView];
五、作为cell自适应行高
- 在didFinishNavigation方法中获取行高,然后刷新表格,网上资料所说的获取webview.scrollview.contentsize.height本人测试不可行,此处采用调用js通过ajax获取高度
// 页面加载完成之后调用 此方法会调用多次
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
__block CGFloat webViewHeight;
self.height = webView.frame.size.height;
//获取内容实际高度(像素)@"document.getElementById(\"content\").offsetHeight;"
[webView evaluateJavaScript:@"document.body.scrollHeight" completionHandler:^(id _Nullable result,NSError * _Nullable error) {
// 此处js字符串采用scrollHeight而不是offsetHeight是因为后者并获取不到高度,看参考资料说是对于加载html字符串的情况下使用后者可以,但如果是和我一样直接加载原站内容使用前者更合适
//获取页面高度,并重置webview的frame
webViewHeight = [result doubleValue];
NSLog(@"%f",webViewHeight);
dispatch_async(dispatch_get_main_queue(), ^{
if (webViewHeight != self.height) {
webView.frame = CGRectMake(0, 0, self.view.frame.size.width, webViewHeight);
[self.tableView reloadData];
}
});
}];
NSLog(@"结束加载");
}
- 此处完成之后需要在heightforrow方法中设置cell的高为webview的高,但是会出现一个问题就是内容显示不全。还需要调用下面这个方法进行重布局
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ([scrollView isKindOfClass:[self.tableView class]]) {
[self.webView setNeedsLayout];
}
}
六、小结
自适应行高是本文的重点,是我自己试验了多种方法之后确定可行的方法
附上我的博客链接:oragekk'Blog 欢迎留言-不过评论系统换成了disqus需要搭梯子哦