12 Fév 2016

Angular et UIWebView, un mélange parfois complexe

Bonjour à tous!

Il y a bien longtemps que je n’ai pas publié d’article sur ce blog, je vais donc commencer par vous souhaiter mes meilleurs voeux en ce début d’année 2016!

Pour aujourd’hui nous allons nous intéresser à un souci que vous serez peut-être amené à rencontrer dans vos projets sous forme d’une webview sous IOS (Iphone). En effet, le composant [UIWebView] présente un bug dans certaines conditions.

Dans le cadre du développement d’une application à destination de nos clients, nous avons ajouté une fonctionnalité permettant de transmettre des photos directement via l’application.

Lors de l’ajout d’une photo, une miniature de cette dernière s’affiche, permettant au client de visualiser son ajout.

Le souci que nous avons rencontré est que lors de l’ajout d’une photo, il était impossible de scroller sur l’écran – ce dernier était figé -, et par conséquent de valider l’envoi de cette étape via le bouton situé en bas de l’écran (qui n’était plus visible une fois la photo ajoutée).

Pour corser le tout, une fois une seconde photo ajoutée, il redevenait possible de scroller, et ainsi d’accéder au bouton permettant de valider l’étape.

Kepasa?

Et bien s’est très simple, il y a un “bug” sur le composant UIWebView d’IOS qui est à l’origine de ceci.

Je m’explique, images à l’appui.

app_0

Comme vous pouvez le constater sur l’image ci-dessus – très moche, j’en conviens, mais je suis développeur, pas graphiste -, en l’absence de photo, le bouton est parfaitement accessible.

Maintenant, lors de l’ajout d’une photo, un node est inseré dans le markup, avec en attribut src l’image encodée en base64.

app_1

C’est la que les ennuis surviennent.

Pourquoi?

Et bien tout simplement, parce qu’au moment ou vous inserez le node dans le dom, le composant UIWebView perçoit ce changement, recalcule la hauteur de la webview, et active – ou non – les scrollbars.

Ben, si il active les scrollbars, il est ou le souci?

Facile! Je la voyais venir, alors laissez moi vous expliquer. Au moment ou vous inserez une image, si cette dernière pointe vers une ressource distante, pas de soucis, l’iphone va attendre – à priori – le chargement de cette dernière pour recalculer la hauteur totale de votre contenu, le fera correctement, et vous permettra de scroller dans votre écran.

En revanche, et c’est la TOUTE LA DIFFÉRENCE, quand vous utilisez un src en base64, IOS considère que votre image est déjà chargé, effectue son calcul et.. Se plante.

Si l’iphone considère que l’image est déjà chargé, ce en quoi je ne peux lui donner tord (en l’absence de requête http, on peut considérer l’image comme chargée), et fait son calcul en se basant la dessus. Mais l’erreur est que bien que chargée, l’image n’a pas encore été affichée, sa hauteur et sa largeur ne sont pas encore fixées, et le calcul d’IOS est donc erroné.

Eh, mais tu es en train de me dire que y’a un bug IOS avec les images en base64 dans les webviews?

Oui, mais attention, uniquement si ces dernières sont les causes de l’apparition des scrollbars. Si les scrollbar sont déjà présentes, tout devrait à priori bien se passer. S’il n’y a pas besoin de scrollbar après le chargement de votre image, la encore, tout va bien.

C’est donc un cas d’usage très particulier que nous avons la.

Par la même occasion, ce comportement explique parfaitement qu’une fois une seconde image ajoutée, les scrollbars soient à nouveaux disponibles (ben oui, la première image est déjà affichée, sa hauteur bien calculée.. Vous commencez à comprendre? 😉 )

app_3
Bon ok, on a maintenant bien compris le souci et le fonctionnement du composant UIWebView. Mais comment on s’en sort?

Et bien s’est encore une fois assez simple. Nous allons nous servir de ce que nous avons compris du composant UIWebView.

Ce dernier recalcule la hauteur du contenu une fois qu’une modification du dom est survenue? Et bien nous allons lui donner une modification du dom qui le forcera à afficher les scrollbars, et nous seront prémunis!

Pour se faire, nous allons créer une directive en charge de faire le job. Cette directive ajoutera un bloc d’une taille déterminée, à un endroit déterminé qui forcera IOS a bosser correctement.

app_4

Comment?

Nous avons précédemment inséré un node défini comme suit:

<img alt="" />

Comme tout les éléments, il est possible de leur associer une ou plusieurs directives.

Un peu d’explications maintenant – encore dirons certains ^^ -!

Ici, nous créons une div d’une hauteur choisie – dans notre cas, 300px suffirons -, que nous plaçons de sorte que 50% de sa surface soit hors de la zone visible.

bottom:-" + (scrollAreaHeight / 2) + "px;position:absolute

Se faisant, nous somme sur qu’une fois inséré nous provoquerons un recalcule de la hauteur de la webview qui forcera l’iphone a afficher ses scrollbars.

L’insertion, justement, se fait simplement à l’aide de

Ben oui, mais on ne va pas ajouter ce bloc pour chaque image que l’on envoie?

Non. C’est vrai. D’ailleurs on ne va même pas le laisser, ce bloc.

Notez les lignes suivantes:

Ces dernières ont pour effet de provoquer la suppression du bloc après un délai de 1sec, ce qui est largement suffisant pour que l’iphone laisse la photo s’afficher complètement. En supprimant ce node, on provoque un nouveau changement du dom, et donc un nouveau calcul de la hauteur totale.

Mais si il n’y a plus ton bloc, alors la hauteur calculée n’est pas bonne, c’est ton souci!

Ce n’est plus exact jeune padawan. Relis bien. Ce délai de 1sec permet amplement à l’ichose de redessiner complètement l’image, sa hauteur est donc exacte, et le calcul de la hauteur totale est enfin correct!

C’est avec cette “petite” astuce que nous avons réussi à contourner un souci ma foi, franchement gênant..

Share
  • clakech

    Merci pour l’article,

    Est-ce qu’il y a un bug référencé quelque part auprès d’Apple ou ailleurs pour que le problème soit traité un jour ?

    Merci

    Cyril

  • pierre

    A partir d’iOS8, il faut utiliser WKWebView au lieu d’UIWebView. A t-on également le problème avec ?