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

WKWebView не загружает локальные файлы под iOS 8

Для предыдущих бета-версий iOS 8 загрузите локальное веб-приложение (в Bundle), и оно отлично работает для UIWebView и WKWebView, и я даже портировал веб-игру с помощью нового API WKWebView.

var url = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("car", ofType:"html"))

webView = WKWebView(frame:view.frame)
webView!.loadRequest(NSURLRequest(URL:url))

view.addSubview(webView)

Но в бета-версии 4 я просто получил пустой белый экран (UIWebView все еще работает), похоже, что ничего не загружено или не выполнено. Я видел ошибку в журнале:

Не удалось создать расширение песочницы для /

Любая помощь, чтобы вести меня в правильном направлении? Спасибо!

4b9b3361

Ответ 1

Они, наконец, решили ошибку! Теперь мы можем использовать -[WKWebView loadFileURL:allowingReadAccessToURL:]. По-видимому, исправление стоило несколько секунд в WWDC 2015 video 504 Знакомство с Safari View Controller

https://developer.apple.com/videos/wwdc/2015/?id=504

Для iOS8 ~ iOS10 (Swift 3)

Как Dan Fabulish answer говорится, что это ошибка WKWebView , которая, по-видимому, не решена в ближайшее время, и поскольку он сказал, что есть обход:)

Я отвечаю только потому, что хочу показать здесь работу. Код IMO, показанный в https://github.com/shazron/WKWebViewFIleUrlTest, наполнен несвязанными деталями, которые, вероятно, не интересуют большинство людей.

Работает 20 строк кода, обработка ошибок и комментарии включены, нет необходимости в сервере:)

func fileURLForBuggyWKWebView8(fileURL: URL) throws -> URL {
    // Some safety checks
    if !fileURL.isFileURL {
        throw NSError(
            domain: "BuggyWKWebViewDomain",
            code: 1001,
            userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("URL must be a file URL.", comment:"")])
    }
    try! fileURL.checkResourceIsReachable()

    // Create "/temp/www" directory
    let fm = FileManager.default
    let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("www")
    try! fm.createDirectory(at: tmpDirURL, withIntermediateDirectories: true, attributes: nil)

    // Now copy given file to the temp directory
    let dstURL = tmpDirURL.appendingPathComponent(fileURL.lastPathComponent)
    let _ = try? fm.removeItem(at: dstURL)
    try! fm.copyItem(at: fileURL, to: dstURL)

    // Files in "/temp/www" load flawlesly :)
    return dstURL
}

И может использоваться как:

override func viewDidLoad() {
    super.viewDidLoad()
    var fileURL = URL(fileURLWithPath: Bundle.main.path(forResource:"file", ofType: "pdf")!)

    if #available(iOS 9.0, *) {
        // iOS9 and above. One year later things are OK.
        webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
    } else {
        // iOS8. Things can (sometimes) be workaround-ed
        //   Brave people can do just this
        //   fileURL = try! pathForBuggyWKWebView8(fileURL: fileURL)
        //   webView.load(URLRequest(url: fileURL))
        do {
            fileURL = try fileURLForBuggyWKWebView8(fileURL: fileURL)
            webView.load(URLRequest(url: fileURL))
        } catch let error as NSError {
            print("Error: " + error.debugDescription)
        }
    }
}

Ответ 2

WKWebView не может загружать контент из файла: URL через его метод loadRequest:. http://www.openradar.me/18039024

Вы можете загружать контент через loadHTMLString:, но если ваш baseURL является файлом: URL-адрес, он все равно не будет работать.

iOS 9 имеет новый API, который будет делать то, что вы хотите, [WKWebView loadFileURL:allowingReadAccessToURL:].

Существует обходной путь для iOS 8, продемонстрированный shazron в Objective-C здесь https://github.com/shazron/WKWebViewFIleUrlTest до скопировать файлы в /tmp/www и загрузить их там.

Если вы работаете в Swift, вы можете попробовать образец nachos4d. (Это также намного меньше, чем образец шазрона, поэтому, если у вас возникли проблемы с шазронным кодом, попробуйте вместо этого.)

Ответ 3

Пример использования [WKWebView loadFileURL: allowReadAccessToURL:] в iOS 9.

Когда вы перемещаете веб-папку в проект, выберите "Создать ссылки на папку"

введите описание изображения здесь

Затем используйте код, что-то вроде этого (Swift 2):

if let filePath = NSBundle.mainBundle().resourcePath?.stringByAppendingString("/WebApp/index.html"){
  let url = NSURL(fileURLWithPath: filePath)
  if let webAppPath = NSBundle.mainBundle().resourcePath?.stringByAppendingString("/WebApp") {
    let webAppUrl = NSURL(fileURLWithPath: webAppPath, isDirectory: true)
    webView.loadFileURL(url, allowingReadAccessToURL: webAppUrl)
  }
}

