UIWebViewのコンテンツサイズを確認する方法


116

私が持っているUIWebView別の(単一ページ)コンテンツに。CGSize親ビューを適切にサイズ変更するためのコンテンツを見つけたいのですが。明らかに-sizeThatFits:残念ながら、webViewの現在のフレームサイズを返します。


1
私はわからないんだけど、多分この質問への回答は役立ちます:stackoverflow.com/questions/745160/...
グレッグ

回答:


250

私の最初の推測-sizeThatFits:は完全に間違っているわけではないことがわかりました。動作するようですが、送信する前にwebViewのフレームが最小サイズに設定されている場合のみです-sizeThatFits:。その後、フィッティングサイズによって間違ったフレームサイズを修正できます。これはひどいように聞こえますが、実際にはそれほど悪くはありません。両方のフレームの変更をお互いの直後に行うため、ビューは更新されず、ちらつきません。

もちろん、コンテンツが読み込まれるまで待つ必要があるため、コードを-webViewDidFinishLoad:デリゲートメソッドに挿入します。

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に感謝)があることを指摘しておきます。どのソリューションの方がパフォーマンスが良いかわかりません。

2つのハッキングソリューションのうち、私はこれをより気に入っています。


私はあなたのアプローチを試みました。私が経験した問題は、スクロールがないことです。助言がありますか?
テスト

1
Webviewの高さが変更されたため、スクロールの問題が発生することがわかりました。UIViewのサイズを変更する必要がありますか?
2010

あなたの問題が何かわからない。おそらく、いくつかのコードまたは説明を使用して新しい質問を投稿できますか?
Ortwin Gentz 2010

@ testing、@ Bogatyr:そうUIWebViewです、あなたの親ビューよりも高い場合、それをに埋め込む必要がありますUIScrollView
Ortwin Gentz

7
webView.scalesPageToFit = YESを与えている場合; その後、唯一この解決策は...解決のためworks..thanks
SP

92

うまくいく別の解決策があります。

一方では、OrtwinのアプローチとソリューションはiOS 6.0以降でのみ機能しますが、iOS 5.0、5.1、および5.1.1では正しく機能しません。他方、私が気に入らない、理解できないものがあります。 Ortwinのアプローチで[webView sizeThatFits:CGSizeZero]は、パラメータを使用したメソッドの使用です。CGSizeZeroこのメソッドとそのパラメータに関するAppleの公式ドキュメントを読んだ場合、それは明確に述べています。

このメソッドのデフォルト実装は、ビューの境界の長方形のサイズ部分を返します。サブクラスはこのメソッドをオーバーライドして、任意のサブビューの必要なレイアウトに基づいてカスタム値を返すことができます。たとえば、UISwitchオブジェクトはスイッチビューの標準サイズを表す固定サイズ値を返し、UIImageViewオブジェクトは現在表示されている画像のサイズを返します。

つまり、ドキュメンテーションを読むと、渡されるパラメーターに[webView sizeThatFits: ...]は少なくとも望ましいが必要なので、ロジックなしでソリューションに出くわしたようなものwidthです。彼のソリューションでは、パラメーターでwebView呼び出す前に、必要な幅がのフレームに設定されsizeThatFitsていCGSizeZeroます。したがって、私はこのソリューションが「チャンス」によってiOS 6で動作していることを維持します。

iOS 5.0以降で機能するという利点のある、より合理的なアプローチを想像しました...また、複数のwebView(プロパティwebView.scrollView.scrollEnabled = NOがに埋め込まれている)の複雑な状況でもscrollView

以下は、Layoutのレイアウトを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、と私は追加する必要がありましたコードの最後の部分は計算だったheightcontentSize私のカスタムのscrollViewこれらを埋め込むwebViewsが、それは簡単なようでした上記のトリックで計算された私webViewのを合計frame.size.heightすると...


3
これは、受け入れられているソリューションよりもはるかに洗練されており、文書化されていない未定義の動作に依存していません。これは、javascriptを挿入するよりもハックが少ないです。これを64回賛成できるといいのですが。
bugloaf

