web-dev-qa-db-ja.com

WKWebViewは、loadHTMLString(_、baseURL :)を使用して画像とCSSをロードできません

Appleの推奨

IOS 8以降で実行されるアプリでは、UIWebViewを使用する代わりにWKWebViewクラスを使用します。

したがって、私は古き良きUIWebViewを光沢のある新しいWKWebViewに置き換えました。しかし、簡単な演習(単純にクラスを交換し、デリゲートメソッドを置き換える)であると思ったのは、実際の混乱でした。

問題

を使用してHTML文字列を読み込むとき

_loadHTMLString(String, baseURL: URL?)
_

webビューは純粋なHTMLをロードしてレンダリングしますが、htmlString内で参照される画像やCSSファイルはロードしません。

これは実際のデバイスでのみ発生します!
Simultorでは、参照されるすべてのリソースが正しくロードされます。

SimulatorReal Device

View Controllerクラスで単純なhtmlStringを定義しました。

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

画像はルートライブラリフォルダに保存されるため、URLは次のとおりです。

_let imageURL = libraryURL.appendingPathComponent(imageName)
_

次に、htmlStringをWebビューにロードします。

_webView.loadHTMLString(htmlString, baseURL: libraryURL)
_

baseURLが正しく設定されていても、画像はロードされません。

ソリューションのアイデア

  1. おそらくWKWebViewには相対パスの解決に問題があるので、私の最初のアイデアは、代わりにHTML文字列内で絶対パスを使用することでした。
    →❌機能しません。

  2. another SO post への2つの答えは、
    loadFileURL(URL, allowingReadAccessTo: URL)
    loadHTMLString(...)の代わりにiOS 9以降で動作します。
    →✅動作します。

ただし、HTMLファイルは暗号化されており、復号化されたファイルはディスクに保存してはならないため、ソリューション2は使用できません。

質問

WKWebViewを使用して画像やスタイルなどのローカルリソースをロードする方法はありますか

_loadHTMLString(String, baseURL: URL?)
_

関数?または、まだiOS 9以降のバグですか?

(AppleはHTML文字列内からローカルWebコンテンツをロードできないWebビューの使用を提供および推奨しますか?!)

31
Mischa

実際のプロジェクトを見ていないと、百パーセントの確実なアドバイスを与えることは困難です。

しかしながら:

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

ここでのキーポイントはbaseUrlパラメーターだと思うので、正しく設定する必要があります。私の場合、私は最後のパスコンポーネントなしでhtmlのURLを使用しました-例えばフォルダーを含む。これは、デバイスとシミュレータの両方で正常に動作します-デバイスのスナップショットを確認してください。サンプルプロジェクトを https://github.com/soxjke/WKWebViewTest にアップロードしたので、ご覧ください(gitからコード署名情報を削除しました)

Device snapshot

要約すると、メソッドは機能し、機能は機能していますが、あなたは何か間違ったことをしています。ソリューションの問題を解決するために、いくつかの提案を追加します。1.シミュレータファイルシステムはcase-insensitiveであり、デバイスファイルシステムは大文字と小文字を区別します。そのため、ファイル名が小文字のhtmlにある場合、これはデバイス上では機能しません。 8fFsD.png != 8ffsd.png 2.リソースをコピーするとき、XCodeはフォルダー構造を無視することに注意してください。したがって、HTMLに<img src="./img/1.png">そしてあなたのXCOdeプロジェクトは次のようなフォルダ構造を持っています

test.htm

img/

    1.png 
    2.png

ビルド後はフラット化されるため、test.htmと1.pngおよび2.pngは同じレベルに存在します

test.htm
1.png 
2.png

これら2つの仮定を検証した後、このメソッドが機能することはほぼ確実です。

12
Petro Korienev

私は今日この問題を抱えていました、私は解決策と潜在的に原因を見つけました:

loadHTMLString(String, baseURL: URL?) 

私が知っている限り、この関数はレンダリングされたHTMLがローカルメディアにアクセスすることを許可しません。これはインジェクションのリスクがあるためです。これにより、レンダリングされたHTMLがローカルファイルシステムにアクセスして操作できるようになります。 HTML文字列を使用すると、どこからでも、または誰からでも送信できます。

