Як визначити розмір вмісту UIWebView?


116

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


1
Я не впевнений, але, можливо, відповіді на це питання допоможуть: stackoverflow.com/questions/745160/…
— Грег,

Відповіді:


250

Виявилося, що моя перша здогадка про використання -sizeThatFits:була не зовсім помилковою. Здається, це працює, але лише якщо рамка веб-перегляду встановлена ​​на мінімальний розмір до надсилання -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);
}

Швидкий 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. Не впевнений, яке рішення працює краще.

З двох химерних рішень мені це більше подобається.


Я спробував ваш підхід. Проблема, яку я відчув, полягає в тому, що прокрутки немає. Будь-які пропозиції?
— тестування

1
Я дізнався, що проблема з прокруткою виникає через змінену висоту веб-перегляду. Чи потрібно змінювати розміри UIView?
— тестування

Не впевнений, у чому ваша проблема. Можливо, ви можете залишити нове запитання з якимсь кодом чи описом?
— Ортвін Генц

@testing, @Bogatyr: Правильно, якщо ваш UIWebViewпоказник вище, ніж ваш батьківський вигляд, вам потрібно вбудувати його в UIScrollView.
— Ортвін Генц

7
якщо я даю webView.scalesPageToFit = ТАК; тоді працює лише це рішення .. дякую за рішення ..
— СП

92

У мене є ще одне рішення, яке чудово працює.

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

Реалізація за замовчуванням цього методу повертає частину розміру прямокутника меж перегляду. Підкласи можуть замінити цей метод, щоб повернути спеціальне значення на основі бажаного макета будь-яких підпрезентацій. Наприклад, об'єкт UISwitch повертає значення фіксованого розміру, яке представляє стандартний розмір перегляду комутатора, а об’єкт UIImageView повертає розмір зображення, яке він відображає в даний час.

Я маю на увазі те, що це наче він натрапив на своє рішення без будь-якої логіки, тому що, читаючи документацію, параметр, який передається, [webView sizeThatFits: ...]повинен хоча б мати бажане width. З його розчином бажаної шириною встановлюються на webViewраму «s перед викликом sizeThatFitsз CGSizeZeroпараметром. Тому я вважаю, що це рішення працює на iOS 6 "випадково".

Я уявив більш раціональний підхід, який має перевагу роботи для iOS 5.0 і пізніших версій ... А також у складних ситуаціях, коли більше одного веб-перегляду (з його властивістю webView.scrollView.scrollEnabled = NOвбудовано в a 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.
}

Швидкий 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обчислюється за допомогою трюку описано вище ...


3
Це набагато більш елегантно, ніж прийняте рішення, і воно не покладається на недокументовану та невизначену поведінку. Це набагато менше хакі, ніж вставляти JavaScript. Я б хотів, щоб я міг підтвердити це 64 рази.
— bugloaf

Саме це я і намагався пояснити. Дякую. І великою перевагою є те, що він, здається, працює під усіма версіями iOS.
— Феномени

Рішення виглядає справді приємно, але будьте обережні, якщо ви все ще орієнтуєтесь на iOS 4.x. -[UIWebView scrollView]доступний лише в iOS 5.0 або новішої версії.
— Ортвін Генц

2
Я спробував це і подібне рішення, і виявилося, що це працює лише, якщо веб-перегляд або перегляд, в який він вбудований, вже є на екрані. Якщо ви спробуєте це перед тим, як додати веб-перегляд до ієрархії перегляду, результатом буде висота розміру вручну.
— k1th

1
інше рішення працює не кожен раз, не знаю чому, але це робить трюк !!! це має бути позначено правильною відповіддю.
— Василь

37

Воскрешаючи це запитання, тому що я знайшов відповідь Ортвіна лише на МОСТЬ часу ...

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

Оновлення: Я виявив, як згадувалося в коментарях, це, схоже, не сприймає випадок, коли вміст зменшився. Не впевнений, чи це правда для всього вмісту та версії ОС, спробуйте.


Ви включили зображення або інші об’єкти на свою веб-сторінку? Це може спричинити додатковий вистріл делегата. Крім того, ваше рішення працює лише тоді, коли ви не даєте, webView.scalesPageToFit = YESяк @Sijo, зазначений у коментарі вище.
— Ортвін Генц

Так, це, мабуть, так - ваше рішення чудово працює в моїх текстових випадках. Але проблема sizeThatFits, що не дає правильного значення під час остаточного виклику делегата, є проблемою ... дуже дивно. Як ми можемо знати, який дзвінок буде останнім, щоб ми не намагалися до цього часу використовувати sizeThatFits?
— Kieran Harper

Якщо під час першого дзвінка делегата ви отримуєте правильний результат від sizeThatFits, чи не можете ви просто ігнорувати будь-які наступні дзвінки?
— Ортвін Генц

Так, але це навпаки :) Перший виклик делегата може призвести до неправильного розміруThatFits, потім це значення дотримується. Це як би або sizeThatFits якимось чином змінює веб-перегляд, щоб будь-які подальші виклики sizeThatFits давали той же результат, або це встановлення кадру, поки він все ще завантажується, що це робить.
— Kieran Harper

