繁体   English   中英

如何确定UIWebView的内容大小?

[英]How to determine the content size of a UIWebView?

我有一个具有不同(单页)内容的UIWebView 我想找出内容的CGSize来适当调整父视图的大小。 显而易见的-sizeThatFits:遗憾的是只返回webView的当前帧大小。

事实证明,我使用-sizeThatFits:第一次猜测并非完全错误。 它似乎工作,但只有在发送-sizeThatFits:之前,webView的框架设置为最小尺寸-sizeThatFits: 之后,我们可以通过拟合尺寸校正错误的框架尺寸。 这听起来很可怕,但实际上并没有那么糟糕。 由于我们彼此之后都进行了两次帧更改,因此视图不会更新且不会闪烁。

当然,我们必须等到内容加载完毕,因此我们将代码放入-webViewDidFinishLoad: delegate方法。

OBJ-C

- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
    CGRect frame = aWebView.frame;
    frame.size.height = 1;
    aWebView.frame = frame;
    CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
    frame.size = fittingSize;
    aWebView.frame = frame;

    NSLog(@"size: %f, %f", fittingSize.width, fittingSize.height);
}

Swift 4.x

func webViewDidFinishLoad(_ webView: UIWebView) {
    var frame = webView.frame
    frame.size.height = 1
    webView.frame = frame
    let fittingSize = webView.sizeThatFits(CGSize.init(width: 0, height: 0))
    frame.size = fittingSize
    webView.frame = frame
}

我应该指出使用JavaScript的另一种方法 (感谢@GregInYEG)。 不确定哪种解决方案表现更好。

在两个hacky解决方案中,我更喜欢这个。

我有另一个很好的解决方案。

一方面,Ortwin的方法和解决方案仅适用于iOS 6.0及更高版本,但无法在iOS 5.0,5.1和5.1.1上正常工作,另一方面,有些东西我不喜欢也无法理解使用Ortwin的方法,使用方法[webView sizeThatFits:CGSizeZero]和参数CGSizeZero :如果您阅读Apple官方文档有关此方法及其参数,它会清楚地说:

此方法的默认实现返回视图边界矩形的大小部分。 子类可以覆盖此方法,以根据所需子视图的所需布局返回自定义值。 例如,UISwitch对象返回一个固定大小的值,表示切换视图的标准大小,UIImageView对象返回当前显示的图像的大小。

我的意思是,就像他在没有任何逻辑的情况下遇到他的解决方案一样,因为阅读文档,传递给[webView sizeThatFits: ...]的参数至少应该具有所需的width 使用他的解决方案,在使用CGSizeZero参数调用sizeThatFits之前,将所需宽度设置为webView的框架。 所以我认为这个解决方案是通过“偶然”在iOS 6上运行的。

我想象一种更合理的方法,它具有适用于iOS 5.0及更高版本的优势......而且在复杂的情况下,其中多个webView(其属性webView.scrollView.scrollEnabled = NO嵌入在scrollView

这是我的代码,强制webView的布局到所需的width并获得相应的height设置回webView本身:

OBJ-C

- (void)webViewDidFinishLoad:(UIWebView *)aWebView
{   
    aWebView.scrollView.scrollEnabled = NO;    // Property available in iOS 5.0 and later 
    CGRect frame = aWebView.frame;

    frame.size.width = 200;       // Your desired width here.
    frame.size.height = 1;        // Set the height to a small one.

    aWebView.frame = frame;       // Set webView's Frame, forcing the Layout of its embedded scrollView with current Frame's constraints (Width set above).

    frame.size.height = aWebView.scrollView.contentSize.height;  // Get the corresponding height from the webView's embedded scrollView.

    aWebView.frame = frame;       // Set the scrollView contentHeight back to the frame itself.
}

Swift 4.x

func webViewDidFinishLoad(_ aWebView: UIWebView) {

    aWebView.scrollView.isScrollEnabled = false
    var frame = aWebView.frame

    frame.size.width = 200
    frame.size.height = 1

    aWebView.frame = frame
    frame.size.height = aWebView.scrollView.contentSize.height

    aWebView.frame = frame;
}

请注意,在我的例子中, webView是嵌入在一个自定义的scrollView具有其它webViews ......所有这些webViews有其webView.scrollView.scrollEnabled = NO ,和代码的最后一块,我不得不添加是计算height的我的自定义scrollViewcontentSize嵌入这些webViews ,但它就像使用上述技巧计算我的webViewframe.size.height一样简单...

复活这个问题是因为我发现Ortwin的答案只适用于大部分时间......

webViewDidFinishLoad方法可能会被多次调用,而sizeThatFits返回的第一个值只是最终大小应该是的一部分。 然后无论出于何种原因,当webViewDidFinishLoad再次触发时,下一次调用sizeThatFits将错误地返回之前执行的相同值! 对于相同的内容,这将是随机发生的,就好像它是某种并发问题一样。 也许这种行为随着时间的推移而发生了变化,因为我正在为iOS 5构建并且还发现sizeToFit工作方式大致相同(虽然以前这不是吗?)

我已经解决了这个简单的解决方案:

- (void)webViewDidFinishLoad:(UIWebView *)aWebView
{        
    CGFloat height = [[aWebView stringByEvaluatingJavaScriptFromString:@"document.height"] floatValue];
    CGFloat width = [[aWebView stringByEvaluatingJavaScriptFromString:@"document.width"] floatValue];
    CGRect frame = aWebView.frame;
    frame.size.height = height;
    frame.size.width = width;
    aWebView.frame = frame;
}

斯威夫特(2.2):

func webViewDidFinishLoad(webView: UIWebView) {

    if let heightString = webView.stringByEvaluatingJavaScriptFromString("document.height"),
        widthString = webView.stringByEvaluatingJavaScriptFromString("document.width"),
        height = Float(heightString),
        width = Float(widthString) {

        var rect = webView.frame
        rect.size.height = CGFloat(height)
        rect.size.width = CGFloat(width)
        webView.frame = rect
    }
}

更新:我发现在评论中提到这似乎没有抓住内容缩小的情况。 不确定所有内容和操作系统版本是否属实,请试一试。

在Xcode 8和iOS 10中确定Web视图的高度。 你可以使用高度

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    CGFloat height = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] floatValue];
    NSLog(@"Webview height is:: %f", height);
}

