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

Tengo una UIWebView con contenido diferente (una sola página). Me gustaría averiguar el CGSize del contenido para cambiar el tamaño de las vistas de mis padres de manera adecuada. El obvio -sizeThatFits: desgracia, simplemente devuelve el tamaño de fotogtwig actual de la webView.

Resultó que mi primera suposition usando -sizeThatFits: no estaba completamente equivocado. Parece que funciona, pero solo si el marco de la webView se establece en un tamaño mínimo antes de enviar -sizeThatFits: Después de eso, podemos corregir el tamaño incorrecto del marco por el tamaño de ajuste. Esto suena terrible, pero en realidad no es tan malo. Dado que hacemos ambos cambios de marco justo después de cada uno, la vista no se actualiza y no parpadea.

Por supuesto, tenemos que esperar hasta que el contenido se haya cargado, por lo 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); } 

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

De dos soluciones hacky, me gusta más.

Tengo otra solución que funciona bien.

Por un lado, el enfoque y solución de Ortwin solo funciona con iOS 6.0 y posterior, pero no funciona 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 lee la documentation oficial de Apple sobre estos methods y su parámetro, dice claramente:

La implementación pnetworkingeterminada de este método devuelve la porción de tamaño del rectángulo de límites de la vista. Las subclasss pueden anular este método para devolver un valor personalizado en function del layout deseado de cualquier subvención. Por ejemplo, un object UISwitch devuelve un valor de tamaño fijo que representa el tamaño estándar de una vista de conmutador, y un object UIImageView devuelve el tamaño de la image que está visualizando actualmente.

Lo que quiero decir es que es como si se hubiera encontrado con su solución sin ninguna lógica, porque al leer la documentation, el parámetro pasado a [webView sizeThatFits: ...] debería tener al less el width deseado. Con su solución, el ancho deseado se establece en el webView la webView antes de llamar a sizeThatFits con un parámetro CGSizeZero . Por lo tanto, mantengo que esta solución funciona en iOS 6 por "casualidad".

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

Aquí está mi código para forzar el Layout de la webView al width deseado y get la height correspondiente establecida de nuevo en la 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 desinetworking 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 count que, en mi ejemplo, el webView estaba embedded en un scrollView personalizado 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 the contentSize de mi scrollView personalizado que incrusta estas webViews , pero fue tan fácil como sumr el webView my frame.size.height calculado con el truco descrito anteriormente …

Resurgiendo esta pregunta porque encontré que la respuesta de Ortwin solo funcionaba la mayor parte del time …

El método webViewDidFinishLoad se puede llamar 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 próxima llamada a sizeThatFits cuando webViewDidFinishLoad se dispara de nuevo incorrectamente devolverá el mismo valor que antes! Esto sucederá aleatoriamente para el mismo contenido como si se tratara de algún tipo de problema de concurrency. Tal vez este comportamiento haya cambiado con el time, porque estoy desarrollando para iOS 5 y también he descubierto que sizeToFit funciona de la misma manera (aunque anteriormente esto no)

Me he decidido por esta solución simple:

 - (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 como se menciona en los comentarios, esto no parece captar el caso en que el contenido se ha networkingucido. No estoy seguro de si es cierto para todo el contenido y la versión del sistema operativo, pruébalo.

En Xcode 8 y iOS 10 para determinar la altura de una vista web. puedes get estatura 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 usa JavaScript, esto funciona con security:

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

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

Estoy usando una UIWebView que no es una subvista (y, por lo tanto, no forma parte de la jerarquía de windows) para determinar los tamaños de 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 get la altura adecuada.

Si la altura de UIWebView es demasiado grande (es decir, la has configurado en 1000, pero el tamaño de contenido HTML es 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 utilizo esta solución cuando mi UIWebView.frame.width es el mismo que el -[UIWebView sizeThatFits:] .

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

El consejo importante resultó ser que UIWebView no informa correctamente su tamaño hasta que se visualiza en la pantalla. Entonces, cuando carga el contenido, configuro el tamaño del contenido a la altura de la pantalla disponible. Entonces, en viewDidAppear actualizo el tamaño de contenido de scrollview al valor correcto. Esto funcionó para mí porque estoy llamando loadHTMLString en el contenido local. Si está utilizando loadRequest, es posible que también necesite actualizar contentSize en webViewDidFinishLoad, dependiendo de qué tan rápido se recupere el html.

No hay parpadeo, porque solo se cambia la parte invisible de la vista de desplazamiento.

Si su HTML contiene contenidos HTML pesados como iframe (es decir, facebook, twitter, instagram-embed), la solución real es mucho más difícil, primero envuelva su 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>"]; 

Luego, agregue handling 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 time (SetTimeout y setInterval) dentro de su código ObjectiveC / Swift: depende de usted.

PSS Información importante sobre UIWebView y las incorporaciones de Facebook: La publicación incorporada de Facebook no se muestra correctamente en UIWebView

Cuando se usa la vista web como una subvista en alguna parte de la vista de desplazamiento, puede establecer la restricción de altura a un valor constante y luego hacer que salga de ella y usarla como:

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

¡Esto es raro!

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

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

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

Verificado en iOS 9.3 Simulator / Device, ¡buena suerte!

EDITAR:

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

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

También me quedé atrapado en este problema, luego me di count de que si quiero calcular la altura dinámica de la webView, primero necesito decir el ancho del webView, así que agrego una línea antes de js y resulta que puedo get 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 funcionamiento correcto de todos los methods mencionados, agregue esto en el método view viewDidLoad controller:

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

De lo contrario, ninguno de los methods funcionaría como debería.

Xcode8 swift3.1:

  1. Primero establezca el alto de la vista web en 0 .
  2. En webViewDidFinishLoad Delegado:

let height = webView.scrollView.contentSize.height

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