Как определить размер содержимого UIWebView? - PullRequest
116 голосов
/ 14 октября 2010

У меня есть UIWebView с различным (одной страницей) контентом.Я хотел бы узнать CGSize содержимого, чтобы соответствующим образом изменить размеры моих родительских представлений.Очевидное -sizeThatFits:, к сожалению, просто возвращает текущий размер фрейма webView.

Ответы [ 15 ]

249 голосов
/ 15 октября 2010

Оказалось, что мое первое предположение с использованием -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
}

Я должен отметить, что есть другой подход (спасибо @GregInYEG) с использованием JavaScript. Не уверен, какое решение работает лучше.

Из двух хакерских решений мне больше нравится это.

90 голосов
/ 27 октября 2012

У меня есть другое решение, которое прекрасно работает.

С одной стороны, подход и решение Ortwin работает только с iOS 6.0 и более поздними версиями, но не работает корректно на iOS 5.0, 5.1 и 5.1.1 и нас другой стороны, есть кое-что, что мне не нравится и не может понять с помощью подхода Ортвина, это использование метода [webView sizeThatFits:CGSizeZero] с параметром CGSizeZero: если вы читаете официальную документацию Apple об этих методах и их параметре,в нем четко сказано:

Реализация этого метода по умолчанию возвращает размерную часть прямоугольника границ вида.Подклассы могут переопределить этот метод, чтобы возвращать пользовательское значение, основанное на желаемой компоновке любых подпредставлений.Например, объект UISwitch возвращает значение фиксированного размера, которое представляет стандартный размер представления переключателя, а объект UIImageView возвращает размер изображения, которое он отображает в данный момент.

Я имею в виду, чтоПохоже, он наткнулся на свое решение без какой-либо логики, потому что, читая документацию, параметр, переданный в [webView sizeThatFits: ...], должен, по крайней мере, иметь желаемый width.С его решением желаемая ширина устанавливается в кадр webView перед вызовом sizeThatFits с параметром CGSizeZero.Поэтому я утверждаю, что это решение работает на iOS 6 «случайно».

Я представил более рациональный подход, который имеет преимущество работы на iOS 5.0 и более поздних версиях ... А также в сложных ситуациях, когда более чемодин веб-вид (со свойством 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 из contentSize моего обычая scrollView с вложением этих webViews, но это было так же просто, как суммирование моих webView * frame.size.height, вычисленных с помощью трюка, описанного выше ...

37 голосов
/ 02 августа 2012

Воскрешение этого вопроса, потому что я нашел ответ Ортвина только на работу БОЛЬШЕ времени ...

Метод 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;
}

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

Обновление: я обнаружил, как упоминалось в комментариях, похоже, что это не соответствует случаю, когда контент сократился. Не уверен, верно ли это для всего контента и версии ОС, попробуйте.

22 голосов
/ 09 сентября 2016

В Xcode 8 и iOS 10 для определения высоты веб-просмотра.Вы можете получить высоту используя

- (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)")
    }
8 голосов
/ 10 августа 2013

Простым решением было бы просто использовать webView.scrollView.contentSize, но я не знаю, работает ли это с JavaScript.Если JavaScript не используется, это точно работает:

- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
    CGSize contentSize = aWebView.scrollView.contentSize;
    NSLog(@"webView contentSize: %@", NSStringFromCGSize(contentSize));
}
3 голосов
/ 03 октября 2016

Для iOS10 я получил 0 (ноль) значение document.height, поэтому document.body.scrollHeight - это решение для получения высоты документа в Webview.Проблема может быть решена также за width.

3 голосов
/ 08 сентября 2016

Это странно!

Я протестировал решения, которые sizeThatFits: и [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] НЕ работают на меня.

Однако я нашел интересный простой способ получить нужную высоту содержимого веб-страницы. В настоящее время я использовал его в своем методе делегата scrollViewDidScroll:.

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

Проверено в iOS 9.3 Симулятор / Устройство, удачи!

EDIT:

Справочная информация. Содержание html рассчитывается с помощью моей строковой переменной и шаблона содержимого HTTP, загруженного методом loadHTMLString:baseURL:, там нет зарегистрированных JS-скриптов.

3 голосов
/ 14 октября 2010

AFAIK, вы можете использовать [webView sizeThatFits:CGSizeZero], чтобы выяснить размер контента.

2 голосов
/ 13 декабря 2015

При использовании webview как подпредставления где-то в scrollview, вы можете установить ограничение высоты на некоторое постоянное значение, а затем сделать из него выход и использовать его следующим образом:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    webView.scrollView.scrollEnabled = NO;
    _webViewHeight.constant = webView.scrollView.contentSize.height;
}
2 голосов
/ 06 октября 2015

Если ваш HTML содержит тяжелый HTML-контент, такой как iframe (например, 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-update-webview-height -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.
    ...

P.S. Вы можете реализовать временную задержку (SetTimeout и setInterval) внутри своего ObjectiveC / Swift-кода - это ваше дело.

P.S.S. Важная информация о UIWebView и Facebook Embeds: Встроенная запись Facebook не отображается должным образом в UIWebView

...