如何在可变高度的UITableView中根据内容确定UIWebView的高度?

71

我正在尝试创建一个可变高度行的UITableView,就像这个问题中的答案所解释的那样。

我的问题是,每个单元格都包含一个带有不同(静态加载)内容的UIWebView,我无法计算基于该内容的正确高度。是否有一种方法可以做到这一点? 我已经尝试了这样的方法:

   (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
       WebViewCell *cell = (WebViewCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
       [cell setNeedsLayout];
       [cell layoutIfNeeded];
       return cell.bounds.size.height;
    }

单元格本身是从一个nib文件加载的,这个nib文件包含一个UITableViewCell和一个UIWebView。(如果单元格可以自适应html内容的最大高度而不是固定高度会更好)。


你最近有没有想过怎样实现这个功能?下面的答案似乎可以获取高度,但是必须在需要它之后才能得到。如果必须依赖于回调(webview代理)调用webViewDidFinishLoad来计算heightForRowAtIndexPath的高度,该如何处理呢? - pbx
你好,最终你是如何确定webview的高度的?我也遇到了同样的问题... - Nik's
有人有解决方案吗????还是想不出来。。。 - mikemike396
7个回答

77

这段代码可能在表格视图中使用太慢了,但是可以解决问题。看起来似乎没有其他选择,因为UIWebView没有直接访问DOM的方式。

在视图控制器的viewDidLoad方法中,我在Web视图中加载了一些HTML,并在加载完成后运行一些JavaScript来返回元素的高度。

- (void)viewDidLoad
{
    [super viewDidLoad];
    webview.delegate = self;
    [webview loadHTMLString:@"<div id='foo' style='background: red'>The quick brown fox jumped over the lazy dog.</div>" baseURL:nil];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *output = [webview stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"foo\").offsetHeight;"];
    NSLog(@"height: %@", output);
}

听起来似乎可以解决,但我已经在表视图中尝试过了,但它返回一个空字符串(我想是因为网页视图还没有完成加载)。我想我可以通过使用离屏网页视图预计算此结果,并将结果缓存到表视图中。 - frankodwyer
你是在webViewDidFinishLoad中测量高度吗?那是代理回调,表示加载完成。它不应该返回空字符串。 - duncanwilcox
这几乎就是我所做的。虽然我遇到了一个问题,即WebView在完成加载之前无法返回其大小,而当表格需要知道其大小时,它可能还没有完成加载。有人有解决方法吗? - Erik B
37
我使用了以下代码:NSString *output = [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"]; - BadPirate
3
在 iOS 4.3 中,我需要进行以下操作:将我的 webView 的高度设置得非常小(例如1像素),并执行 BadPirate 推荐的调用,之后调整滚动视图的内容值和 webView 的框架值。 - Dirty Henry
2
您必须将初始UIWebView的高度设置为至少1像素,否则在进行JS调用时,您将返回屏幕高度。 - Vadoff

28

看到你的回答,我非常高兴,javascript的技巧对我很有用。

但是,我想说还有一个方法叫做 "sizeThatFits:":

CGSize goodSize = [webView sizeThatFits:CGSizeMake(anyWidth,anyHeigth)];

除了需要设置一个非零的首选大小之外,它通常总是有效的!

通常情况下,使用UIWebViews时,它似乎只在第二次加载时才起作用(我使用“loadHTMLString:”方法,并且在委托“didFinishLoad:”方法中调用“sizeThatFits:”)。

所以,这就是为什么我很高兴找到你的解决方案,它在任何情况下都可以工作。


@Shade:请查看我的答案,了解如何在更高版本的iOS上实现此功能。 - Ortwin Gentz
小心这个答案!有时会返回错误的“高度”!当我用它处理多张图片时,得到了错误的值。 - Andrey Gordeev

6

这似乎可以工作。目前为止。

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    CGFloat webViewHeight = 0.0f;
    if (self.subviews.count > 0) {
        UIView *scrollerView = [self.subviews objectAtIndex:0];
        if (scrollerView.subviews.count > 0) {
            UIView *webDocView = scrollerView.subviews.lastObject;
            if ([webDocView isKindOfClass:[NSClassFromString(@"UIWebDocumentView") class]])
                webViewHeight = webDocView.frame.size.height;
        }
    }
}

如果失败,它应该安全地返回0。

3
这有何优势比起这个代码:[webview stringByEvaluatingJavaScriptFromString:@"document.getElementById("foo").offsetHeight;"];?问题在于,网页视图加载完成之前无法返回其大小,而当表格需要知道其大小时,可能尚未加载完成。 - Erik B

4
-sizeThatFits:方法存在问题,至少在iOS 4.1上不起作用。它只返回webView的当前大小(无论是使用CGSizeZero还是任意非零大小调用)。
我发现,如果在调用-sizeThatFits:之前减小webView的框架高度,则实际上可以解决该问题。
请参见我的解决方案:如何确定UIWebView的内容大小?

谢谢你的指针。我在遇到这个问题时确实看到了你的解决方案,但在我的情况下它并没有更好地工作。我不太记得我需要这个做什么,但最终我用了另一种方式解决了它。 - Shade

4
更好的方法是使用scrollView.contentSize.height属性,如下所示。
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
     NSLog(@"%f",webView.scrollView.contentSize.height);   
}

1
对我来说,它是scrollView.contentSize.height - CGRectGetHeight(scrollView.frame)。https://dev59.com/82865IYBdhLWcg3wM7q0#39386880 - Itachi

3

在加载webview内容后才能测量其大小。

  • 将webview变小(比如5px)
  • 加载它并等待完成
  • 然后调用sizeToFit方法
  • 获取尺寸
  • 重新加载tableview(返回单元格的正确高度)

广告: :D 在github上查看我的M42WebviewTableViewCell类,该类解决了这个问题。


可以了!谢谢,我之前用的是JavaScript的方法,但这个对我来说更好。 - Stan

3

如果您在使用sizeThatFits时第一次出现错误的值,但之后没有问题,这里有一个可能的原因:外部CSS的bug。

似乎webViewDidFinisLoad被调用得太早了,在外部CSS被检索和网页文档完全构建之前。

我认为如果由于缓存而重新加载,则该错误会消失。

一个技巧:在应用程序中尽快使用虚拟UIWebView预加载您的CSS。

SDK:IOS4


在我的情况下,加载晚的不是CSS本身,而更具体地说是CSS中的字体文件。 - beetstra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接