web-dev-qa-db-fra.com

WKWebView ne parvient pas à charger les images et CSS à l'aide de loadHTMLString (_, baseURL :)

recommandation d'Apple :

Dans les applications qui s'exécutent dans iOS 8 et versions ultérieures, utilisez la classe WKWebView au lieu d'utiliser UIWebView.

Ainsi, j'ai remplacé mon bon vieux UIWebView par un nouveau WKWebView brillant. Mais ce que je pensais être un exercice facile (simplement échanger les classes et remplacer les méthodes déléguées) s'est avéré être un vrai gâchis.

Le problème

Lorsque je charge une chaîne HTML à l'aide

loadHTMLString(String, baseURL: URL?)

la vue Web charge et rend le HTML pur mais elle ne charge aucune image ou fichier CSS référencé à l'intérieur de htmlString.

Cela se produit uniquement sur un appareil réel !
Dans Simultor, toutes les ressources référencées sont chargées correctement.

SimulatorReal Device

Exemple

J'ai défini un simple htmlString dans ma classe de contrôleur de vue:

let imageName = "image.png"

let libraryURL: URL // The default Library URL

var htmlString: String {
    return "<html> ... <img src=\"\(imageName)\" /> ... </html>"
    // "..." represents more valid HTML code incl. header and body tags
}

L'image est stockée dans le dossier racine de la bibliothèque, son URL est donc:

let imageURL = libraryURL.appendingPathComponent(imageName)

Maintenant, je charge le htmlString dans la vue Web:

webView.loadHTMLString(htmlString, baseURL: libraryURL)

et il ne charge pas l'image même si le baseURL est correctement défini.

Idées pour une solution

  1. Peut-être que WKWebView a un problème avec la résolution des chemins relatifs, donc ma première idée a été d'utiliser des chemins absolus à la place dans la chaîne HTML.
    → ❌ Ne fonctionne pas.

  2. Deux réponses à n autre SO post a suggéré d'utiliser
    loadFileURL(URL, allowingReadAccessTo: URL)
    au lieu de loadHTMLString(...) fonctionne dans iOS 9+.
    → ✅ Cela fonctionne.

Cependant, je ne peux pas utiliser la solution 2 car mes fichiers HTML sont cryptés et les fichiers décryptés ne doivent pas être stockés sur le disque.

Question

Existe-t-il un moyen de charger des ressources locales comme des images et des styles en utilisant les WKWebView

loadHTMLString(String, baseURL: URL?)

une fonction? Ou est-ce toujours un bug dans iOS 9+?

(Je n'arrive pas à croire que Apple fournit et recommande d'utiliser une vue Web qui ne peut pas charger de contenu Web local depuis une chaîne HTML?!)

31
Mischa

Sans jeter un oeil à votre projet réel, il est difficile de donner quelques centaines de conseils sûrs.

Toutefois:

class ViewController: UIViewController {

    var webView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()
        webView.translatesAutoresizingMaskIntoConstraints = false
        let views = [
            "webView" : webView
        ]
        view.addSubview(webView)
        var constraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|[webView]|", options: [.AlignAllLeading, .AlignAllTrailing], metrics: nil, views: views)
        constraints.appendContentsOf(NSLayoutConstraint.constraintsWithVisualFormat("V:|[webView]|", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: views))
        NSLayoutConstraint.activateConstraints(constraints)

        let path = NSBundle.mainBundle().pathForResource("ios - WKWebView fails to load images and CSS using loadHTMLString(_, baseURL_) - Stack Overflow", ofType: "htm")
        let url = NSURL(fileURLWithPath: path!)
        webView.loadHTMLString(try! String(contentsOfURL: url), baseURL: url.URLByDeletingLastPathComponent)

        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

Je pense que le point clé ici est le paramètre baseUrl, vous devez le configurer correctement. Dans mon cas, j'ai utilisé l'URL de HTML sans le dernier composant de chemin - par exemple dossier contenant. Cela fonctionne très bien sur l'appareil et le simulateur - vérifiez l'instantané de l'appareil. J'ai téléchargé un exemple de projet sur https://github.com/soxjke/WKWebViewTest pour que vous puissiez y jeter un œil (j'ai supprimé les informations de signature de code de git)

Device snapshot

Donc, pour récapituler - la méthode fonctionne, la fonctionnalité fonctionne, il suffit de faire quelque chose de mal avec elle. Pour vous aider à comprendre ce qui ne va pas avec vos solutions, j'ajouterai quelques suggestions: 1. N'oubliez pas que le système de fichiers du simulateur est insensible à la casse , le système de fichiers du périphérique est sensible à la casse . Donc, si vous avez vos noms de fichiers en html en minuscules - cela ne fonctionnera pas sur l'appareil. 8fFsD.png != 8ffsd.png 2. N'oubliez pas que lors de la copie de ressources, XCode ignore votre structure de dossiers. Donc, si votre html a <img src="./img/1.png"> et votre projet XCOde a une structure de dossiers comme

test.htm

img/

