WKWebView不会加载iOS 8下的本地文件

对于之前的iOS 8testing版,加载一个本地的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) 

但在testing版4中,我只是得到一个空白的白色屏幕( UIWebView仍然工作),看起来像什么都没有加载或执行。 我在日志中看到一个错误:

无法创build沙盒扩展/

任何帮助,以指导我正确的方向? 谢谢!

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

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

对于iOS8〜iOS10(Swift 3)

正如Dan Fabulish的回答所述,这是一个WKWebView的bug ,显然不是很快就能解决的 ,正如他说的那样,有一个解决方法:)

我正在回答,因为我想在这里展示工作。 在https://github.com/shazron/WKWebViewFIleUrlTest中显示的IMO代码充满了大多数人可能不感兴趣的无关细节。;

解决方法是20行代码,包括error handling和注释,不需要服务器:)

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

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

您可以通过loadHTMLString:加载内容,但是如果您的baseURL是一个文件:URL,那么它仍然不起作用。

iOS 9有一个新的API,将做你想做的, [WKWebView loadFileURL:allowingReadAccessToURL:]

有一个iOS 8的解决方法 ,由Objective-C中的shazron演示https://github.com/shazron/WKWebViewFIleUrlTest 将文件复制到/tmp/www并从那里加载 。

如果你在Swift中工作,你可以尝试nachos4d的示例 。 (它比Shazron的样本还要短得多,所以如果你在Shazron的代码中遇到问题,可以试一试。)

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

当您将Web文件夹移动到项目中时,select“创build文件夹引用”

在这里输入图像描述

然后使用这样的代码(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文件中使用像这样的文件path

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

不是这样的

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

一个移动到xcode项目的目录的例子。

在这里输入图像描述

临时解决方法: 按照GuidoMB的build议使用GCDWebServer。

我首先find捆绑的“www /”文件夹(包含“index.html”)的path:

 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...启动/重新启动它application:didFinishLaunching...applicationWillEnterForeground: application:didFinishLaunching...

除了Dan Fabulich提到的解决scheme之外, XWebView是另一种解决方法。 [WKWebView的loadFileURL:allowsReadAccessToURL:]是通过扩展实现的 。

我还不能评论,所以我作为一个单独的答案张贴这个。

这是nacho4d解决scheme的客观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; } 
 [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] ]; 

我正在使用下面。 有一些额外的东西我正在工作,但你可以看到我已经注释了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)") } } 

在同一行GCDWebServer,我使用SImpleHttpServer( http://www.andyjamesdavies.com/blog/javascript/simple-http-server-on-mac-os-x-in-seconds ),然后loadRequest与本地主机url。 使用这种方法,您不必添加任何库,但网站文件将不在该包中,因此无法交付。 因此,这将更适合于debugging的情况。

我已经设法在OS X上使用PHP的Web服务器。复制到临时/ www目录不适合我。 Python的SimpleHTTPServer抱怨想要读取MIMEtypes,可能是一个沙盒问题。

这是一个使用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() 

@ nacho4d解决scheme是不错的。 我想稍微改变它,但是我不知道如何在你的文章中改变它。 所以我把它放在这里,我希望你不介意。 谢谢。

万一你有一个万维网文件夹还有许多其他的文件,如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 } 

如果您试图在更大的HTMLstring中显示本地图像,例如: <img src="file://..."> ,它仍然不会出现在设备上,因此我加载了图像文件到NSData中,并能够通过用数据本身replacesrcstring来显示它。 帮助构buildHTMLstring以加载到WKWebView中的示例代码,其中的结果是将replacesrc =“”引号内的内容:

迅速:

 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]; 

谁必须在iOS8下解决这个问题:

如果您的页面不复杂,则可以select将该页面设置为单页面应用程序。

换句话说,要将所有资源embedded到html文件中。

要做到:1.将你的js / css文件的内容分别复制到html文件的/ tags中; 2.把你的图片文件转换成svg来取代相应的。 3.像以前一样加载页面,例如使用[webView loadHTMLString:baseURL:]

样式的svg图像有点不同,但它不应该阻止你这么多。

页面渲染性能似乎有所下降,但值得在iOS8 / 9/10下使用这种简单的解决方法。

尝试使用

 [webView loadHTMLString:htmlFileContent baseURL:baseURL]; 

似乎还在工作。 然而。