1
Це рішення також не працює належним чином. Як тільки ви встановите більшу сторінку, а потім встановите меншу, вам завжди буде повернено більше значення. Схоже, що "ліниво" розширює внутрішні погляди. Будь-які обходи для цього?
— Legoless

23

У 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)")
    }

1
Спробуйте знайти розмір вмісту веб-перегляду .. myWebView.scrollView.contentSize.height
— Hardik Thakkar

8

Простим рішенням було б просто користуватися, webView.scrollView.contentSizeале я не знаю, чи це працює з JavaScript. Якщо JavaScript не використовується, це працює точно:

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

3

AFAIK, за допомогою якого можна [webView sizeThatFits:CGSizeZero]визначити розмір вмісту.


3

Це дивно!

Я тестував рішення як sizeThatFits:і [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]є НЕ працюю для мене.

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

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

Перевірено в iOS 9.3 Simulator / Device, удачі!

Редагувати:

Передумови: Вміст html обчислюється за моєю змінною рядка та шаблоном вмісту HTTP, завантаженою методом 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 , ви повинні встановити значення UIWebView" frame height1", щоб отримати правильну висоту взагалі.

Якщо UIWebViewвисота 'занадто велика (тобто у вас встановлено 1000, але розмір вмісту HTML становить лише 500):

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

Усі повертають height1000.

Щоб вирішити свою проблему в цьому випадку, я використав https://stackoverflow.com/a/11770883/9636 , яку я гідно проголосував. Однак я використовую це рішення лише тоді, коли мій UIWebView.frame.widthтакий самий, як і -[UIWebView sizeThatFits:] width.


1
Я спробував усі приклади в цій темі, і використання "document.height" для отримання висоти документа є найбільш надійним. Додатковий HTML може бути доданий поза контейнером (наприклад, DIV), що робить getElementById (...) ненадійним у всіх тестах, які я виконував. document.height працює частуванням.
— Джон Роджерс

2

Жодна із пропозицій тут не допомогла мені зі своєю ситуацією, але я прочитав щось, що дало мені відповідь. У мене є ViewController з фіксованим набором елементів користувальницького інтерфейсу, за яким слід UIWebView. Я хотів, щоб вся сторінка прокручувалася так, ніби елементи керування інтерфейсом були підключені до вмісту HTML, тому я відключаю прокрутку на UIWebView і повинен правильно встановити розмір вмісту батьківського перегляду прокрутки.

Важливим підказом виявилось те, що UIWebView не повідомляє про свій розмір правильно, доки не буде виведено на екран. Тому коли я завантажую вміст, я встановлюю розмір вмісту на доступну висоту екрана. Потім в viewDidAppear я поновлюю розмір вмісту прокрутки до правильного значення. Це працювало для мене, тому що я викликаю loadHTMLString на локальному вмісті. Якщо ви використовуєте loadRequest, можливо, знадобиться оновити contentSize у webViewDidFinishLoad також залежно від швидкості отримання файлу html.

Не буває мерехтіння, оскільки змінюється лише невидима частина перегляду прокрутки.


Я боюся, що це чиста удача, що веб-перегляд завантажується досить швидко, що він закінчений на viewDidAppear. Можливо, це працює лише в тому випадку, якщо представлення виглядає анімованим, тому анімація є достатньо довгою для завантаження вмісту. Я б краще покластися на webViewDidFinishLoadбезпечний підхід.
— Ортвін Генц

2

Якщо ваш 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 -sheme в свою слідStartLoadWithRequest :

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

І, нарешті, додайте такий код у свій макетSubviews :

    ...
    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 Ви можете впроваджувати затримку часу (SetTimeout та setInterval) всередині вашого ObjectiveC / Swift-коду - саме вам.

PSS Важлива інформація про UIWebView та Facebook Вбудовування : Вбудований пост у Facebook не відображається належним чином у UIWebView


Це цікаво! Чи можете ви описати, що робить сценарій та як її можна змінити за потреби? Схоже, він буде періодично перевіряти висоту, але задіяно кілька часових інтервалів?
— Kieran Harper

@KieranHarper, що саме вам потрібно описати?
— MingalevME

2

Використовуючи веб-перегляд як підвид, десь у перегляді прокрутки, ви можете встановити обмеження висоти на якесь постійне значення, а пізніше зробити з нього розетку і використовувати її як:

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

1

Я також зациклювався на цій проблемі, тоді я зрозумів, що якщо я хочу обчислити динамічну висоту 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;

    //......
}

1

Xcode8 swift3.1:

  1. Встановіть висоту webView спочатку 0 .
  2. У webViewDidFinishLoadДелегат:

let height = webView.scrollView.contentSize.height

Без кроку 1, якщо webview.height> фактичний contentHeight, крок 2 поверне webview.height, але не contentize.height.


Це причина, чому я не міг отримати потрібний кадр у моєму випадку .. аплодовано
— NSPratik

0

Також в iOS 7 для належної роботи всіх згаданих методів додайте це у свій viewDidLoadметод контролера перегляду :

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

Інакше жоден із методів не працював би як слід.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.