Jak określić rozmiar zawartości UIWebView?

Mam UIWebView z inną (pojedynczą stroną) treścią. Chciałbym dowiedzieć się CGSize zawartości, aby odpowiednio zmienić rozmiar widoków moich rodziców. Oczywisty -sizeThatFits: niestety po prostu zwraca bieżący rozmiar ramki widoku sieci Web.

Author: Ortwin Gentz, 2010-10-14

16 answers

Okazało się, że moje pierwsze zgadywanie za pomocą -sizeThatFits: nie było całkowicie błędne. Wydaje się, że działa, ale tylko wtedy, gdy ramka webView jest ustawiona na minimalny rozmiar przed wysłaniem -sizeThatFits:. Następnie możemy skorygować niewłaściwy rozmiar ramki o rozmiar dopasowania. Brzmi okropnie, ale nie jest tak źle. Ponieważ obie klatki zmieniamy bezpośrednio po sobie, widok nie jest aktualizowany i nie migocze.

Oczywiście musimy poczekać aż zawartość zostanie załadowana, więc umieszczamy kod do metody -webViewDidFinishLoad: delegate.

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

Powinienem zwrócić uwagę, że istnieje inne podejście (dzięki @GregInYEG) przy użyciu JavaScript. Nie wiem, które rozwiązanie działa lepiej.

Z dwóch hacky rozwiązań podoba mi się ten bardziej.

 247
Author: Ortwin Gentz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:35

Mam inne rozwiązanie, które działa świetnie.

Z jednej strony podejście i rozwiązanie Ortwina działa tylko z iOS 6.0 i nowszymi, ale nie działa poprawnie na iOS 5.0, 5.1 i 5.1.1, a z drugiej strony jest coś, czego nie lubię i nie mogę zrozumieć z podejściem Ortwina, jest to użycie metody [webView sizeThatFits:CGSizeZero] z parametrem CGSizeZero: Jeśli czytasz oficjalną dokumentację Apple o tych metodach i jej parametrze, mówi wyraźnie : {[26]]}

Domyślna implementacja tej metody zwraca rozmiar prostokąta obramowania widoku. Podklasy mogą nadpisać tę metodę, aby zwrócić wartość niestandardową na podstawie pożądanego układu dowolnych podwidów. Na przykład obiekt Uiswitch Zwraca wartość o stałym rozmiarze, która reprezentuje standardowy rozmiar widoku przełącznika, a obiekt UIImageView zwraca Rozmiar obrazu, który jest aktualnie wyświetlany.

Chodzi mi o to, że to tak jakby natknął się na swoje rozwiązanie bez żadnej logiki, bo czytając dokumentacja, parametr przekazany do [webView sizeThatFits: ...] powinien mieć co najmniej żądaną wartość width. W jego rozwiązaniu żądana szerokość jest ustawiana na ramkę webView przed wywołaniem sizeThatFits z parametrem CGSizeZero. Więc utrzymuję, że to rozwiązanie działa na iOS 6 przez "przypadek".

Wyobrażałem sobie bardziej racjonalne podejście, które ma tę zaletę, że działa na iOS 5.0 i Później... A także w złożonych sytuacjach, w których więcej niż jeden webView (z jego właściwością webView.scrollView.scrollEnabled = NO jest osadzony w scrollView.

Oto mój kod, aby wymusić Układ webView na żądanym width i uzyskać odpowiadający mu height z powrotem do samego webView:

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

Zauważ, że w moim przykładzie webView został osadzony w niestandardowym scrollView mającym inne webViews... Wszystkie te webViews miały swoje webView.scrollView.scrollEnabled = NO, a ostatnim fragmentem kodu, który musiałem dodać, było obliczenie height z contentSize mojego zwyczaju scrollView osadzania tych webViews, ale było to tak proste, jak podsumowanie moich webView frame.size.height obliczonych za pomocą triku opisanego powyżej...

 89
Author: Phenomena,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-29 10:56:06

Wskrzeszając to pytanie, ponieważ znalazłem odpowiedź Ortwina, która działa tylko przez większość czasu...

Metoda webViewDidFinishLoad może być wywołana więcej niż raz, a pierwsza wartość zwracana przez sizeThatFits jest tylko częścią tego, jaki powinien być ostateczny rozmiar. Następnie z jakiegokolwiek powodu następne wywołanie sizeThatFits, Gdy webViewDidFinishLoad zostanie ponownie wywołane, niepoprawnie zwróci tę samą wartość, co poprzednio! Stanie się to losowo dla tej samej treści, jakby był to jakiś problem z współbieżnością. Może to zachowanie ma zmieniał się w czasie, ponieważ buduję dla iOS 5 i również odkryłem, że sizeToFit działa w podobny sposób (chociaż wcześniej tak nie było?)

Zdecydowałem się na to proste rozwiązanie:

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

Swift (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
    }
}