それはまさに私が説明しようとしたことです。ありがとうございました。そしてそれは、すべてのiOSバージョンで動作するように見えるという大きな利点です。
現象

ソリューションは確かに見栄えが良いですが、それでもiOS 4.xをターゲットにしている場合は注意してください。-[UIWebView scrollView]iOS 5.0以降でのみ使用できます。
Ortwin Gentz

2
私はこれと同様の解決策を試しましたが、これが機能するのは、Webビューまたはそれが埋め込まれているビューがすでに画面上にある場合に限られます。WebViewをビュー階層に追加する前にこれを試すと、結果は手動のサイズの高さになります。
k1th 2013年

1
他の解決策は常に機能するわけではありませんが、理由はわかりませんが、これでうまくいきます!!! これには正しい答えのタグを付ける必要があります。
Vassily 2013

37

Ortwinの答えがほとんどの時間しか機能しないことに気付いたため、この質問を復活させます...

このwebViewDidFinishLoadメソッドは複数回呼び出される場合があり、によって返される最初の値はsizeThatFits、最終的なサイズの一部にすぎません。次に、なんらかの理由で次のsizeThatFitswhenのwebViewDidFinishLoad起動が再度実行されると、以前と同じ値が誤って返されます。これは、同じコンテンツに対して、ある種の同時実行の問題であるかのようにランダムに発生します。私は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
    }
}

更新:私はコメントで述べたように、コンテンツが縮小したケースをキャッチしていないようです。すべてのコンテンツとOSバージョンに当てはまるかどうかわからない場合は、試してみてください。


Webページに画像やその他のアセットを含めましたか?これにより、デリゲートがさらに発砲する可能性があります。それを除けば、あなたの解決策webView.scalesPageToFit = YESは、上記のコメントで言及されている@Sijoのように与えない場合にのみ機能します。
Ortwin Gentz 2012

ええ、それはおそらくそれだけです-あなたの解決策は私のテキストのみの場合にうまく機能します。しかし、最後のデリゲート呼び出し中にsizeThatFitsが正しい値を与えないことが問題です...非常に奇妙です。それまでにどの呼び出しが最後になるかを知るために、それまでにsizeThatFitsを使用しないようにすることができますか?
キーランハーパー、

最初のデリゲート呼び出しでsizeThatFitsから正しい結果が得られた場合、後続の呼び出しを無視できませんか?
Ortwin Gentz 2012

はい、しかしそれは逆です:)最初のデリゲート呼び出しは間違ったsizeThatFitsを引き起こす可能性があり、それからその値が残ります。これは、sizeThatFitsが何らかの方法でWebビューを変更しているため、それ以降のsizeThatFits呼び出しでも同じ結果が得られるか、ロード中にフレームの設定が行われているようです。
キーランハーパー

1
このソリューションも正しく機能しません。大きいページを設定してから小さいページを設定すると、常に大きい値が返されます。内部のビューを「ゆるく」拡張しているようです。これの回避策はありますか?
Legoless 2015年

23

Xcode 8およびiOS 10では、Webビューの高さを決定します。あなたは身長を得ることができます

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

またはSwiftの場合

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

1
myWebView.scrollView.contentSize.height ..のWebViewコンテンツのサイズを使用して検索してみてください
のHardik Thakkar

8

単純な解決策は単に使用するwebView.scrollView.contentSizeことですが、これがJavaScriptで動作するかどうかはわかりません。JavaScriptが使用されていない場合、これは確実に機能します。

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


3

これは変だ!

私は解決策をテストし、両方sizeThatFits:[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]されないで私のために働い。

しかし、ウェブページのコンテンツの適切な高さを取得する興味深い簡単な方法を見つけました。現在、デリゲートメソッドで使用していscrollViewDidScroll:ます。

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

iOS 9.3シミュレーター/デバイスで確認済み、頑張ってください!

編集:

背景:HTMLコンテンツは、文字列変数とHTTPコンテンツテンプレートによって計算され、methodによって読み込まれloadHTMLString:baseURL:ます。登録されたJSスクリプトはありません。


