¿Cómo determinar el tamaño del contenido de un UIWebView?

Tengo un UIWebView con contenido diferente (página única). Me gustaría averiguar el CGSize del contenido para cambiar el tamaño de mis vistas padre de forma adecuada. El obvio -sizeThatFits: lamentablemente solo devuelve el tamaño de fotogtwig actual de la vista web.

Resultó que mi primera suposición usando -sizeThatFits: no estaba del todo mal. Parece que funciona, pero solo si el marco de la vista web está configurado en un tamaño mínimo antes de enviar -sizeThatFits: Después de eso, podemos corregir el tamaño de fotogtwig incorrecto por el tamaño apropiado. Esto suena terrible, pero en realidad no es tan malo. Como hacemos ambos cambios de cuadros uno tras otro, la vista no se actualiza y no parpadea.

Por supuesto, tenemos que esperar hasta que el contenido se haya cargado, así que colocamos el código en el método -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); } 

Debo señalar que hay otro enfoque (gracias @GregInYEG) usando JavaScript. No estoy seguro de qué solución funciona mejor.

De dos soluciones hacky me gusta esta mejor.

Tengo otra solución que funciona genial.

Por un lado, el enfoque y la solución de Ortwin solo funcionan con iOS 6.0 y posterior, pero no funcionan correctamente en iOS 5.0, 5.1 y 5.1.1, y por otro lado hay algo que no me gusta y no puedo entender con el enfoque de Ortwin, es el uso del método [webView sizeThatFits:CGSizeZero] con el parámetro CGSizeZero : si lees la documentación de Apple Official sobre este método y su parámetro, dice claramente:

La implementación predeterminada de este método devuelve la porción de tamaño del rectángulo de límites de la vista. Las subclases pueden anular este método para devolver un valor personalizado en función del diseño deseado de cualquier subvista. Por ejemplo, un objeto UISwitch devuelve un valor de tamaño fijo que representa el tamaño estándar de una vista de interruptor, y un objeto UIImageView devuelve el tamaño de la imagen que está mostrando actualmente.

Lo que quiero decir es que es como si hubiera encontrado su solución sin ninguna lógica, porque al leer la documentación, el parámetro pasado a [webView sizeThatFits: ...] debería tener al menos el width deseado. Con su solución, el ancho deseado se establece en el marco de sizeThatFits antes de llamar a sizeThatFits con un parámetro CGSizeZero . Así que mantengo que esta solución está funcionando en iOS 6 por “casualidad”.

Imaginé un enfoque más racional, que tiene la ventaja de trabajar para iOS 5.0 y posterior … Y también en situaciones complejas donde más de una vista web (con su propiedad webView.scrollView.scrollEnabled = NO está incrustada en un scrollView .

Aquí está mi código para forzar el diseño de la vista webView al width deseado y obtener el ajuste de height correspondiente a la propia vista 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. } 

Tenga en cuenta que en mi ejemplo, la vista webView estaba incrustada en una vista de scrollView personalizada que tenía otras webViews … Todas estas webViews tenían su webView.scrollView.scrollEnabled = NO , y la última pieza de código que tuve que agregar fue el cálculo de la height de el contentSize scrollView de mi scrollView personalizado que incorpora estas webViews , pero fue tan fácil como sumr el webView mi frame.size.height calculado con el truco descrito anteriormente …

Resurgió esta pregunta porque la respuesta de Ortwin me pareció que solo funcionaba la mayor parte del tiempo …

El método webViewDidFinishLoad se puede webViewDidFinishLoad más de una vez, y el primer valor devuelto por sizeThatFits es solo una parte de lo que debería ser el tamaño final. Entonces, por cualquier razón, la siguiente llamada a sizeThatFits cuando webViewDidFinishLoad nuevamente devolverá el mismo valor que antes. Esto ocurrirá al azar para el mismo contenido como si fuera algún tipo de problema de concurrencia. Tal vez este comportamiento ha cambiado con el tiempo, porque estoy sizeToFit para iOS 5 y también he descubierto que sizeToFit funciona de la misma manera (¿aunque antes no?)

Me he decidido por esta simple solución:

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

Actualización: he encontrado lo mencionado en los comentarios, esto no parece captar el caso donde el contenido se ha reducido. No estoy seguro si es cierto para todo el contenido y la versión del sistema operativo, pruébelo.

En Xcode 8 e iOS 10 para determinar el alto de una vista web. puedes obtener altura usando

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

O para Swift

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

Una solución simple sería simplemente usar webView.scrollView.contentSize pero no sé si esto funciona con JavaScript. Si no se utiliza JavaScript, esto funciona con certeza:

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

AFAIK puedes usar [webView sizeThatFits:CGSizeZero] para descubrir su tamaño de contenido.

¡Esto es extraño!

sizeThatFits: las soluciones sizeThatFits: y [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] NO funcionan para mí.

