wkwebview 下移20像素_UITableView嵌套WKWebView的那些坑

最近项目中遇到了一个需求,TableView中需要嵌套Web页面,我的解决办法是在系统的UITableViewCell中添加WKWebView。开发的过程中,遇到了些坑,写出来分享一下。

1.首先说一下WKWebView的代理方法中,页面加载完成后会走的代理方法,与UIWebView的页面加载完成代理方法一样。- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation

- (void)webViewDidFinishLoad:(UIWebView *)webView;

这两个方法都是在web页面加载完成后才会走的代理方法。什么才算是加载完成呢?web页面中的所有元素都加载成功,包括图片、音频和视频等资源,都加载成功了,才算加载完成。如果通过这两个代理方法来计算cell的高度,你的整个tableview页面就会加载得很慢,至少要等web页面加载完成后才能计算高度重铺tableview。

2.WKWebView加载完成展示页面的时候,会让整个web页面自动适应屏幕的宽度。这样展示出来的效果就会很紧凑,页面内容会很小。

为了让web页面中的元素都适应屏幕的宽度显示,需要对WKWebView进行一下设置,原理是加载js代码,然后通过js来设置webview中的元素大小:WKWebViewConfiguration *webConfig = [[WKWebViewConfiguration alloc] init];

WKUserContentController *wkController = [[WKUserContentController alloc] init];

webConfig.userContentController = wkController;

// 自适应屏幕宽度js

NSString *jsStr = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";

WKUserScript *wkScript = [[WKUserScript alloc] initWithSource:jsStr injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];

// 添加js调用

[wkUController addUserScript:wkScript];

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:webConfig];

如果加载的是HTML代码,图片的自适应可以通过下面一段CSS代码来设置:NSString *html = @"HTML代码";

NSString *cssStr = @"";

NSString *htmlStr = [NSString stringWithFormat:@"html>

webview%@%@", cssStr, html];

[webView loadHTMLString:htmlStr baseURL:nil];

3.接下来说说重头戏,UITableViewCell嵌套WKWebView。

(1)cell的高度自适应(笔者用的是系统的cell,UITableViewCell)

cell的高度当然是根据webview的高度来设置的,所以首先要算出webview页面的高度。开篇就已经说过,webview加载完成的代理方法是web页面中的所有元素都加载成功,包括图片、音频和视频等资源,都加载成功了,才算加载完成。如果页面中的元素过多,网络图片过大,视频过大等,就会导致web页面加载卡顿。本身WKWebView这些资源是异步加载的,但是计算cell高度的时候是在这些资源都加载完成后才计算高度,WKWebView也就失去的异步加载的意义,所以整个tableview都会加载得很慢。

明白了这个道理,我个人推荐使用KVO来代替webview的代理方法。// 对webView中的scrollView设置KVO

[_webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];

// KVO具体实现

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

{

if ([keyPath isEqualToString:@"contentSize"]) {

// 这里有两种方法获得webview高度,第一种就是通过JS代码来计算出高度,如下

// document.documentElement.scrollHeight

// document.body.offsetHeight

/*

[_webView evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id _Nullable result, NSError * _Nullable error) {

// 加20像素是为了预留出边缘,这里可以随意

CGFloat height = [result doubleValue] + 20;

// 设置一个高度属性,赋值后便于设置cell的高度

_webHeight = height;

// 设置cell上子视图的frame,主要是高度

_webView.frame = CGRectMake(0, 0, kScreenWidth, height);

_scrollView.frame = CGRectMake(0, 0, kScreenWidth, height);

_scrollView.contentSize =CGSizeMake(kScreenWidth, height);

// 获取了高度之后,要更新webview所在的cell,其他的cell就不用更新了,这样能更节省资源

[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:1 inSection:0], nil] withRowAnimation:UITableViewRowAnimationNone];

}];

*/

// 第二种方法就是直接使用监听的contentSize.height(这里要感谢[markss](http://www.jianshu.com/u/cc0bd919cb50)简友的评论提醒),但是与第一种方法不同的就是不能再加多余的高度了,那样会循环出发KVO,不断的增加高度直到crash。

UIScrollView *scrollView = (UIScrollView *)object;

CGFloat height = scrollView.contentSize.height;

_webHeight = height;

_webView.frame = CGRectMake(0, 0, kScreenWidth, height);

_scrollView.frame = CGRectMake(0, 0, kScreenWidth, height);

_scrollView.contentSize =CGSizeMake(kScreenWidth, height);

[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:1 inSection:0], nil] withRowAnimation:UITableViewRowAnimationNone];

}

}

// 别忘注销kvo

- (void)dealloc

{

[_webView.scrollView removeObserver:self forKeyPath:@"contentSize"];

}

这样设置后的webView直接addSubview到webCell.contentView上,页面是加载出来了,但是webView的高度并不能改变。

(2)然后笔者百度了一下,找到了一个方法。就是先将webView addSubview到一个scrollView上[_scrollView addSubview:_webView];

然后再将这个scrollView addSubview到webCell.contentView上[webCell.contentView addSubview:_scrollView];

这样webview的高度就可以改变了。但是这是什么原理,笔者还没搞清楚,希望懂的大神能给予热情而细心的指导。

(3)在刷新tableview刷新cell的时候,会重新走一遍所有的tableview代理方法,所以笔者建议尽量优化- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath中的代码,防止多次加载webview- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

switch (indexPath.row) {

case 0:

{

ArticleHeaderTableViewCell *articleHeaderCell = [tableView dequeueReusableCellWithIdentifier:_articleHeaderIdentifier forIndexPath:indexPath];

return articleHeaderCell;

}

break;

case 1:

{

// 嵌套webivew的cell

UITableViewCell *webCell = [tableView dequeueReusableCellWithIdentifier:_articleWebIdentifier forIndexPath:indexPath];

[webCell.contentView addSubview:_scrollView];

return webCell;

}

break;

case 2:

{

ArticleCommentTableViewCell *articleCommentCell = [tableView dequeueReusableCellWithIdentifier:_articleCommentIdentifier forIndexPath:indexPath];

return articleCommentCell;

}

break;

default:

{

CommentsTableViewCell *commentsCell = [tableView dequeueReusableCellWithIdentifier:_commentsIdentifier forIndexPath:indexPath];

return commentsCell;

}

break;

}

}

总结:

1.在cell上嵌套webview之前,一定要在中间多加一层scrollview。

2.在计算cell高度的时候,不建议直接使用系统webview的代理方法,建议使用kvo。

3.进行减少- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法中对webview的操作,这样能减少webview的重复加载,提高性能和速度。

ps:每个人都有自己的想法,每个人的优化方式不同,笔者也是在摸索中尝试着,如果有说得不对不准确的地方,还请各位指正出来,大家共同探讨,互相学习。

代码下载地址:https://github.com/VonKeYuan/WKWebViewTest

作者:天蝎座的Von动必晒

來源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值