В html файле используйте пути к файлу, подобные этому

<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">

не нравится

<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">

Пример каталога, который перемещается в проект xcode.

введите описание изображения здесь

Ответ 4

Временное обходное решение: я использую GCDWebServer, как было предложено GuidoMB.

Сначала я нашел путь к моей папке "www/" (которая содержит "index.html" ):

NSString *docRoot = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"www"].stringByDeletingLastPathComponent;

... затем запустите его так:

_webServer = [[GCDWebServer alloc] init];
[_webServer addGETHandlerForBasePath:@"/" directoryPath:docRoot indexFilename:@"index.html" cacheAge:3600 allowRangeRequests:YES];
[_webServer startWithPort:port bonjourName:nil];

Чтобы остановить его:

[_webServer stop];
_webServer = nil;

Производительность кажется прекрасной даже на iPad 2.


Я заметил крах после того, как приложение зашло в фоновый режим, поэтому я останавливаю его на applicationDidEnterBackground: и applicationWillTerminate:; Я запускаю/перезапускаю его на application:didFinishLaunching... и applicationWillEnterForeground:.

Ответ 6

Я еще не могу прокомментировать, поэтому я отправляю это как отдельный ответ.

Это objective-c версия решения nacho4d. Лучшее обходное решение, которое я видел до сих пор.

- (NSString *)pathForWKWebViewSandboxBugWithOriginalPath:(NSString *)filePath
{
    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"www"];
    NSError *error = nil;

    if (![manager createDirectoryAtPath:tempPath withIntermediateDirectories:YES attributes:nil error:&error]) {
        NSLog(@"Could not create www directory. Error: %@", error);

        return nil;
    }

    NSString *destPath = [tempPath stringByAppendingPathComponent:filePath.lastPathComponent];

    if (![manager fileExistsAtPath:destPath]) {
        if (![manager copyItemAtPath:filePath toPath:destPath error:&error]) {
            NSLog(@"Couldn't copy file to /tmp/www. Error: %@", error);

            return nil;
        }
    }

    return destPath;
}

Ответ 7

