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页面自动适应屏幕的宽度。这样展示出来的效果就会很紧凑,页面内容会很小。


图2.1

为了让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];
图2.2

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

NSString *html = @"HTML代码";
NSString *cssStr = @"<style type=\"text/css\">p{line-height: 24px!important;background-color: #fff;}img{max-width: 100%!important;}</style>";
NSString *htmlStr = [NSString stringWithFormat:@"<!DOCTYPE html><html><head><title>webview</title></head><body>%@%@</body></html>", 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<NSString *,id> *)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/Nov08/WKWebViewTest

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,613评论 4 59
  • 这几天总是会想起痒痒说的穿越的事,每次想起都笑的不行,所以赶紧记下来!当时正在看杨幂演的电视剧 老公,你说我要是穿...
    挠挠爱痒痒阅读 218评论 0 1
  • 译者:作者讲述的这几个网络营销中的问题,国内也屡见不鲜。分享给大家,一起少走弯路。 我讨厌看到人们把大把的金钱浪费...
    TsingGuo阅读 220评论 0 2
  • 文/初见 上学的时候男生总是很不理解,为什么我们女生上厕所总要成群结队的,是怕掉进厕所没人打捞起来吗? 这是因为啊...
    玛丽莲懵露阅读 2,227评论 1 5
  • 送你一片天,愿你自由翱翔。 送你一片海,愿你胸怀坦荡。 送你一片绿,愿你生气蓬勃。 送你一片心,愿你被爱环绕。 送...
    喜新亦念旧阅读 440评论 0 4