WKWebView 没有在 iOS8下加载本地文件

对于以前的 iOS8测试版,加载一个本地网络应用程序(在 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)

但是在 beta 4中,我只得到了一个空白屏幕(UIWebView仍然可以工作) ,看起来没有任何东西被加载或执行。我在日志中看到一个错误:

无法为 /创建沙盒扩展

有什么可以帮助我找到正确的方向吗? 谢谢!

85610 次浏览

尝试使用

[webView loadHTMLString:htmlFileContent baseURL:baseURL];

看起来它还在工作,暂时还没有。

我用下面的。还有一些我正在处理的额外内容,但是您可以看到我在哪里注释掉了 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)")


}


}

WKWebView 无法通过其 loadRequest:方法从 file: URL 加载内容

您可以通过 loadHTMLString:加载内容,但是如果 baseURL 是一个 file: URL,那么它仍然无法工作。

IOS9有一个新的 API,可以做你想做的事情, [WKWebView loadFileURL:allowingReadAccessToURL:]

有一个 iOS 8 的解决方案,Shazron 在 Objective-C 中的 https://github.com/shazron/WKWebViewFIleUrlTest将文件复制到 /tmp/www并从那里加载它们中证明了这一点。

如果你在斯威夫特工作,你可以选择 try nachos4d's sample。(它也比 shazron 的示例短得多,所以如果您在使用 shazron 的代码时遇到麻烦,可以尝试使用它。)

Temporary workaround: I'm using GCDWebServer, as suggested by 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];

To stop it:

[_webServer stop];
_webServer = nil;

即使在 iPad2上,性能也表现良好。


我确实注意到应用程序进入后台后出现了崩溃,所以我在 applicationDidEnterBackground:applicationWillTerminate:上停止了它; 我在 application:didFinishLaunching...applicationWillEnterForeground:上启动/重启了它。

在 GCDWebServer 的同一行中,我使用 SimpleHttpServer (http://www.andyjamesdavies.com/blog/javascript/simple-http-server-on-mac-os-x-in-seconds) ,然后使用 localhost URL 加载 Request。使用这种方法,你不必添加任何库,但网站文件将不会在捆绑,所以它将不会交付。因此,这将更适合于 Debug 案例。

他们终于解决了这个问题! 现在我们可以使用 -[WKWebView loadFileURL:allowingReadAccessToURL:]。 很明显,在 WWDC 2015视频504介绍 Safari 视图控制器里修复值几秒钟

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

我已经设法在 OS X 上使用 PHP 的 web 服务器。复制到临时/www 目录对我来说不起作用。PythonSimpleHTTPServer 抱怨希望读取 MIME 类型,这可能是一个沙箱问题。

Here’s a server using 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()

除了丹 · 法布里奇提到的解决方案之外,XWebView是另一种解决方案。

我还不能发表评论,所以我把它作为一个单独的答案发布。

这是目标 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;
}

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

函数 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
}

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

当您将网页资料夹移至专案时,请选取「建立资料夹参考」

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

如果您试图在较大的 HTML 字符串(如: <img src="file://...">)的中间显示本地图像,那么它仍然不会出现在设备上,因此我将图像文件加载到 NSData 中,并能够通过用数据本身替换 src 字符串来显示它。帮助构建要加载到 WKWebView 中的 HTML 字符串的示例代码,其中 result 将替换 src = “”引号中的内容:

斯威夫特:

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

目标 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下解决这个问题的人:

如果页不复杂,则可以选择将该页作为单页应用程序。

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

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

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

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

[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:FILE_PATH]
allowingReadAccessToURL: [NSURL fileURLWithPath:FILE_PATH]
];