Aktualizacja: znalazłem, jak wspomniano w komentarzach, nie wydaje się, aby uchwycić przypadek, w którym zawartość się skurczyła. Nie wiem, czy to prawda dla wszystkich treści i wersji systemu operacyjnego, spróbuj.

 36
Author: Kieran Harper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-30 14:17:28

W Xcode 8 i iOS 10, aby określić wysokość widoku sieci web. możesz uzyskać wysokość za pomocą

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

Lub dla Swift

 func webViewDidFinishLoad(aWebView:UIWebView){
        let height: Float = (aWebView.stringByEvaluatingJavaScriptFromString("document.body.scrollHeight")?.toFloat())!
        print("Webview height is::\(height)")
    }
 22
Author: Hardik Thakkar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-16 07:10:56

Prostym rozwiązaniem byłoby użycie webView.scrollView.contentSize, ale nie wiem, czy to działa z JavaScript. Jeśli nie ma JavaScript używany to działa na pewno:

- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
    CGSize contentSize = aWebView.scrollView.contentSize;
    NSLog(@"webView contentSize: %@", NSStringFromCGSize(contentSize));
}
 8
Author: OemerA,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-16 07:10:05

AFAIK możesz użyć [webView sizeThatFits:CGSizeZero], aby dowiedzieć się, jaki jest rozmiar zawartości.

 3
Author: Rengers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-14 18:17:58

To dziwne!

Przetestowałem rozwiązania zarówno sizeThatFits:, jak i [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"], a nie działają dla mnie.

Znalazłem jednak ciekawy, łatwy sposób na uzyskanie odpowiedniej wysokości zawartości strony internetowej. Obecnie używam go w mojej metodzie delegata scrollViewDidScroll:.

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

Zweryfikowany w iOS 9.3 symulator / urządzenie, powodzenia!

EDIT:

Tło: zawartość html jest obliczana przez moją zmienną string i szablon zawartości HTTP, ładowany metodą loadHTMLString:baseURL:, bez zarejestrowanego JS Skrypty tam.

 3
Author: Itachi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-08 09:15:40

Dla iOS10 otrzymywałem 0 (zero) wartości document.height więc {[1] } jest rozwiązaniem do uzyskania wysokości dokumentu w widoku Webview. Problem można rozwiązać również dla width.

 3
Author: iAnkit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-07 04:34:24

Używam UIWebView, który nie jest subview (a więc nie jest częścią hierarchii okien) do określania rozmiarów zawartości HTML dla UITableViewCells. Okazało się, że rozłączony UIWebView nie zgłasza swojego rozmiaru poprawnie za pomocą -[UIWebView sizeThatFits:]. Dodatkowo, jak wspomniano w https://stackoverflow.com/a/3937599/9636 , musisz ustawić UIWebView ' s frame height do 1 w celu uzyskania odpowiedniej wysokości w ogóle.

Jeśli wysokość UIWebView jest zbyt duża (tzn. masz ją ustawioną na 1000, ale rozmiar zawartości HTML jest tylko 500): {]}

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

Wszystkie zwracają height z 1000.

Aby rozwiązać mój problem w tym przypadku, użyłem https://stackoverflow.com/a/11770883/9636 , na które stanowczo głosowałem. Jednak używam tego rozwiązania tylko wtedy, gdy mój UIWebView.frame.width jest taki sam jak -[UIWebView sizeThatFits:] width.

 2