3

iOS10のために、私はの0(ゼロ)の値になっていたdocument.heightそうすることdocument.body.scrollHeightのWebViewのドキュメントの高さを取得するためのソリューションです。この問題はでも解決できますwidth


2

私が使用しているUIWebViewサブビューではありません(したがって、ウィンドウ階層の一部ではない)のためのHTMLコンテンツのサイズを決定することUITableViewCells。切断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:]

すべてのa heightは1000を返します。

この場合の問題を解決するために、私はhttps://stackoverflow.com/a/11770883/9636を使用しました。ただし、私UIWebView.frame.widthがと同じ場合にのみ、このソリューションを使用し-[UIWebView sizeThatFits:] widthます。


1
私はこのスレッドのすべての例を試しましたが、「document.height」を使用してドキュメントの高さを取得することが最も信頼できます。追加のHTMLをコンテナー(DIVなど)の外に追加すると、私が実行したすべてのテストでgetElementById(...)が信頼できなくなります。document.heightはごちそうです。
ジョンロジャース

2

ここでの提案のどれも私の状況に役立ちませんでしたが、私は私に答えを与える何かを読みました。UIControllerの固定セットとそれに続くUIWebViewを備えたViewControllerがあります。UIコントロールがHTMLコンテンツに接続されているかのようにページ全体をスクロールさせたいので、UIWebViewでのスクロールを無効にし、親スクロールビューのコンテンツサイズを正しく設定する必要があります。

重要なヒントは、UIWebViewが画面にレンダリングされるまでサイズを正しく報告しないことです。そのため、コンテンツをロードするときに、コンテンツサイズを使用可能な画面の高さに設定しました。次に、viewDidAppearで、スクロールビューのコンテンツサイズを正しい値に更新します。ローカルコンテンツでloadHTMLStringを呼び出しているので、これはうまくいきました。loadRequestを使用している場合は、htmlの取得速度によっては、webViewDidFinishLoadのcontentSizeも更新する必要がある場合があります。

スクロールビューの非表示部分のみが変更されるため、ちらつきはありません。


viewDidAppearで終了するほど速くWebビューが読み込まれるのは、本当に幸運だと私は思います。これはおそらく、ビューがアニメーション表示されている場合にのみ機能するため、アニメーションはコンテンツをロードするのに十分な長さです。webViewDidFinishLoad安全なアプローチとして頼りにしたいと思います。
Ortwin Gentz 2014年

2

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>"];

その後の取り扱いを追加X-更新WebViewの高さのあなたに-schemeをshouldStartLoadWithRequest

    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に正しく表示されません


これは面白い!スクリプトパーツの機能と、必要に応じてスクリプトパーツをどのように調整できるかを説明できますか?高さを定期的にチェックするようですが、複数の時間間隔が関係していますか?
Kieran Harper

@KieranHarper、正確に何を説明する必要がありますか?
MingalevME 2016年

2

scrollviewのどこかでwebviewをサブビューとして使用する場合、高さの制約を一定の値に設定し、後でそれからアウトレットを作成して次のように使用できます。

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

1

私もこの問題で立ち往生していて、webViewの動的な高さを計算する場合は、最初にwebViewの幅を指定する必要があるので、jsの前に1行追加するとわかります。非常に正確な実際の高さ。

コードは次のように単純です:

-(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;

    //......
}

1

Xcode8 swift3.1:

  1. webViewの高さを0に設定します最初にます。
  2. webViewDidFinishLoad委任:

let height = webView.scrollView.contentSize.height

ステップ1がない場合、webview.height>実際のcontentHeightの場合、ステップ2はwebview.heightを返しますが、contentsize.heightは返しません。


これが、私のケースで必要なフレームを取得できなかった理由です。賛成
NSPratik

0

また、iOS 7では、前述のすべてのメソッドを適切に機能させるために、ビューコントローラーviewDidLoadメソッドに次のコードを追加します。

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

そうしないと、どちらのメソッドも正常に機能しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.