或者对于斯威夫特

 func webViewDidFinishLoad(aWebView:UIWebView){
        let height: Float = (aWebView.stringByEvaluatingJavaScriptFromString("document.body.scrollHeight")?.toFloat())!
        print("Webview height is::\(height)")
    }

一个简单的解决方案是使用webView.scrollView.contentSize但我不知道这是否适用于JavaScript。 如果没有使用JavaScript,这肯定有效:

- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
    CGSize contentSize = aWebView.scrollView.contentSize;
    NSLog(@"webView contentSize: %@", NSStringFromCGSize(contentSize));
}

AFAIK你可以使用[webView sizeThatFits:CGSizeZero]来计算它的内容大小。

这太奇怪了!

我测试了sizeThatFits:的解决方案sizeThatFits:[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] 适合我。

但是,我发现了一种有趣的方法来获得正确的网页内容。 目前,我在我的委托方法scrollViewDidScroll:使用它。

CGFloat contentHeight = scrollView.contentSize.height - CGRectGetHeight(scrollView.frame);

在iOS 9.3模拟器/设备中验证,祝您好运!

编辑:

背景:html内容由我的字符串变量和HTTP内容模板计算,由方法loadHTMLString:baseURL:加载,没有注册的JS脚本。

对于iOS10,我获得了document.height 0(零)值,因此document.body.scrollHeight是获取Webview中文档高度的解决方案。 对于width也可以解决该问题。

我正在使用不是子视图的UIWebView (因此不是窗口层次结构的一部分)来确定UITableViewCells的HTML内容的大小。 我发现断开连接的UIWebView没有正确报告其大小-[UIWebView sizeThatFits:] 此外,如https://stackoverflow.com/a/3937599/9636中所述 ,您必须将UIWebViewframe height为1才能获得正确的高度。

如果UIWebView的高度太大(即您将其设置为1000,但HTML内容大小仅为500):

UIWebView.scrollView.contentSize.height
-[UIWebView stringByEvaluatingJavaScriptFromString:@"document.height"]
-[UIWebView sizeThatFits:]

全部返回1000的height

为了解决我在这种情况下的问题,我使用了https://stackoverflow.com/a/11770883/9636 ,我尽职尽责地投票。 但是,当我的UIWebView.frame.width-[UIWebView sizeThatFits:] width同时,我只使用此解决方案。

这里的建议都没有帮助我解决我的情况,但我读了一些确实给我答案的东西。 我有一个ViewController,其中包含一组固定的UI控件,后跟一个UIWebView。 我希望整个页面滚动,好像UI控件已连接到HTML内容,因此我禁用UIWebView上的滚动,然后必须正确设置父滚动视图的内容大小。

重要提示原来是UIWebView在呈现到屏幕之前不会正确报告其大小。 因此,当我加载内容时,我将内容大小设置为可用的屏幕高度。 然后,在viewDidAppear中,我将scrollview的内容大小更新为正确的值。 这对我有用,因为我在本地内容上调用loadHTMLString。 如果您正在使用loadRequest,则可能还需要更新webViewDidFinishLoad中的contentSize,具体取决于检索html的速度。

