代码之家  ›  专栏  ›  技术社区  ›  Ortwin Gentz

如何确定UIWebView的内容大小?

  •  116
  • Ortwin Gentz  · 技术社区  · 14 年前

    我有一个 UIWebView 有不同的(单页)内容。我想知道 CGSize 以适当调整父视图的大小。显而易见的 -sizeThatFits: 不幸的是,只返回webView的当前帧大小。

    15 回复  |  直到 6 年前
        1
  •  249
  •   Argus Legonaftik    6 年前

    结果我的第一个猜测是 -sizeThatFits: 不是完全错了。它似乎可以工作,但前提是在发送之前将webView的帧设置为最小大小 -尺寸: . 然后我们可以根据合适的尺寸来修正错误的框架尺寸。这听起来很糟糕,但其实没那么糟。因为我们两个帧一个帧地改变,所以视图不会更新,也不会闪烁。

    -webViewDidFinishLoad: 委托方法。

    目标-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);
    }
    

    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
    }
    

    我应该指出 another approach (谢谢@GregInYEG)使用JavaScript。不确定哪种解决方案性能更好。

    我更喜欢这一个。

        2
  •  92
  •   Argus Legonaftik    6 年前

    我有另一个解决方案,效果很好。

    一方面,Ortwin的方法和解决方案只适用于iOS6.0及更高版本,但无法在iOS5.0、5.1和5.1.1上正常工作;另一方面,有些东西我不喜欢,也无法理解Ortwin的方法,那就是方法的使用 [webView sizeThatFits:CGSizeZero] 带参数 CGSizeZero :如果您阅读了苹果官方文档中有关此方法及其参数的内容,它会清楚地说明:

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

    我的意思是,他遇到的解决方案没有任何逻辑,因为阅读文档时,参数传递给 [webView sizeThatFits: ...] 至少应该有想要的 width . 通过他的解决方案,所需的宽度设置为 webView 呼叫前的帧 sizeThatFits CGSizeZero公司 参数。所以我认为这个解决方案是“偶然”在iOS 6上运行的。

    我设想了一种更合理的方法,它的优点是可以为iOS 5.0和更高版本工作。。。以及在多个webView(具有其属性)的复杂情况下 webView.scrollView.scrollEnabled = NO 嵌入到 scrollView

    这是我的代码来强制 网络视图 达到预期 宽度 得到相应的 height 网络视图

    目标-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.
    }
    

    斯威夫特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;
    }
    

    注意在我的例子中 网络视图 嵌入到一个自定义 滚动视图 有其他的 webViews ... 所有这些 网络视图 有他们的 webView.scrollView.scrollEnabled=否 ,我要添加的最后一段代码是 高度 contentSize 我的习惯 滚动视图 嵌入这些 网络视图 网络视图 frame.size.height 用上面描述的技巧计算。。。

        3
  •  37
  •   Jayesh Thanki    7 年前

    重新提出这个问题是因为我发现奥特温的答案大部分时间都是有效的。。。

    这个 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
        }
    }
    

    更新:我发现,正如评论中提到的,这似乎并没有抓住内容缩水的情况。不确定它是否适用于所有内容和操作系统版本,请尝试一下。

        4
  •  23
  •   Hardik Thakkar    8 年前

    在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)")
        }
    
        5
  •  8
  •   Hardik Thakkar    8 年前

    一个简单的解决办法就是 webView.scrollView.contentSize 但我不知道这是否适用于JavaScript。如果没有使用JavaScript,那么可以肯定:

    - (void)webViewDidFinishLoad:(UIWebView *)aWebView {
        CGSize contentSize = aWebView.scrollView.contentSize;
        NSLog(@"webView contentSize: %@", NSStringFromCGSize(contentSize));
    }
    
        6
  •  3
  •   Rengers    14 年前

    如果你能用的话 [webView sizeThatFits:CGSizeZero] 找出它的内容大小。

        7
  •  3
  •   Itachi    8 年前

    真奇怪!

    我测试了两种解决方案 sizeThatFits: [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] 不是 为我工作。

    然而,我发现了一个有趣的简单方法来获得网页内容的正确高度。目前,我在委托方法中使用了它 scrollViewDidScroll: .

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

    编辑:

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

        8
  •  3
  •   iAnkit    8 年前

    对于iOS10,我得到了 document.height 所以 document.body.scrollHeight 是获取Webview中文档高度的解决方案。这个问题也可以解决 width .

        9
  •  2
  •   Community CDub    8 年前

    我用的是 UIWebView UITableViewCells . 我发现 UIWebView网站 -[UIWebView sizeThatFits:] . 此外,如中所述 https://stackoverflow.com/a/3937599/9636 ,必须设置 UIWebView网站 frame height 为了得到合适的高度。

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

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

    高度 共1000个。

    在这种情况下,我用 https://stackoverflow.com/a/11770883/9636 UIWebView.frame.width width .

        10
  •  2
  •   Eli Burke    11 年前

    这里的建议对我的处境毫无帮助,但我读到了一些确实给我答案的东西。我有一个ViewController,它有一组固定的UI控件,后面跟着一个UIWebView。我希望整个页面都能像UI控件连接到HTML内容一样滚动,所以我禁用了UIWebView上的滚动,然后必须正确设置父滚动视图的内容大小。

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

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

        11
  •  2
  •   Community CDub    8 年前

    如果HTML包含 像iframe这样的HTML内容(即facebook、twitter、instagram嵌入)真正的解决方案要困难得多,首先要包装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>"];
    

    然后添加处理 x-update-webview-height -策划你的 请求时应立即加载 :

        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;
        }
        ...
    

    最后在 :

        ...
        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.
        ...
    

    P、 你可以在ObjectiveC/Swift代码中实现延时(SetTimeout和setInterval)——这取决于你自己。

    P、 关于UIWebView和Facebook嵌入的重要信息: Embedded Facebook post does not shows properly in UIWebView

        12
  •  2
  •   younke    9 年前

    将webview用作scrollview中某个位置的子视图时,可以将高度约束设置为某个常量值,然后从该值中生成outlet,并按如下方式使用:

    - (void)webViewDidFinishLoad:(UIWebView *)webView {
        webView.scrollView.scrollEnabled = NO;
        _webViewHeight.constant = webView.scrollView.contentSize.height;
    }
    
        13
  •  1
  •   user5047504 user5047504    10 年前

    我也陷入了这个问题,然后我意识到如果我想计算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;
    
        //......
    }
    
        14
  •  1
  •   hstdt    8 年前

    Xcode8迅捷3.1:

    1. 设置webView高度 到0 第一。
    2. webViewDidFinishLoad 代表:

    let height = webView.scrollView.contentSize.height

    如果没有步骤1,如果webview.height>实际contentHeight,步骤2将返回webview.height,而不是contentsize.height。

        15
  •  0
  •   Regexident Amit Attias    11 年前

    同样在iOS 7中,为了正确使用所有提到的方法,在视图控制器中添加这个 viewDidLoad 方法:

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