    1.png 
    2.png

Après la construction, il sera aplati, donc test.htm et 1.png et 2.png résideront au même niveau

test.htm
1.png 
2.png

Je suis presque sûr, après avoir vérifié ces deux hypothèses, que cette méthode fonctionnera.

12
Petro Korienev

J'ai eu ce problème aujourd'hui, j'ai trouvé la solution et potentiellement la cause:

loadHTMLString(String, baseURL: URL?) 

Cette fonction ne permet pas au HTML rendu d'accéder aux médias locaux, à ma connaissance, c'est parce que ce serait un risque d'injection, cela pourrait permettre au HTML rendu d'accéder et de manipuler votre système de fichiers local. Avec une chaîne html, cela pourrait provenir de n'importe où ou de n'importe qui.

loadFileURL(URL, allowingReadAccessTo: URL)

Avec cette fonction, vous pointez le WKWebview vers le fichier html dans votre FileManager, et vers le dossier contenant avec 'allowReadAccessTo'. Parce que le html est stocké dans le FileManager, il permettra au HTML rendu d'accéder aux médias stockés localement.

Si vous n'avez pas le fichier html stocké localement pour une raison quelconque (je suppose que vous le faites), vous pouvez écrire la piqûre html dans un fichier .html, puis pointer vers l'URL de ce fichier. Cependant, cela ne fait que renverser la protection d'Apple, alors faites-le à vos risques et périls (ne le faites pas).

C'est juste la solution qui a fonctionné pour moi et ma compréhension des raisons pour lesquelles nous avons le problème pour commencer.

Edit # 1: Typo.

Edit # 2: J'ai depuis trouvé une autre nuance, lorsque vous indiquez l'URL "permitReadAccessTo:", si le HTML lui-même doit accéder à des choses dans les dossiers parents (par exemple: fichiers .css, .js), vous devez spécifier le dossier parent , pas nécessairement l'emplacement du code HTML lui-même, cela autorisera alors implicitement l'accès aux dossiers enfants comme requis également. Pour moi, ce problème n'était apparent que sur un périphérique physique, cela ne semblait pas avoir d'effet lors de l'exécution dans le simulateur, probablement une autre différence entre le fonctionnement des autorisations sur le simulateur et un périphérique physique.

3
Scott Browne

Eh bien, vous devriez pouvoir utiliser des images locales et des fichiers CSS (et des fichiers JavaScript d'ailleurs) avec WKWebViews avec la fonction que vous avez déjà trouvée. Je suppose que le problème vient de votre variable baseURL.

Mise à jour 7.5.2017:

J'ai complètement mis à jour le code de n autre SO answer du mien qui était lié à ma réponse ici. J'ai un projet de travail pour loadHTMLString() et .loadFileURL()

3
tech4242

Vous pouvez coder en base64 les images ... Je sais que cela fonctionne. Je ne sais pas si cela conviendra à votre cas d'utilisation.

Un peu drôle, je suis juste tombé sur ce problème en faisant le contraire - passer de codés en base64 à des fichiers image.

2
moliveira

Personnellement, j'ai dû passer à l'utilisation de XWebView car le comportement prêt à l'emploi de WKWebView ne permet pas le chargement de fichiers locaux. XWebView le trompe en chargeant un serveur Web local en arrière-plan et en dirigeant le trafic local à travers lui. (XWebView est basé sur WKWebView)

Semble un peu exagéré, mais c'est ce que j'ai fini par faire.

2
Chris Brandsma

J'ai également expérimenté cela, avec des restrictions similaires, et le problème semble être que les chemins ne sont pas résolus à moins que baseURL fasse référence au bundle d'application. Cela ne fonctionne pas si, par exemple, vous avez quelque chose dans les documents de l'application.

Edit: j'ai déposé un radar pour cela rdar: // 2913086

1
ianyh

J'ai pu reproduire un problème similaire. WKWebView charge mes images spécialement si elles sont situées à distance, en dehors de mon serveur d'application.

Pour les serveurs qui ne sont pas sécurisés par SSL (http au lieu de https), vous pouvez définir votre info.plist comme ci-dessous:

App Transport Security Settings

 - Allow Arbitrary Loads in Web Content (Set to YES)
 - Allow Arbitrary Loads (Set to YES)

Le problème était en fait sur le serveur. L'application serveur était soit:

  • Changer le src de l'image de "http://IP-or-domain/uploads/file.jpg" à "../../uploads/file.jpg"

- OR -

  • L'image src était "http://localhost/uploads/file.jpg"o"http://127.0.0.1/uploads/file.jpg"au lieu de"http://YOUR-SERVER-IP-ADDRESS/uploads/file.jpg"

Dans ces cas, l'appareil réel ne pourra pas localiser l'image. Cela ne fonctionne qu'avec iOS Simulator car le périphérique virtuel est le même que le serveur et la machine de développement. Il peut lire LOCALHOST et 127.0.0.1.

Dans mon serveur, j'utilisais un éditeur de texte enrichi (TinyMCE) et il supprime automatiquement l'adresse IP après avoir détecté qu'il s'agit de la même source.

0
Keith Manilla

Essayez de créer baseURL en utilisant:

let baseURL = URL(fileURLWithPath: "#path#")

au lieu de:

let baseURL = URL(string: "#path#")

La principale différence est que la première méthode ajoute file:// préfixe avant le chemin.

0
kowal