没有闪烁,因为只有滚动视图的不可见部分被更改。

如果你的HTML包含像iframe这样的重量级 HTML内容(即facebook-,twitter,instagram-embeds),那么真正的解决方案要困难得多,首先包装你的HTML:

[htmlContent appendFormat:@"<html>", [[LocalizationStore instance] currentTextDir], [[LocalizationStore instance] currentLang]];
[htmlContent appendFormat:@"<head>"];
[htmlContent appendString:@"<script type=\"text/javascript\">"];
[htmlContent appendFormat:@"    var lastHeight = 0;"];
[htmlContent appendFormat:@"    function updateHeight() { var h = document.getElementById('content').offsetHeight; if (lastHeight != h) { lastHeight = h; window.location.href = \"x-update-webview-height://\" + h } }"];
[htmlContent appendFormat:@"    window.onload = function() {"];
[htmlContent appendFormat:@"        setTimeout(updateHeight, 1000);"];
[htmlContent appendFormat:@"        setTimeout(updateHeight, 3000);"];
[htmlContent appendFormat:@"        if (window.intervalId) { clearInterval(window.intervalId); }"];
[htmlContent appendFormat:@"        window.intervalId = setInterval(updateHeight, 5000);"];
[htmlContent appendFormat:@"        setTimeout(function(){ clearInterval(window.intervalId); window.intervalId = null; }, 30000);"];
[htmlContent appendFormat:@"    };"];
[htmlContent appendFormat:@"</script>"];
[htmlContent appendFormat:@"..."]; // Rest of your HTML <head>-section
[htmlContent appendFormat:@"</head>"];
[htmlContent appendFormat:@"<body>"];
[htmlContent appendFormat:@"<div id=\"content\">"]; // !important https://stackoverflow.com/a/8031442/1046909
[htmlContent appendFormat:@"..."]; // Your HTML-content
[htmlContent appendFormat:@"</div>"]; // </div id="content">
[htmlContent appendFormat:@"</body>"];
[htmlContent appendFormat:@"</html>"];

然后在你的shouldStartLoadWithRequest中添加处理x-update-webview-height -scheme

    if (navigationType == UIWebViewNavigationTypeLinkClicked || navigationType == UIWebViewNavigationTypeOther) {
    // Handling Custom URL Scheme
    if([[[request URL] scheme] isEqualToString:@"x-update-webview-height"]) {
        NSInteger currentWebViewHeight = [[[request URL] host] intValue];
        if (_lastWebViewHeight != currentWebViewHeight) {
            _lastWebViewHeight = currentWebViewHeight; // class property
            _realWebViewHeight = currentWebViewHeight; // class property
            [self layoutSubviews];
        }
        return NO;
    }
    ...

最后在layoutSubviews中添加以下代码:

    ...
    NSInteger webViewHeight = 0;

    if (_realWebViewHeight > 0) {
        webViewHeight = _realWebViewHeight;
        _realWebViewHeight = 0;
    } else {
        webViewHeight = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"content\").offsetHeight;"] integerValue];
    }

    upateWebViewHeightTheWayYorLike(webViewHeight);// Now your have real WebViewHeight so you can update your webview height you like.
    ...

PS你可以在ObjectiveC / Swift代码中实现时间延迟(SetTimeout和setInterval) - 这取决于你。

PSS关于UIWebView和Facebook嵌入的重要信息: 嵌入式Facebook帖子在UIWebView中无法正确显示

当使用webview作为scrollview中的某个子视图时,您可以将高度约束设置为某个常量值,然后从中创建出口并使用它,如:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    webView.scrollView.scrollEnabled = NO;
    _webViewHeight.constant = webView.scrollView.contentSize.height;
}

我也坚持这个问题,然后我意识到,如果我想计算webView的动态高度,我需要先告诉webView的宽度,所以我在js之前添加一行,结果我得到了非常准确的实际高度。

代码很简单,如下所示:

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

    //tell the width first
    webView.width = [UIScreen mainScreen].bounds.size.width;

    //use js to get height dynamically
    CGFloat scrollSizeHeight = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] floatValue];
    webView.height = scrollSizeHeight;

    webView.x = 0;
    webView.y = 0;

    //......
}

Xcode8 swift3.1:

  1. 首先将webView高度设置为0
  2. webViewDidFinishLoad委托中:

let height = webView.scrollView.contentSize.height

如果没有step1,如果webview.height>实际contentHeight,则步骤2将返回webview.height但不返回contentsize.height。

同样在iOS 7中正确处理所有提到的方法,在视图控制器viewDidLoad方法中添加:

if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
    self.automaticallyAdjustsScrollViewInsets = NO;
}

否则两种方法都不会起作用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM