簡體   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