WKWebView可以从本地文档文件夹加载资源。

40

在我的Swift iOS应用程序中,我想从远程服务器下载一些动态HTML页面,将它们保存在文档目录中,并从文档目录中显示这些页面。

我曾使用以下方式加载该页面:

var appWebView:WKWebView?
...
appWebView!.loadRequest(NSURLRequest(URL: NSURL(fileURLWithPath: htmlPath)))

模拟器上一切正常,但当我移动到真实手机时,它只显示一个空白页面。 然后我使用Safari连接到应用程序,并发现它报错“加载资源失败”。

然后我尝试首先读取htmlPath页面的内容,然后使用

appWebView!.loadHTMLString()

加载页面。如果HTML页面简单,则起作用。但是,如果HTML引用其他内容,例如文档目录中的JavaScript文件(具有类似于<script src="file:////var/mobile/Containers/Data/Application/762035C9-2BF2-4CDD-B5B1-574A0E2B0728/Documents/xxxxx.js">的绝对路径),则无法加载。

是否有人知道为什么会发生这种情况,以及如何解决这个问题?

更多信息:

  • XCode版本:7.3.1
  • 部署目标:8.1(我也尝试使用9.3,但没有帮助)

你有检查过这个吗? - Roman Ermolov
谢谢!我会尝试的。 - Peter
请粘贴您的下载/保存 HTML 内容代码。 - Jakub
根据我的经验,当WebView从视图层次结构中分离时,WKWebView存在加载问题。这可能是您的问题所在。此外,您可以尝试使用以下API进行文件请求: func loadFileURL(_ URL: URL, allowingReadAccessTo readAccessURL: URL) -> WKNavigation? [https://developer.apple.com/reference/webkit/wkwebview/1414973-loadfileurl] - DocForNoc
8个回答

42
这是我在我的项目中使用的加载本地文件的简化版(iOS 10,Swift 3)。如Raghuram和Fox5150在评论中所请求的,在iOS 10.3.1和iPhone 7+上测试后,我刚刚更新了我的代码(7.5.2017)。
我刚刚创建了一个全新的项目,这是文件夹结构:enter image description here 更新于19.04.2018:添加了一个新功能,可以下载包含HTML、CSS、JS文件的.zip文件,将其解压缩到/Documents/(使用Alamofire + Zip),然后将这些文件加载到webView中。您也可以在GitHub示例项目中找到它。同样,随意fork & star! :)

更新于2018年08月02日:最终添加了一个GitHub示例项目,其中包含一个本地的JavaScript文件。欢迎fork和star!:)

使用webView.loadFileURL()的第一版

ViewController.swift

import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        let webView = WKWebView()
        let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
        let htmlUrl = URL(fileURLWithPath: htmlPath!, isDirectory: false)
        webView.loadFileURL(htmlUrl, allowingReadAccessTo: htmlUrl)
        webView.navigationDelegate = self
        view = webView
    }
}

使用webView.loadHTMLString()的第二个版本

ViewController.swift

import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        let webView = WKWebView()
        let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
        let folderPath = Bundle.main.bundlePath
        let baseUrl = URL(fileURLWithPath: folderPath, isDirectory: true)
        do {
            let htmlString = try NSString(contentsOfFile: htmlPath!, encoding: String.Encoding.utf8.rawValue)
             webView.loadHTMLString(htmlString as String, baseURL: baseUrl)
        } catch {
            // catch error
        }
        webView.navigationDelegate = self
        view = webView
    }
}

需要注意的问题:

  • 确保您的本地 html/js/css 文件在项目 -> 目标 -> 构建阶段 -> 复制捆绑资源中
  • 确保您的 html 文件不引用相对路径,例如 css/styles.css,因为 iOS 会压缩您的文件结构,并将 styles.css 放在与 index.html 同一级别,所以请改写为 <link rel="stylesheet" type="text/css" href="styles.css">

鉴于这两个版本和注意事项,以下是我的项目中的 html/css 文件:

web/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
    <title>Offline WebKit</title>
    <link rel="stylesheet" type="text/css" href="styles.css">
  </head>
  <body>
    <h1 id="webkit-h1">Offline WebKit!</h1>
  </body>
</html>

web/css/styles.css

#webkit-h1 {
  font-size: 80px;
  color: lightblue;
}

如果有人需要 GitHub 的示例项目,请在评论区告诉我,我会上传它。