Author: Heath Borders,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:10

Żadna z sugestii nie pomogła mi w mojej sytuacji, ale przeczytałem coś, co dało mi odpowiedź. Mam ViewController ze stałym zestawem kontrolek UI, a następnie UIWebView. Chciałem, aby cała strona przewijała się tak, jakby kontrolki interfejsu użytkownika były podłączone do zawartości HTML, więc wyłączam przewijanie w widoku UIWebView i muszę ustawić rozmiar zawartości nadrzędnego widoku przewijania poprawnie.

Ważną wskazówką okazało się to, że UIWebView nie zgłasza poprawnie swojego rozmiaru do czasu renderowania na ekranie. Podczas ładowania zawartości ustawiam Rozmiar zawartości na dostępną wysokość ekranu. Następnie w viewDidAppear aktualizuję Rozmiar zawartości widoku przewijania do odpowiedniej wartości. To działało dla mnie, ponieważ dzwonię loadHTMLString na lokalnej treści. Jeśli używasz loadRequest, może być konieczne zaktualizowanie contentSize w webViewDidFinishLoad również, w zależności od tego, jak szybko zostanie pobrany html.

Nie ma migotania, bo tylko niewidoczna część zwoju Widok został zmieniony.

 2
Author: Eli Burke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-30 18:10:36

Facebook, Instagram, Instagram-embedes) realne rozwiązanie jest o wiele trudniejsze, najpierw zawiń swój 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>"];

Następnie dodaj obsługę x-update-webview-height - scheme do 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;
    }
    ...

I na koniec dodaj następujący kod wewnątrz 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.
    ...

P. S. możesz zaimplementować opóźnianie czasu (SetTimeout i setInterval) wewnątrz twojego ObjectiveC / Swift-code-to zależy od Ciebie.

Facebook facebook post nie wyświetla się poprawnie w UIWebView }}}}}}}}}} {\displaystyle {\displaystyle {\displaystyle {\displaystyle {\displaystyle {\displaystyle {\displaystyle {\displaystyle {\displaystyle {\displaystyle]}
 2
Author: MingalevME,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:10

Gdy używasz webview jako subview gdzieś w scrollview, możesz ustawić ograniczenie wysokości na jakąś stałą wartość, a później zrobić z niego wyjście i używać go w następujący sposób:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    webView.scrollView.scrollEnabled = NO;
    _webViewHeight.constant = webView.scrollView.contentSize.height;
}
 2
Author: younke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-13 05:14:06

Jestem również zatrzymany na ten problem, a następnie zdałem sobie sprawę, że jeśli chcę obliczyć dynamiczną wysokość webView, muszę powiedzieć szerokość webView pierwszy, więc dodaję jedną linię przed js i okazuje się, że mogę uzyskać bardzo dokładną rzeczywistą wysokość.

Kod jest prosty tak:

-(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
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-25 07:26:09

Również w iOS 7 do poprawnego działania wszystkich wymienionych metod dodaj to w kontrolerze widoku viewDidLoad Metoda:

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

W przeciwnym razie żadna z metod nie będzie działać tak, jak powinna.

 0
Author: Nikolay Shubenkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-09 09:56:03

Xcode83.1:

  1. ustaw najpierw wysokość widoku sieci web na 0 .
  2. In webViewDidFinishLoad Delegat:

let height = webView.scrollView.contentSize.height

Bez Kroku 1, jeśli webview.height > actual contentHeight, Krok 2 zwróci webview.wysokość, ale nie zawartość.wzrost.

 0
Author: hstdt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-24 09:25:06

Mam ten sam problem, jeśli UIWebView załaduje różne adresy URL, TYLKO uzyskaj max contentSize

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if (![keyPath isEqualToString:@"contentSize"]) return;
    CGSize vWebContentSize = [self.vcWeb.vWeb.wvBaseMain sizeThatFits:CGSizeZero];
    self.vcWeb.view.height = vWebContentSize.height;
    [self updateContainerContentSize];
}
 0
Author: OutMan_Coder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-04 08:34:39