WKWebView 不加载 iOS 8 下的本地文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24882834/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WKWebView not loading local files under iOS 8
提问by Lim Thye Chean
For previous iOS 8 betas, load a local web app (in Bundle) and it works fine for both UIWebView
and WKWebView
, and I even ported a web game using the new WKWebView
API.
对于以前的iOS测试版8,负荷(束)的本地网络应用程序,它工作得很好既UIWebView
和WKWebView
,我甚至移植使用新的网页游戏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)
But in beta 4, I just got a blank white screen (UIWebView
still work), looks like nothing is loaded or executed. I saw an error in the log:
但是在 beta 4 中,我只看到一个空白的白屏(UIWebView
仍然有效),看起来没有加载或执行任何内容。我在日志中看到一个错误:
Could not create a sandbox extension for /
无法创建沙箱扩展 /
Any help to guide me to the right direction? Thanks!
任何帮助指导我走向正确的方向?谢谢!
回答by nacho4d
They finally solved the bug! Now we can use -[WKWebView loadFileURL:allowingReadAccessToURL:]
.
Apparently the fix was worth some seconds in WWDC 2015 video 504 Introducing Safari View Controller
他们终于解决了这个错误!现在我们可以使用-[WKWebView loadFileURL:allowingReadAccessToURL:]
. 显然,WWDC 2015 视频 504 Introducing Safari View Controller 中的修复值得几秒钟
For iOS8 ~ iOS10 (Swift 3)
适用于 iOS8 ~ iOS10 (Swift 3)
As Dan Fabulish's answerstates this is a bug of WKWebView which apparently is not being solved any time soonand as he said there is a work-around :)
正如Dan Fabulish 的回答所说,这是WKWebView 的一个错误,显然不会很快得到解决,正如他所说,有一个解决方法:)
I am answering just because I wanted to show the work-around here. IMO code shown in https://github.com/shazron/WKWebViewFIleUrlTestis full of unrelated details most people are probably not interested in.
我回答只是因为我想在这里展示解决方法。https://github.com/shazron/WKWebViewFIleUrlTest 中显示的 IMO 代码充满了大多数人可能不感兴趣的无关细节。
The work-around is 20 lines of code, error handling and comments included, no need of a server :)
解决方法是 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
}
And can be used as:
并且可以用作:
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)
}
}
}
回答by Dan Fabulich
WKWebView can't load content from file: URLs via its loadRequest:
method. http://www.openradar.me/18039024
WKWebView 无法通过其loadRequest:
方法从 file: URLs 加载内容。http://www.openradar.me/18039024
You can load content via loadHTMLString:
, but if your baseURL is a file: URL, then it still won't work.
您可以通过 加载内容loadHTMLString:
,但如果您的 baseURL 是 file: URL,那么它仍然无法正常工作。
iOS 9 has a new API that will do what you want,[WKWebView loadFileURL:allowingReadAccessToURL:]
.
iOS 9 有一个新的 API 可以做你想做的事,[WKWebView loadFileURL:allowingReadAccessToURL:]
.
There is a workaround for iOS 8, demonstrated by shazron in Objective-C here https://github.com/shazron/WKWebViewFIleUrlTestto copy files into /tmp/www
and load them from there.
有针对iOS 8一种变通方法,通过shazron在Objective-C在这里展示https://github.com/shazron/WKWebViewFIleUrlTest以将文件复制到/tmp/www
并从那里加载它们。
If you're working in Swift, you could try nachos4d's sampleinstead. (It's also much shorter than shazron's sample, so if you're having trouble with shazron's code, give that a try instead.)
如果您使用 Swift,则可以尝试使用 nachos4d 的示例。(它也比 shazron 的示例短得多,所以如果您在使用 shazron 的代码时遇到问题,请尝试一下。)
回答by Markus T.
An example of how to use [WKWebView loadFileURL:allowingReadAccessToURL:] on iOS 9.
如何在iOS 9上使用 [WKWebView loadFileURL:allowingReadAccessToURL:] 的示例。
When you are moving the web folder to a project, select "Create folder references"
将 Web 文件夹移动到项目时,选择“创建文件夹引用”
Then use code that is something like this(Swift 2):
然后使用类似这样的代码(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)
}
}
In the html file use filepaths like this
在 html 文件中使用这样的文件路径
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
not like this
不是这样
<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">
An example of directory that is moved to a xcode project.
移动到 xcode 项目的目录示例。
回答by EthanB
Temporary workaround: I'm using GCDWebServer, as suggested by GuidoMB.
临时解决方法:按照 GuidoMB 的建议,我正在使用GCDWebServer 。
I first find the path of my bundled "www/" folder (which contains an "index.html"):
我首先找到我捆绑的“www/”文件夹的路径(其中包含一个“index.html”):
NSString *docRoot = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"www"].stringByDeletingLastPathComponent;
... then start it up like so:
...然后像这样启动它:
_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;
Performance appears fine, even on an iPad 2.
性能看起来不错,即使在 iPad 2 上也是如此。
I did notice a crash after the app goes into the background, so I stop it on applicationDidEnterBackground:
and applicationWillTerminate:
; I start/restart it on application:didFinishLaunching...
and applicationWillEnterForeground:
.
我没有通知应用程序后崩溃进入后台,所以我停止它applicationDidEnterBackground:
和applicationWillTerminate:
; 我开始/重新启动它application:didFinishLaunching...
和applicationWillEnterForeground:
。
回答by nullqube
[configuration.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"];
This solved the problem for me iOS 8.0+ dev.apple.com
这为我解决了问题 iOS 8.0+ dev.apple.com
also this seems to worked just fine too...
这似乎也工作得很好......
NSString* FILE_PATH = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:@"htmlapp/FILE"];
[self.webView
loadFileURL: [NSURL fileURLWithPath:FILE_PATH]
allowingReadAccessToURL: [NSURL fileURLWithPath:FILE_PATH]
];
回答by soflare
Besides solutions mentioned by Dan Fabulich, XWebViewis another workaround. [WKWebView loadFileURL:allowingReadAccessToURL:]is implemented through extension.
除了 Dan Fabulich 提到的解决方案,XWebView是另一种解决方法。[WKWebView loadFileURL:allowingReadAccessToURL:]是通过扩展实现。
回答by Faryar
I cannot comment yet, so I am posting this as a separate answer.
我还不能发表评论,所以我将此作为单独的答案发布。
This is an objective-c version of nacho4d's solution. The best workaround I've seen so far.
这是nacho4d 解决方案的 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;
}
回答by patrickd105
In the case that you are trying to display a local image in the middle of a larger HTML string like: <img src="file://...">
, it still does not appear on device so I loaded the image file into NSData and was able to display it by replacing the src string with the data itself. Sample code to help build the HTML string to load into WKWebView, where result is what will replace what's inside the quotes of src="":
如果您尝试在较大的 HTML 字符串中间显示本地图像,例如:<img src="file://...">
,它仍然不会出现在设备上,因此我将图像文件加载到 NSData 中,并且能够通过将 src 字符串替换为数据本身。帮助构建 HTML 字符串以加载到 WKWebView 的示例代码,其中 result 将替换 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:
目标-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];
回答by firebear
For who must workaround this issue under iOS8:
对于必须在 iOS8 下解决此问题的人:
If your page is not complicated, you might choose to make the page as a Single Page Application.
如果您的页面不复杂,您可以选择将页面设为单页应用程序。
In other words, to embed all the resources into the html file.
换句话说,将所有资源嵌入到 html 文件中。
To do: 1. copy your js/css file's content into / tags in the html file respectively; 2. convert your image files into svg to replace the accordingly. 3. load the page as before, using [webView loadHTMLString: baseURL:], for example
要做: 1. 将你的js/css文件的内容分别复制到html文件中的/标签中;2. 将您的图像文件转换为 svg 以相应地替换。3. 像之前一样加载页面,例如使用[webView loadHTMLString: baseURL:]
It was a bit different to styling a svg image, but it should not block you so much.
这与设置 svg 图像的样式有点不同,但它不应该对您造成太大影响。
It seemed that the page render performance decreased a bit, but it was worthy to have such a simple workaround worked under iOS8/9/10.
看起来页面渲染性能下降了一点,但在iOS8/9/10下有这样一个简单的解决方法是值得的。
回答by Dustin Nielson
I'm using the below. Has some extra stuff I'm working on but you can see where I've commented out the loadRequest and am substituting loadHTMLString call. Hope this helps until they fix the bug.
我正在使用下面的。有一些我正在处理的额外内容,但您可以看到我在哪里注释掉了 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)")
}
}