首页 文章

WKWebView无法在iOS 8下加载本地文件

提问于
浏览
109

对于以前的iOS 8测试版,加载一个本地Web应用程序(在Bundle中)它适用于 UIWebViewWKWebView ,我甚至使用新的 WKWebView API移植了一个网页游戏 .

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 仍在工作),看起来没有任何加载或执行 . 我在日志中看到一个错误:

无法为 / 创建沙箱扩展

有没有帮助引导我走向正确的方向?谢谢!

14 回答

  • 91

    他们终于解决了这个错误!现在我们可以使用 -[WKWebView loadFileURL:allowingReadAccessToURL:] . 显然这个修复在WWDC 2015 video 504 Introducing Safari View Controller值得花几秒钟

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

    适用于iOS8~iOS10(Swift 3)

    正如Dan Fabulish's answer所述,这是WKWebView的一个错误,显然很快就没有解决,因为他说有一个解决办法:)

    我的回答只是因为我想在这里展示解决方法 . https://github.com/shazron/WKWebViewFIleUrlTest中显示的IMO代码充满了大多数人可能不感兴趣的无关细节 .

    解决方法是包含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)
            }
        }
    }
    
  • 79

    WKWebView无法通过 loadRequest: 方法从文件:URL加载内容 . http://www.openradar.me/18039024

    您可以通过 loadHTMLString: 加载内容,但如果您的baseURL是文件:URL,那么它仍然无效 .

    iOS 9 has a new API that will do what you want, [WKWebView loadFileURL:allowingReadAccessToURL:] .

    There is a workaround for iOS 8 ,由Shazron在Objective-C中演示https://github.com/shazron/WKWebViewFIleUrlTestcopy files into /tmp/www and load them from there .

    如果你在Swift工作,你可以改为try nachos4d's sample . (它是's also much shorter than shazron'的样本,所以如果您使用're having trouble with shazron'代码,请尝试代替 . )

  • 3

    如何在 iOS 9 上使用[WKWebView loadFileURL:allowsReadAccessToURL:]的示例 .

    将Web文件夹移动到项目时,选择“创建文件夹引用”

    enter image description here

    然后使用类似这样的代码(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项目的目录示例 .

    enter image description here

  • 5

    临时解决方法:我正在使用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: 上启动/重启它 .

  • 4

    除了Dan Fabulich提到的解决方案之外,XWebView是另一种解决方法 . [WKWebView loadFileURL:allowsReadAccessToURL:]是implemented through extension .

  • 0
    [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]
    ];
    
  • -1

    我还不能评论,所以我将其作为一个单独的答案发布 .

    这是nacho4d's solution的Objective-c版本 . 到目前为止我见过的最好的解决方法 .

    - (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;
    }
    
  • 0

    我正在使用下面的内容 . 有一些额外的东西,我正在努力,但你可以看到我已经注释掉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)")
    
        }
    
    }
    
  • 0

    对于谁必须在iOS8下解决此问题:

    如果页面不复杂,您可以选择将页面设置为单页面应用程序 .

    换句话说,将所有资源嵌入到html文件中 .

    要做的事情:1 . 将js / css文件的内容分别复制到html文件中的/ tags; 2.将您的图像文件转换为svg以相应地替换 . 3.像以前一样使用[webView loadHTMLString:baseURL:]加载页面

    设计一个svg图像有点不同,但它不应该阻止你这么多 .

    看起来页面渲染性能有所下降,但是在iOS8 / 9/10下有这么简单的解决方法是值得的 .

  • 7

    在GCDWebServer的同一行中,我使用SImpleHttpServer(http://www.andyjamesdavies.com/blog/javascript/simple-http-server-on-mac-os-x-in-seconds),然后使用localhost url加载LoadRequest . 使用这种方法,您不必添加任何库,但网站文件将不在包中,因此无法交付 . 因此,这对于Debug案例更合适 .

  • 0

    我已经设法在OS X上使用PHP的Web服务器 . 复制到临时/ 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()
    
  • 1

    @ nacho4d解决方案很好 . 我想稍微改变它,但我不知道如何改变它帖子 . 所以我把它放在这里我希望你不介意 . 谢谢 .

    如果你有一个www文件夹,还有许多其他文件,如png,css,js等 . 然后你必须将所有文件复制到tmp / www文件夹 . 例如,你有一个像这样的www文件夹:
    enter image description here

    然后在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)")
            }
        }
    }
    

    从@ nacho4d复制函数fileURLForBuggyWKWebView8:

    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
    }
    
  • 4

    如果您尝试在较大的HTML字符串中显示本 Map 像,例如: <img src="file://..."> ,它仍然不会出现在设备上,因此我将图像文件加载到NSData中,并且能够通过替换src字符串来显示它与数据本身 . 示例代码,以帮助构建要加载到WKWebView的HTML字符串,其结果是将替换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];
    
  • 1

    尝试使用

    [webView loadHTMLString:htmlFileContent baseURL:baseURL];
    

    似乎它仍然有效 . 然而 .

相关问题