loadFileURL(URL, allowingReadAccessTo: URL)

この関数を使用して、FileManagerのhtmlファイルと、「allowingReadAccessTo」を含むフォルダーをWKWebviewにポイントします。 htmlはFileManager内に保存されるため、レンダリングされたHTMLがローカルに保存されたメディアにアクセスできます。

何らかの理由でhtmlファイルがローカルに保存されていない場合(そうすることを前提としています)、. htmlファイルにhtmlスティングを書き込み、そのファイルのURLをポイントできます。ただし、これはAppleの保護を破壊するだけなので、自分の責任で実行してください(実行しないでください)。

これはちょうど私のために働いた解決策であり、最初に問題を抱えている理由を理解しています。

編集#1:タイプミス。

編集#2:「allowingReadAccessTo:」URLを指定するときに、HTML自体が親フォルダー(.css、.jsファイル)にアクセスする必要がある場合、別のニュアンスを見つけました。親フォルダーを指定する必要があります。 、必ずしもHTML自体の場所ではありません。これにより、必要に応じて子フォルダーへのアクセスも暗黙的に許可されます。私にとって、この問題は物理デバイスでのみ明らかでした。これは、シミュレーターでの実行中には効果がないようでした。おそらく、シミュレーターと物理デバイスでの権限の動作の間に別の矛盾があります。

3
Scott Browne

既に見つけた機能を備えたWKWebViewsで、ローカルイメージとCSSファイル(およびそのためのJavaScriptファイル)を使用できるはずです。私の推測では、問題はbaseURL変数にあるということです。

7.5.2017の更新:

another SO answer からのコードを完全に更新しました。以前はここで私の答えにリンクされていました。loadHTMLString()の作業プロジェクトがあります。および.loadFileURL()

3
tech4242

画像をbase64でエンコードできます...私はそれが機能することを知っています。ただし、ユースケースに適しているかどうかはわかりません。

ちょっとおかしいのですが、反対のことをしながらこの問題に遭遇しました-base64エンコードから画像ファイルに移行しました。

2
moliveira

個人的には、 XWebView を使用するように切り替える必要がありました。WKWebViewの初期状態の動作ではローカルファイルの読み込みが許可されていないためです。 XWebViewは、バックグラウンドでローカルWebサーバーをロードし、それを介してローカルトラフィックを誘導することにより、それをtrickします。 (XWebViewはWKWebViewのトップに基づいています)

少しやり過ぎのようですが、それが私がやらなければならなかったことです。

2
Chris Brandsma

私も同様の制限でこれを実験してきましたが、問題はbaseURLがアプリケーションバンドルを参照しない限りパスが解決されないことです。たとえば、アプリケーションのドキュメントに何かがあると機能しません。

編集:これについてレーダーを提出しました rdar:// 2913086

1
ianyh

同様の問題を再現することができました。 WKWebViewは、アプリサーバーとは別に、リモートに配置されている画像を特別に読み込みます。

SSLで保護されていないサーバー(httpsではなくhttp)では、以下のようにinfo.plistを設定できます。

App Transport Security Settings

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

問題は実際にサーバーにありました。サーバーアプリケーションは次のいずれかでした。

  • 画像ソースを"http://IP-or-domain/uploads/file.jpg"から"../../uploads/file.jpg"に変更しています

-OR-

  • 画像srcは"http://localhost/uploads/file.jpg"または"http://127.0.0.1/uploads/file.jpg"の代わりに"http://YOUR-SERVER-IP-ADDRESS/uploads/file.jpg"でした

これらの場合、実際のデバイスは画像を見つけることができません。仮想デバイスはサーバーおよび開発マシンと同じであるため、これはiOSシミュレーターでのみ機能します。 LOCALHOSTおよび127.0.0.1を読み取ることができます。

私のサーバーでは、リッチテキストエディター(TinyMCE)を使用しており、同じソースであることを検出した後、IPアドレスを自動的に削除します。

0
Keith Manilla

次を使用してbaseURLを作成してみてください。

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

の代わりに:

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

主な違いは、最初のメソッドがパスの前にfile://プレフィックスを追加することです。

0
kowal