web-dev-qa-db-fra.com

Comment déterminer la taille du contenu d'un UIWebView?

J'ai un UIWebView avec un contenu différent (une seule page). J'aimerais connaître le CGSize du contenu pour redimensionner correctement les vues de mes parents. L'évident -sizeThatFits: _ ne renvoie malheureusement que la taille d'image actuelle de la WebView.

116
Ortwin Gentz

Il s’est avéré que ma première hypothèse utilisant -sizeThatFits: n'était pas complètement faux. Cela semble fonctionner, mais seulement si le cadre de la webView est défini à une taille minimale avant l'envoi -sizeThatFits:. Après cela, nous pouvons corriger la taille de trame incorrecte par la taille de raccord. Cela semble terrible, mais ce n'est pas si grave. Comme nous modifions les images l'une après l'autre, la vue n'est pas mise à jour et ne scintille pas.

Bien sûr, nous devons attendre que le contenu ait été chargé pour placer le code dans le -webViewDidFinishLoad: méthode déléguée.

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
}

Je devrais signaler qu'il y a ne autre approche (merci @GregInYEG) en utilisant JavaScript. Vous ne savez pas quelle solution fonctionne le mieux.

De deux solutions de hacky, je préfère celle-ci.

249
Ortwin Gentz

J'ai une autre solution qui fonctionne très bien.

D'une part, l'approche et la solution d'Ortwin fonctionnent uniquement avec iOS 6.0 et versions ultérieures, mais ne fonctionnent pas correctement sous iOS 5.0, 5.1 et 5.1.1, et d'autre part, il y a quelque chose que je n'aime pas et ne comprend pas. avec l'approche d'Ortwin, c'est l'utilisation de la méthode [webView sizeThatFits:CGSizeZero] avec le paramètre CGSizeZero: Si vous lisez Apple Documentation officielle sur cette méthode et son paramètre, cela dit clairement:

L’implémentation par défaut de cette méthode renvoie la partie taille du rectangle aux limites de la vue. Les sous-classes peuvent remplacer cette méthode pour renvoyer une valeur personnalisée en fonction de la présentation souhaitée de toutes les sous-vues. Par exemple, un objet UISwitch renvoie une valeur de taille fixe qui représente la taille standard d'une vue de commutateur et un objet UIImageView renvoie la taille de l'image qu'il affiche actuellement.

Ce que je veux dire, c'est que c'est comme s'il avait trouvé sa solution sans aucune logique, car en lisant la documentation, le paramètre passé à [webView sizeThatFits: ...] Devrait au moins avoir le width souhaité. Avec sa solution, la largeur souhaitée est définie sur le cadre de webView avant d'appeler sizeThatFits avec un paramètre CGSizeZero. Je maintiens donc que cette solution fonctionne sur iOS 6 par "hasard".

J'ai imaginé une approche plus rationnelle, qui présente l'avantage de fonctionner pour iOS 5.0 et les versions ultérieures ... Et aussi dans des situations complexes où plusieurs vues Web (avec sa propriété webView.scrollView.scrollEnabled = NO Sont intégrées dans un scrollView.

Voici mon code pour forcer la présentation du webView sur le width souhaité et obtenir le réglage correspondant height sur le webView lui-même:

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

Notez que dans mon exemple, le webView était incorporé dans une scrollView personnalisée ayant un autre webViews ... Tous ces webViews avaient leur webView.scrollView.scrollEnabled = NO, et le dernier morceau de code que j’ai dû ajouter était le calcul du height du contentSize de mon client scrollView incorporant ces webViews, mais c'était aussi facile que de résumer mes frame.size.height de webView calculés avec le truc décrit ci-dessus ...

91
Phenomena

Ressusciter cette question parce que je trouvais que la réponse d'Ortwin ne fonctionnait que la plupart du temps ...

La méthode webViewDidFinishLoad peut être appelée plusieurs fois, et la première valeur renvoyée par sizeThatFits ne représente qu'une partie de ce que devrait être la taille finale. Alors pour quelque raison que ce soit, le prochain appel à sizeThatFits lorsque webViewDidFinishLoad se déclenchera à nouveau renverra de manière incorrecte la même valeur qu’avant! Cela se produira de manière aléatoire pour le même contenu, comme s'il s'agissait d'un problème de concurrence. Peut-être que ce comportement a changé avec le temps, parce que je construis pour iOS 5 et que j’ai également constaté que sizeToFit fonctionnait sensiblement de la même manière (bien que cela n’ait pas été le cas auparavant?)

J'ai opté pour cette solution 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
    }
}

Mise à jour: j'ai constaté, comme indiqué dans les commentaires, que le contenu du fichier ne semble pas avoir été corrigé. Pas sûr que ce soit vrai pour tous les contenus et toutes les versions du système d'exploitation, essayez-le.

37
Kieran Harper

Dans Xcode 8 et iOS 10 pour déterminer la hauteur d'une vue Web. Vous pouvez obtenir cette hauteur avec

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

OU pour Swift

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

Une solution simple consisterait simplement à utiliser webView.scrollView.contentSize mais je ne sais pas si cela fonctionne avec JavaScript. Si aucun JavaScript n'est utilisé, cela fonctionne à coup sûr:

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