Sin embargo, encontré una manera interesante y fácil de obtener la altura correcta del contenido de la página web. Actualmente, lo utilicé en mi método delegado scrollViewDidScroll:

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

Verificado en iOS 9.3 simulador / dispositivo, ¡buena suerte!

EDITAR:

Antecedentes: el contenido html se calcula mediante mi variable de cadena y plantilla de contenido HTTP, cargada por el método loadHTMLString:baseURL: no hay scripts JS registrados allí.

Para iOS10, obtuve el valor 0 (cero) de document.height para document.body.scrollHeight es la solución para obtener la altura del documento en Webview. El problema se puede resolver también para el width .

Estoy usando un UIWebView que no es una subvista (y por lo tanto no es parte de la jerarquía de la ventana) para determinar el tamaño del contenido HTML para UITableViewCells . Descubrí que el UIWebView desconectado no informa correctamente su tamaño con -[UIWebView sizeThatFits:] . Además, como se menciona en https://stackoverflow.com/a/3937599/9636 , debe establecer la height frame UIWebView en 1 para obtener la altura adecuada.

Si la altura de UIWebView es demasiado grande (es decir, tiene un valor de 1000, pero el tamaño del contenido HTML es de solo 500):

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

Todos devuelven una height de 1000.

Para resolver mi problema en este caso, utilicé https://stackoverflow.com/a/11770883/9636 , que voté diligentemente. Sin embargo, solo uso esta solución cuando mi UIWebView.frame.width es igual que el -[UIWebView sizeThatFits:] width .

Ninguna de las sugerencias aquí me ayudó con mi situación, pero leí algo que me dio una respuesta. Tengo un ViewController con un conjunto fijo de controles de UI seguido de un UIWebView. Quería que la página completa se desplazara como si los controles de la UI estuvieran conectados al contenido HTML, por lo que desactivo el desplazamiento en UIWebView y luego debo establecer correctamente el tamaño de contenido de una vista de desplazamiento principal.

La sugerencia importante resultó ser que UIWebView no informa su tamaño correctamente hasta que se procesa en la pantalla. Entonces, cuando cargo el contenido, configuro el tamaño del contenido a la altura de la pantalla disponible. Luego, en viewDidAppear, actualizo el tamaño del contenido de la vista de desplazamiento al valor correcto. Esto funcionó para mí porque estoy llamando a loadHTMLString en el contenido local. Si está utilizando loadRequest, es posible que deba actualizar contentSize en webViewDidFinishLoad también, según la rapidez con que se recupere el html.

No parpadea, porque solo se modifica la parte invisible de la vista de desplazamiento.

Si su HTML contiene contenidos pesados ​​de HTML como iframe (es decir, facebook-, twitter, instagram-embeds) la solución real es mucho más difícil, primero ajuste su HTML:

 [htmlContent appendFormat:@"", [[LocalizationStore instance] currentTextDir], [[LocalizationStore instance] currentLang]]; [htmlContent appendFormat:@""]; [htmlContent appendString:@""]; [htmlContent appendFormat:@"..."]; // Rest of your HTML -section [htmlContent appendFormat:@""]; [htmlContent appendFormat:@""]; [htmlContent appendFormat:@"
"]; // !important https://stackoverflow.com/a/8031442/1046909 [htmlContent appendFormat:@"..."]; // Your HTML-content [htmlContent appendFormat:@"
"]; // [htmlContent appendFormat:@""]; [htmlContent appendFormat:@""];

A continuación, agregue el manejo de x-update-webview-height -scheme en su 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; } ... 

Y finalmente agregue el siguiente código dentro de su 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. ... 

PD: puede implementar el retraso de tiempo (SetTimeout y setInterval) dentro de su código ObjectiveC / Swift, depende de usted.

Información importante sobre UIWebView y Facebook: la publicación de Facebook incrustada no se muestra correctamente en UIWebView

Al usar webview como subvista en algún lugar de scrollview, puede establecer la restricción de altura en algún valor constante y luego hacer una salida de ella y usarla como:

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

También estoy atascado en este problema, entonces me di cuenta de que si quiero calcular la altura dinámica de la vista web, primero tengo que decir el ancho de la vista web, así que agrego una línea antes de js y resulta que puedo obtener altura real muy precisa.

El código es simple como este:

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

También en iOS 7 para el correcto funcionamiento de todos los métodos mencionados, agregue esto en su controlador de vista método viewDidLoad :

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

De lo contrario, ninguno de los métodos funcionaría como debería.

Xcode8 swift3.1:

  1. Establezca la altura de webView en 0 primero.
  2. En delegado webViewDidFinishLoad :

let height = webView.scrollView.contentSize.height

Sin el paso 1, si webview.height> contentHeight real, el paso 2 devolverá webview.height pero no contentará.height.

Tengo el mismo problema, si UIWebView carga diferentes URL, solo obtiene max contentSize

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)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]; }