125

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

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

无法为/

有什么帮助可以引导我走向正确的方向吗?谢谢!

4

14 回答 14

108

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

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

适用于 iOS8 ~ iOS10 (Swift 3)

正如Dan Fabulish 的回答所说,这是 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)
        }
    }
}
于 2015-02-23T14:51:16.153 回答
84

WKWebView 无法通过其loadRequest:方法从 file: URL 加载内容。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 的示例。(它也比 shazron 的示例短得多,所以如果您在使用 shazron 的代码时遇到问题,请尝试一下。)

于 2014-09-26T07:22:51.717 回答
8

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

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

在此处输入图像描述

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

在此处输入图像描述

于 2016-03-07T19:07:47.393 回答
6

临时解决方法:我正在使用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:

于 2014-10-20T22:13:20.213 回答
5
[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]
];
于 2017-08-06T21:38:57.460 回答
4

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

于 2015-04-28T17:10:58.773 回答
4

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

这是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;
}
于 2015-06-20T04:32:58.530 回答
4

如果您尝试在较大的 HTML 字符串(如:)的中间显示本地图像<img src="file://...">,它仍然不会出现在设备上,因此我将图像文件加载到 NSData 并能够通过将 src 字符串替换为来显示它数据本身。帮助构建 HTML 字符串以加载到 WKWebView 中的示例代码,其中的结果将替换 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];
于 2016-05-25T15:10:57.590 回答
1

我正在使用下面的。我正在处理一些额外的东西,但你可以看到我在哪里注释掉了 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)")

    }

}
于 2014-07-23T23:17:40.633 回答
1

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

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

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

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

这与 svg 图像的样式有点不同,但它不应该阻碍你。

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

于 2016-12-19T15:32:41.093 回答
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()
于 2015-03-17T10:13:42.647 回答
0

在 GCDWebServer 的同一行中,我使用 SIMpleHttpServer ( http://www.andyjamesdavies.com/blog/javascript/simple-http-server-on-mac-os-x-in-seconds ),然后使用本地主机 loadRequest网址。使用这种方法,您不必添加任何库,但网站文件不会包含在捆绑包中,因此无法交付。因此,这将更适合 Debug 案例。

于 2014-12-12T14:23:22.110 回答
0

@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
}
于 2016-02-20T20:14:07.957 回答
-1

尝试使用

[webView loadHTMLString:htmlFileContent baseURL:baseURL];

似乎它仍在工作。然而。

于 2014-07-22T17:47:17.197 回答