C'est bizarre!

J'ai testé les solutions à la fois sizeThatFits: et [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] sont PAS travaille pour moi.

Cependant, j'ai trouvé un moyen facile et intéressant d'obtenir la bonne hauteur de contenu de page Web. Actuellement, je l’utilise dans ma méthode déléguée scrollViewDidScroll:.

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

Vérifié dans iOS 9.3 Simulator/Device, bonne chance!

EDIT:

Arrière-plan: le contenu html est calculé par ma variable chaîne et mon modèle de contenu HTTP, chargé par la méthode loadHTMLString:baseURL:, pas de scripts JS enregistrés.

3
Itachi

Pour iOS10, j'obtenais 0 (zéro) valeur de document.height alors document.body.scrollHeight est la solution pour obtenir une hauteur de document dans Webview. Le problème peut également être résolu pour width.

3
iAnkit

Autant que je sache, vous pouvez utiliser [webView sizeThatFits:CGSizeZero] pour déterminer sa taille.

3
Rengers

Lorsque vous utilisez webview en tant que sous-vue quelque part dans scrollview, vous pouvez définir la contrainte de hauteur sur une valeur constante, puis en créer une sortie et l'utiliser de la manière suivante:

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

Aucune des suggestions ici ne m'a aidé avec ma situation, mais j'ai lu quelque chose qui m'a donné une réponse. J'ai un ViewController avec un ensemble fixe de contrôles d'interface utilisateur suivi d'un UIWebView. Je voulais que toute la page défile comme si les contrôles de l'interface utilisateur étaient connectés au contenu HTML. Je désactive donc le défilement sur UIWebView et dois ensuite définir correctement la taille du contenu d'une vue de défilement parent.

Le conseil important s’est avéré que UIWebView ne signale pas correctement sa taille tant qu’il n’a pas été rendu à l’écran. Ainsi, lorsque je charge le contenu, je règle la taille du contenu sur la hauteur d'écran disponible. Ensuite, dans viewDidAppear, je mets à jour la taille du contenu de scrollview. Cela a fonctionné pour moi parce que j'appelle loadHTMLString sur du contenu local. Si vous utilisez loadRequest, vous devrez peut-être également mettre à jour le contentSize dans webViewDidFinishLoad, en fonction de la vitesse d'extraction du code HTML.

Il n'y a pas de scintillement, car seule la partie invisible de la vue de défilement est modifiée.

2
Eli Burke

J'utilise un UIWebView qui n'est pas une sous-vue (et ne fait donc pas partie de la hiérarchie des fenêtres) pour déterminer la taille du contenu HTML pour UITableViewCells. J'ai trouvé que le UIWebView déconnecté ne rapporte pas correctement sa taille avec -[UIWebView sizeThatFits:]. De plus, comme indiqué dans https://stackoverflow.com/a/3937599/9636 , vous devez définir le UIWebView 's frameheight sur 1 afin d'obtenir la bonne hauteur du tout.

Si la hauteur de UIWebView est trop grande (c'est-à-dire que vous l'avez définie sur 1000, mais que la taille du contenu HTML n'est que de 500):

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

Tous renvoient un height de 1000.

Pour résoudre mon problème dans ce cas, j'ai utilisé https://stackoverflow.com/a/11770883/9636 , ce que j'ai consciencieusement voté. Cependant, je n’utilise cette solution que lorsque mon UIWebView.frame.width est le même que le -[UIWebView sizeThatFits:]width.

2
Heath Borders

Si votre code HTML contient lourd Des contenus HTML tels que ceux d'iframe (c'est-à-dire facebook, Twitter, instagram-embeds), la vraie solution est beaucoup plus difficile, commencez par envelopper votre code 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>"];

Ajoutez ensuite le traitement x-update-webview-height - à votre 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;
    }
    ...

Et enfin, ajoutez le code suivant dans votre 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. Vous pouvez implémenter un délai (SetTimeout et setInterval) dans votre code ObjectiveC/Swift - à vous de choisir.

P.S.S. Informations importantes sur UIWebView et Facebook Embeds: La publication Facebook intégrée ne s'affiche pas correctement dans UIWebView

2
MingalevME

Je suis aussi bloqué sur ce problème, puis je me suis rendu compte que si je veux calculer la hauteur dynamique de la webView, je dois d'abord dire la largeur de la webView, donc j'ajoute une ligne avant js et il s'avère que je peux obtenir hauteur réelle très précise.

Le code est simple comme ceci:

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

Xcode8 Swift3.1:

  1. Définissez la hauteur de la vue Web sur d’abord.
  2. Dans webViewDidFinishLoad Délégué:

let height = webView.scrollView.contentSize.height

Sans l'étape 1, si webview.height> content contentHeight, l'étape 2 renverra webview.height mais pas la taille du contenu.height.

1
hstdt

Toujours dans iOS 7 pour le bon fonctionnement de toutes les méthodes mentionnées, ajoutez ceci dans votre méthode de contrôleur de vue viewDidLoad:

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

Sinon, aucune des méthodes ne fonctionnerait comme il se doit.

0