[configuration.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"];

Это решило проблему для меня iOS 8.0+ dev.apple.com

тоже кажется, что это тоже сработало отлично...

NSString* FILE_PATH = [[[NSBundle mainBundle] resourcePath]
                       stringByAppendingPathComponent:@"htmlapp/FILE"];
[self.webView
    loadFileURL: [NSURL fileURLWithPath:productURL]
    allowingReadAccessToURL: [NSURL fileURLWithPath:FILE_PATH]
];

Ответ 8

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

import UIKit
import WebKit

class ViewController: UIViewController, WKScriptMessageHandler {

    var theWebView: WKWebView?

    override func viewDidLoad() {
        super.viewDidLoad()

        var path = NSBundle.mainBundle().pathForResource("index", ofType: "html", inDirectory:"www" )
        var url = NSURL(fileURLWithPath:path)
        var request = NSURLRequest(URL:url)
        var theConfiguration = WKWebViewConfiguration()

        theConfiguration.userContentController.addScriptMessageHandler(self, name: "interOp")

        theWebView = WKWebView(frame:self.view.frame, configuration: theConfiguration)

        let text2 = String.stringWithContentsOfFile(path, encoding: NSUTF8StringEncoding, error: nil)

        theWebView!.loadHTMLString(text2, baseURL: nil)

        //theWebView!.loadRequest(request)

        self.view.addSubview(theWebView)


    }

    func appWillEnterForeground() {

    }

    func appDidEnterBackground() {

    }

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

    func userContentController(userContentController: WKUserContentController!, didReceiveScriptMessage message: WKScriptMessage!){
        println("got message: \(message.body)")

    }

}

Ответ 9

Для тех, кто должен обходить эту проблему в iOS8:

Если ваша страница не является сложной, вы можете сделать страницу как одностраничное приложение.

Другими словами, чтобы внедрить все ресурсы в html файл.

Сделать: 1. скопируйте содержимое js/css файла в /tags в html файле соответственно; 2. Преобразуйте файлы изображений в svg, чтобы заменить их соответствующим образом. 3. Загрузите страницу по-прежнему, используя [webView loadHTMLString: baseURL:], например

Это немного отличалось от стиля изображения svg, но оно не должно блокировать вас.

Казалось, что производительность рендеринга страницы немного уменьшилась, но было достойно иметь такое проворное обходное решение, работающее под iOS8/9/10.

Ответ 10

В той же строке GCDWebServer я использую SImpleHttpServer (http://www.andyjamesdavies.com/blog/javascript/simple-http-server-on-mac-os-x-in-seconds), а затем loadRequest с URL-адресом localhost. При таком подходе вам не нужно добавлять какую-либо библиотеку, но файлы веб-сайта не будут в комплекте, поэтому она не будет доступна. Из-за этого это было бы более подходящим для случаев отладки.

Ответ 11

Мне удалось использовать веб-сервер PHP на OS X. Копирование во временный каталог /www не работало для меня. Python SimpleHTTPServer жаловался на желание читать MIME-типы, возможно, проблему с песочницей.

Здесь сервер использует php -S:

let portNumber = 8080

let task = NSTask()
task.launchPath = "/usr/bin/php"
task.arguments = ["-S", "localhost:\(portNumber)", "-t", directoryURL.path!]
// Hide the output from the PHP server
task.standardOutput = NSPipe()
task.standardError = NSPipe()

task.launch()

Ответ 12

@nacho4d решение хорошее. Я хочу немного изменить его, но я не знаю, как изменить его в вашем посте. Поэтому я сказал, что надеюсь, вы не против. спасибо.

Если у вас есть папка www, есть много других файлов, таких как png, css, js и т.д. Затем вам нужно скопировать все файлы в папку tmp/www. например, у вас есть папка www, например: введите описание изображения здесь

затем в Swift 2.0:

override func viewDidLoad() {
    super.viewDidLoad()

    let path = NSBundle.mainBundle().resourcePath! + "/www";
    var fileURL = NSURL(fileURLWithPath: path)
    if #available(iOS 9.0, *) {
        let path = NSBundle.mainBundle().pathForResource("index", ofType: "html", inDirectory: "www")
        let url = NSURL(fileURLWithPath: path!)
        self.webView!.loadRequest(NSURLRequest(URL: url))
    } else {
        do {
            fileURL = try fileURLForBuggyWKWebView8(fileURL)
            let url = NSURL(fileURLWithPath: fileURL.path! + "/index.html")
            self.webView!.loadRequest( NSURLRequest(URL: url))
        } catch let error as NSError {
            print("Error: \(error.debugDescription)")
        }
    }
}

функция fileURLForBuggyWKWebView8 копируется из @nacho4d:

func fileURLForBuggyWKWebView8(fileURL: NSURL) throws -> NSURL {
    // Some safety checks
    var error:NSError? = nil;
    if (!fileURL.fileURL || !fileURL.checkResourceIsReachableAndReturnError(&error)) {
        throw error ?? NSError(
            domain: "BuggyWKWebViewDomain",
            code: 1001,
            userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("URL must be a file URL.", comment:"")])
    }

    // Create "/temp/www" directory
    let fm = NSFileManager.defaultManager()
    let tmpDirURL = NSURL.fileURLWithPath(NSTemporaryDirectory())
    try! fm.createDirectoryAtURL(tmpDirURL, withIntermediateDirectories: true, attributes: nil)

    // Now copy given file to the temp directory
    let dstURL = tmpDirURL.URLByAppendingPathComponent(fileURL.lastPathComponent!)
    let _ = try? fm.removeItemAtURL(dstURL)
    try! fm.copyItemAtURL(fileURL, toURL: dstURL)

    // Files in "/temp/www" load flawlesly :)
    return dstURL
}

Ответ 13

В случае, если вы пытаетесь отобразить локальное изображение в середине более крупной строки HTML, например: <img src="file://...">, оно по-прежнему не отображается на устройстве, поэтому я загрузил файл изображения в NSData и смог его отобразить заменяя строку src самими данными. Пример кода, который поможет построить HTML-строку для загрузки в WKWebView, где результат будет тем, что заменит то, что внутри кавычек src="":

Swift:

let pathURL = NSURL.fileURLWithPath(attachmentFilePath)
guard let path = pathURL.path else {
    return // throw error
}
guard let data = NSFileManager.defaultManager().contentsAtPath(path) else {
    return // throw error
}

let image = UIImage.init(data: data)
let base64String = data.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
result += "data:image/" + attachmentType + "base64," + base64String

var widthHeightString = "\""
if let image = image {
    widthHeightString += " width=\"\(image.size.width)\" height=\"\(image.size.height)\""
}

result += widthHeightString

Objective-C:

NSURL *pathURL = [NSURL fileURLWithPath:attachmentFilePath];
NSString *path = [pathURL path];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];

UIImage *image = [UIImage imageWithData:data];
NSString *base64String = [data base64EncodedStringWithOptions:0];
[result appendString:@"data:image/"];
[result appendString:attachmentType]; // jpg, gif etc.
[result appendString:@";base64,"];
[result appendString:base64String];

NSString *widthHeightString = @"\"";
if (image) {
    widthHeightString = [NSString stringWithFormat:@"\" width=\"%f\" height=\"%f\"", image.size.width, image.size.height];
}
[result appendString:widthHeightString];

Ответ 14

Попробуйте использовать

[webView loadHTMLString:htmlFileContent baseURL:baseURL];

Кажется, он все еще работает. Все же.