web-dev-qa-db-ja.com

UIWebViewのコンテンツサイズを決定する方法は?

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

116
Ortwin Gentz

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

もちろん、コンテンツが読み込まれるまで待つ必要があるため、コードを-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つのソリューションのうち、私はこれがより気に入っています。

249
Ortwin Gentz

この質問を復活させるのは、ほとんどの場合にしか機能しないというOrtwinの答えを見つけたからです...

webViewDidFinishLoadメソッドは複数回呼び出すことができ、sizeThatFitsによって返される最初の値は、最終的なサイズの一部にすぎません。何らかの理由で、sizeThatFitsが再度起動したときに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バージョンに当てはまるかどうかわからない場合は、試してみてください。

37
Kieran Harper

Xcode 8およびiOS 10では、Webビューの高さを決定します。使用して高さを取得できます

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

SwiftのOR

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

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

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

これは変です!

sizeThatFits:[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]の両方のソリューションをテストしましたではなく私のために働いています。

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

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

IOS 9.3シミュレータ/デバイスで検証済み、幸運を祈ります!

編集:

背景:htmlコンテンツは、文字列変数とHTTPコンテンツテンプレートによって計算され、メソッドloadHTMLString:baseURL:によってロードされ、そこに登録されたJSスクリプトはありません。

3
Itachi

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

3
iAnkit

[webView sizeThatFits:CGSizeZero]を使用して、コンテンツのサイズを把握できます。

3
Rengers

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

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

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

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

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

2
Eli Burke

サブビューではない(したがってウィンドウ階層の一部ではない)UIWebViewを使用して、UITableViewCellsのHTMLコンテンツのサイズを決定しています。切断されたUIWebViewは、-[UIWebView sizeThatFits:]でそのサイズを適切に報告しないことがわかりました。さらに、 https://stackoverflow.com/a/3937599/9636 で説明されているように、UIWebViewframeheightを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と同じ場合にのみ、このソリューションを使用します。

2
Heath Borders

HTMLにheavyiframeのような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.
    ...

追伸ObjectiveC/Swift-code内に時間遅延(SetTimeoutおよびsetInterval)を実装できます-それはあなた次第です。

追伸UIWebViewおよびFacebook埋め込みに関する重要な情報: 埋め込みFacebook投稿はUIWebViewで適切に表示されません

2
MingalevME

私もこの問題にこだわっており、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
user5047504

Xcode8 Swift3.1:

  1. WebViewの高さを設定最初。
  2. webViewDidFinishLoadデリゲート:

let height = webView.scrollView.contentSize.height

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

1
hstdt

また、iOS 7では、前述のすべてのメソッドが適切に機能するように、View Controller viewDidLoadメソッドにこれを追加します。

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

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

0