Подтвердить что ты не робот

WKWebView не загружает изображения и CSS с помощью loadHTMLString (_, baseURL:)

Рекомендация Apple:

В приложениях, запущенных в iOS 8 и более поздних версиях, используйте класс WKWebView вместо использования UIWebView.

Таким образом, я заменил старый добрый UIWebView блестящим новым WKWebView. Но то, что я считал простым упражнением (просто заменяя классы и заменяя методы делегата), оказалось настоящим беспорядком.

Проблема

Когда я загружаю HTML-строку, используя

loadHTMLString(String, baseURL: URL?)

веб-просмотр загружает и отображает чистый HTML, но он не загружает изображения или файлы CSS, на которые ссылаются внутри htmlString.

Это происходит только на реальном устройстве!
В Simultor все ресурсы, на которые ссылаются, загружаются правильно.

Симулятор Реальное устройство

Пример

Я определил простой 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 в веб-представление:

webView.loadHTMLString(htmlString, baseURL: libraryURL)

и не загружает изображение, даже если baseURL установлен правильно.

Идеи для решения

  • Возможно, WKWebView имеет проблему с решением относительных путей, поэтому моя первая идея заключалась в использовании абсолютных путей внутри строки HTML.
    → ❌ Не работает.

  • Два ответа на другое сообщение SO предполагали, что использование
    loadFileURL(URL, allowingReadAccessTo: URL)
    вместо loadHTMLString(...) работает в iOS 9+.
    → ✅ Это работает.

Однако я не могу использовать решение 2, потому что мои файлы HTML зашифрованы, а дешифрованные файлы не должны храниться на диске.

Вопрос

Есть ли способ загрузить локальные ресурсы, такие как изображения и стили, с помощью WKWebView '

loadHTMLString(String, baseURL: URL?)

функция? Или все еще ошибка в iOS 9 +?

(Я просто не могу поверить, что Apple предоставляет и рекомендует использовать веб-представление, которое не может загрузить какой-либо локальный веб-контент изнутри строки HTML!)

4b9b3361

Ответ 1

Не смотря на ваш реальный проект, сложно дать какие-то надежные советы.

Однако:

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)

Снимок устройства

Итак, чтобы recap-метод работает, функциональность работает, просто вы делаете с ней что-то не так. Чтобы помочь вам понять, что не так с вашими решениями, я добавлю несколько предложений:  1. Помните, что файловая система симулятора без учета регистра, файловая система устройства чувствительна к регистру. Поэтому, если у вас есть имена файлов в 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

Ну, вы должны иметь возможность использовать локальные изображения и файлы CSS (и файлы JavaScript, если на то пошло) с WKWebViews с уже найденной функцией. Я предполагаю, что проблема связана с вашей переменной baseURL.

Обновление 7.5.2017:

Я полностью обновил код из другого SO-ответа, который был связан с моим ответом здесь. У меня есть рабочий проект для loadHTMLString() и .loadFileURL()

Ответ 3

Вы можете закодировать изображения base64... Я знаю, что это работает. Не уверен, что это будет подходящим для вашего варианта использования.

Какой-то смешной, я просто столкнулся с этой проблемой, делая обратное - переход от base64 к файлам изображений.

Ответ 4

Лично мне пришлось переключиться на XWebView, так как поведение WKWebView из готового окна не позволяет загружать локальные файлы. XWebView обманывает его, загружая локальный веб-сервер в фоновом режиме и направляя локальный трафик через него. (XWebView основан на WKWebView)

Кажется, немного перебор, но это то, что мне пришлось сделать.

Ответ 5

Я тоже экспериментировал с подобными ограничениями, и проблема заключается в том, что пути не разрешаются, если baseURL не ссылается на пакет приложений. Это не работает, если вы, например, имеете что-то в документах приложения.

Изменить: я подал радар для этого rdar://29130863

Ответ 6

У меня была эта проблема сегодня, я нашел решение и, возможно, причину:

loadHTMLString(String, baseURL: URL?) 

Эта функция не позволяет визуализированному HTML получить доступ к локальным медиа, насколько я знаю, это потому, что это может привести к риску внедрения, что может позволить визуализированному HTML получить доступ к вашей локальной файловой системе и манипулировать ею. С HTML-строкой, которая может прийти откуда угодно или кому угодно.

loadFileURL(URL, allowingReadAccessTo: URL)

С помощью этой функции вы указываете WKWebview на html файл в вашем FileManager и на содержащую его папку с параметром "allowReadAccessTo". Поскольку HTML файл хранится в FileManager, он позволяет отображаемому HTML файлу получать доступ к локально сохраненным носителям.

Если по какой-то причине у вас нет html файла, который хранится локально (я полагаю, у вас есть), вы можете записать html-строку в html файл, а затем указать URL-адрес этого файла. Тем не менее, это просто подрывает защиту Apple, так что делайте это на свой страх и риск (не делайте этого).

Это просто решение, которое сработало для меня и моего понимания, почему у нас возникла проблема с самого начала.

Правка № 1: Опечатка.

Редактирование # 2: с тех пор я обнаружил еще один нюанс. При указании URL-адреса 'allowReadAccessTo:', если самому HTML требуется доступ к вещам в родительских папках (т.е. Файлах .css,.js), необходимо указать родительскую папку., не обязательно местоположение самого HTML, это затем неявно разрешит доступ к дочерним папкам также, как требуется.

Ответ 7

Я смог воспроизвести подобную проблему. WKWebView загружает мои изображения специально, если они расположены удаленно, кроме моего сервера приложений.

Для серверов, которые не защищены SSL (http вместо https), вы можете установить info.plist следующим образом:

App Transport Security Settings

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

Проблема была на самом деле на сервере. Серверное приложение было либо:

  • Изменение изображения src от "http://IP-or-domain/uploads/file.jpg" до "../../uploads/file.jpg"

- ИЛИ -

  • Изображение src было "http://localhost/uploads/file.jpg" или "http://127.0.0.1/uploads/file.jpg" вместо "http://YOUR-SERVER-IP-ADDRESS/uploads/file.jpg"

В этих случаях фактическое устройство не сможет найти изображение. Это работает только с iOS Simulator, потому что виртуальное устройство совпадает с сервером и машиной разработки. Он может читать LOCALHOST и 127.0.0.1.

На моем сервере я использовал Rich Text Editor (TinyMCE), и он автоматически удаляет IP-адрес после обнаружения того же источника.

Ответ 8

Попробуйте создать baseURL используя:

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

вместо:

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

Основное отличие состоит в том, что первый метод добавляет префикс file:// перед путем.