1
看起来我在调用loadHTMLString时可能错过了“baseURL”参数。但我更喜欢Roman指出的内容:(https://dev59.com/92Af5IYBdhLWcg3wOQi2#28676439)。实际上,我的html页面中有一个<base>标签,它引用了远程服务器(因为该文件包含一些调用远程服务器的javascript,但没有使用绝对路径,我不想更新整个javascript文件中的所有这些调用),同时,html也在本地驱动器上使用一些javascript和css文件。 - Peter
@Raghuram 谢谢!你在哪里得到了 nil?我猜是 path! 吗? - tech4242
4
@tech4242 很好的回答!你也可以在 HTML/CSS/JS 中支持子文件夹引用,方法是使用文件夹引用添加你的资源。你可以将 HTML 资源文件夹拖放到 Xcode 项目结构中,并选择“创建文件夹引用”。然后,通过以下方式访问整个资源目录结构:(假设你的文件夹名为 HtmlAssets)Bundle.main.url(forResource: "HtmlAssets", withExtension: nil) - m_katsifarakis
3
版本1在macOS上可以使用WKWebView。但是版本2在我使用WKWebView在macOS上不能工作。看起来allowingReadAccessTo参数可能是在macOS上解锁访问基本URL路径的关键。 - mservidio
1
@tech4242 上述示例运行良好,但如果我添加与styles.css相同的javascript文件,则在html中不会加载。 - Dhananjay Patil
显示剩余14条评论

15

Swift 4 方法

此方法允许 WKWebView 正确读取链接的 CSS/JS 文件的目录结构及其子目录。您无需更改 HTML、CSS 或 JS 代码。

更新于 Xcode 9.3

第一步

将本地网页文件夹导入项目中任意位置。确保您:

Xcode > File > Add Files to "Project"

☑️ 必要时复制项目

☑️ 创建文件夹引用 (不是 "创建组")

☑️ 添加到目标

第二步

进入带有 WKWebView 的视图控制器并在 viewDidLoad 方法中添加以下代码:

let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "website")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
  • index – 要加载的文件名(不带 .html 扩展名)
  • website – 您的网站文件夹的名称(index.html 应该在此目录的根目录下)

结论

整个代码应该看起来像这样:

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {

    @IBOutlet weak var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        webView.uiDelegate = self
        webView.navigationDelegate = self

        let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "Website")!
        webView.loadFileURL(url, allowingReadAccessTo: url)
        let request = URLRequest(url: url)
        webView.load(request)
    }

}

如果你们有关于这种方法或代码的更多问题,我会尽力回答。:)


3
如果您允许,我问一个简短的问题:为什么您使用webView.loadFileURL再加上webView.load呢?只使用loadFileURL不行吗? - giovannipds
那是一个错误。你不想要两个。 - drewster

3
这个解决方案对我很有帮助:
[configuration.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"];

2
如果我们使用这个,苹果会接受我们的应用程序申请吗? - Iqbal Khan
我遇到了问题,我使用了这个,现在它正在加载文件。 - Iqbal Khan
我正在使用 Xcode 9.4.1,它在最新版本中运行得很好,但我在 iOS 9.0 中无法使其正常工作。 - Vincent Bacalso

1
这个在编程方面很有效(Swift 3,Xcode 8):
import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {

    var webView: WKWebView!

    override func loadView() {
        webView = WKWebView()
        webView.navigationDelegate = self
        view = webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        if let url = Bundle.main.url(forResource: "file", withExtension: "txt")
        {
            do
            {
                let contents = try String(contentsOfFile: url.path)
                webView.loadHTMLString(contents, baseURL: url.deletingLastPathComponent())
            }
            catch
            {
                print("Could not load the HTML string.")
            }
        }
    }
}

1
这样构建URL可以让我使用WKWebView从文档目录加载资源:
guard let docDir = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) else {
    return
}

let resourceURL = docDir.appendingPathComponent("/Path/To/Your/Resource")
self.wkWebView.loadFileURL(resourceURL, allowingReadAccessTo: docDir)

通过您提供的解决方案,我已经可以从文档目录加载单个图像到WKWebView中,但是您能否帮助我?我有一个包含图像最后路径的HTML文件,并且我已经将这些图像下载到文档目录中,如何在WKWebView中显示这些HTML图像? - iMinion

1
这个可以很好地处理文件URL或远程URL,无论文件是在捆绑包中还是在文档中:
if url.isFileURL {
    webView.loadFileURL(url, allowingReadAccessTo: url)
} else {
    let request = URLRequest(url: url)
    webView.load(request)
}

0

文件必须位于文档目录中。

我实现了以下代码以检索文档:

let documentDirUrl = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileNameWithExtension = "IMG_0002.PNG"
let indexFileUrl = documentDirUrl.appendingPathComponent(fileNameWithExtension)
if FileManager.default.fileExists(atPath: indexFileUrl.path) {
    webView.loadFileURL(indexFileUrl, allowingReadAccessTo: documentDirUrl)
}

-1

请检查此票:iOS: 如何在WKWebView中加载本地文件(不在捆绑包中)?

var nsurl = URL(fileURLWithPath: URL(fileURLWithPath: URL(fileURLWithPath: documentsDirectory()).appendingPathComponent(user_appli).absoluteString).appendingPathComponent("index.html").absoluteString) //locally
var readAccessToURL: URL? = nsurl.deletingLastPathComponent?.deletingLastPathComponent

if let anURL = readAccessToURL {
    webView?.loadFileURL(nsurl, allowingReadAccessTo: anURL)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接