ios - example - ¿Cómo determinar el tamaño del contenido de un UIWebView?
wkwebview ios (15)
¡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í.
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 fotograma actual de la vista web.
AFAIK puedes usar [webView sizeThatFits:CGSizeZero]
para descubrir su tamaño de contenido.
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;
}
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://.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://.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.
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
.
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 fotograma 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.
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.
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:@"<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://.com/a/8031442/1046909
[htmlContent appendFormat:@"..."]; // Your HTML-content
[htmlContent appendFormat:@"</div>"]; // </div id="content">
[htmlContent appendFormat:@"</body>"];
[htmlContent appendFormat:@"</html>"];
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
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.
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;
//......
}
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 sumar el webView
mi frame.size.height
calculado con el truco descrito anteriormente ...
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));
}
Xcode8 swift3.1:
- Establezca la altura de webView en 0 primero.
- 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.
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)")
}