袁凤鸣的博客 | Fleeming's Blog Front-end Dev Engineer

精确获取 UIWebView 的高度

2017-03-13
袁凤鸣
iOS

精确获取 UIWebView 的高度

(一)使用背景:

很多情况下。项目需求实现H5文章 和 原生评论效果,文章是加载H5链接,评论信息是后台接口提供,以列表形式展示。

像下面这个内容的详情图。我们希望上面部分的详情内容做成 H5 ,下面部分的评论列表做成原生的。。我们这个时候就想到了把 H5 嵌入到 tableViewtableHeaderView 上面。然后评论就用 cell 进行显示。

如图:( NO Pictures Say G8 O(∩_∩)O~

(二)原始做法:

原始做法,只是在设置 webView 的代理之后 实现 webViewDidFinishLoad代理方法。在方法中使用 [webView sizeThatFits:CGSizeZero] 来得到 webView的内容高度。

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

    CGSize tempSize = [webView sizeThatFits:CGSizeZero];
    _webView.frame = CGRectMake(0, 0, tempSize.width, tempSize.height);
    [self.table setTableHeaderView:_webView];
    
}

(三)问题介绍:

那么问题来了。

获取WebView 内容高度,上面的做法无法获取到准确高度,导致页面布局出现差错。

像上面这样,类似的方法很多,不管是JS获取,还是contentSize获取,最后结果都难以获取到准确高度,并非方法不行,而是:
webViewDidFinishLoad代理方法被调用时,页面并不一定完全展现完成,可能有图片还未加载出来,导致此时获取的高度是并不是最终高度
,过会儿图片加载出来后,浏览器会重新排版,而我们在这之前给了一个错误高度,导致显示异常。

布局出现差错后,上下滑动页面时,在WebView内容区域滑动只是 WebView 的内容上下动,在 tableView 内容区域滑动只是 tableView 内容上下动。而我们要的效果是无论怎样滑动 都只有 tableView 内容上下动。搜到的资料很多但都无法解决问题,相信很多人都踩过坑。以下是个人经验总结:

(四)解决思路:

  • webView加载H5链接,设置它为tableViewheaderView,下方评论信息用Cell加载展示。

  • webView的回调方法webViewDidFinishLoad中获取网页内容高度,设置为webView的高度,重新将webView赋给tableViewheaderView
  • webViewscrollViewcontentSize属性添加监听,每当内容发生变化,contentSize一定会跟着变,捕获这个变动,在监听方法中实现webViewDidFinishLoad中的代码,也就是获取最新的内容高度赋给webView

Tip: 将一个View赋值给UITableView的tableHeaderView时,不需要手动设置高度,HeaderView会自动使用View的高度。

(五)代码实现:

引用 Linus Torvalds 一句装逼的话 :

Talk is cheap, show me the code. —— Linus Torvalds

  • 设置监听的 KeyPath

       static NSString  *kcontentSize = @"contentSize";
    
  • webView的回调方法webViewDidFinishLoad中设置 监听 webView.scrollViewcontentSize 变化:

    - (void)webViewDidFinishLoad:(UIWebView *)webView {
    CGSize tempSize = [webView sizeThatFits:CGSizeZero];
    _webView.frame = CGRectMake(0, 0, tempSize.width, tempSize.height);
    [self.table beginUpdates];
    [self.table setTableHeaderView:_webView];
    [self.table endUpdates];
    [self.webView.scrollView addObserver:self forKeyPath:kcontentSize options:NSKeyValueObservingOptionNew context:nil];
    }
  • 实现 KVO 回调方法,并判断如果 KeyPathcontentSize时再进行一次 webViewDidFinishLoadwebView高度的赋值操作。

      - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    	
          if ([keyPath isEqualToString:kcontentSize]) {
    	        
              CGSize tempSize = [_webView sizeThatFits:CGSizeZero];
              _webView.frame = CGRectMake(0, 0, tempSize.width, tempSize.height);
               [self.table beginUpdates];
               [self.table setTableHeaderView:_webView];
               [self.table endUpdates];
    	        
          }else {
              [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
          }
      }
    
  • 同时原来 webViewDidFinishLoad的操作也是要的 :

      - (void)webViewDidFinishLoad:(UIWebView *)webView {
    	
          CGSize tempSize = [webView sizeThatFits:CGSizeZero];
          _webView.frame = CGRectMake(0, 0, tempSize.width, tempSize.height);
          [self.table setTableHeaderView:_webView];
    	    
      }
    
  • 移除观察者 :

      - (void)dealloc {
          [_webView.scrollView removeObserver:self forKeyPath:kcontentSize context:nil];
    	    
      }
    

(六)来个总结:

  • 好了。这样之后。webView内容发生变化后。就会监听到 webView.scrollView.contentSize 也发生了变化。我们在监听方法里面及时再次设置高度即可。

  • 还有,在cell中使用webView获取高度不准确的解决办法跟上面一样,只不过需要注意cell中使用webView涉及到cell重用,会导致滑动列表时webView多次加载,影响性能,建议缓存高度。



如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

¥ 打赏博主

上一篇 iOS10适配总结

请科学上网看评论 😂

Enjoy music ?
Search
    Content