WKWebView 确实从本地文档文件夹加载资源
WKWebView does load resources from local document folder
在我的 Swift iOS 应用程序中,我想从远程服务器下载一些动态 HTML 页面,将它们保存在文档目录中,并从文档目录中显示这些页面.
我用它来加载页面:
var appWebView:WKWebView?
...
appWebView!.loadRequest(NSURLRequest(URL: NSURL(fileURLWithPath: htmlPath)))
在模拟器上一切正常,但当我转到真机时,它只显示一个空白页面。然后我使用 Safari 连接到应用程序,发现它抱怨 "Failed to load resource"。
然后我尝试先阅读 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,但没有帮助。)
这是我在我的一个项目(iOS 10,Swift 3)中用来加载本地文件的简化版本。根据 Raghuram 和 Fox5150 在 iOS 10.3.1 和 iPhone 7+ 上再次测试后,我刚刚 更新了我的代码 (7.5.2017)评论。
我刚刚创建了一个全新的项目,这是文件夹结构:
2018 年 4 月 19 日更新: 添加了一个新功能,可以下载包含 HTML、CSS、JS 文件的 .zip,在 /Documents 中解压/ (Alamofire + Zip) 然后将这些文件加载到 webView 中。您也可以在 GitHub sample project 中找到它。再次强调,请随意分叉和加注星标! :)
更新 08.02.2018: 终于添加了一个 GitHub sample project,其中还包括一个本地 JavaScript 文件。随意分叉和加星! :)
版本 1 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
}
}
版本 2 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">
而不是
鉴于 2 个版本和问题,这里是我的 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 示例项目,请在评论部分告诉我,我会上传它。
效果很好(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.")
}
}
}
}
这个解决方案帮助了我:
[configuration.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"];
Swift 4 方法
此方法允许 WKWebView 正确读取您的目录层次结构和 sub-directories 链接的 CSS/JS 文件。您 不需要 更改您的 HTML、CSS 或 JS 代码。
更新为 Xcode 9.3
第 1 步
将本地网络文件的文件夹导入到您的项目中。确保您:
☑️ Copy items if needed
☑️ Create folder references (not "Create groups")
☑️ Add to targets
第 2 步
使用 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)
}
}
如果大家对这个方法或代码还有其他疑问,我会尽力解答。 :)
检查那张票:
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)
}
这适用于文件 URL 或远程 URL,无论文件是在包中还是在文档中:
if url.isFileURL {
webView.loadFileURL(url, allowingReadAccessTo: url)
} else {
let request = URLRequest(url: url)
webView.load(request)
}
以这种方式构建 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)
文件必须在文档目录中。
我执行了以下操作来检索文档:
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)
}
在我的 Swift iOS 应用程序中,我想从远程服务器下载一些动态 HTML 页面,将它们保存在文档目录中,并从文档目录中显示这些页面.
我用它来加载页面:
var appWebView:WKWebView?
...
appWebView!.loadRequest(NSURLRequest(URL: NSURL(fileURLWithPath: htmlPath)))
在模拟器上一切正常,但当我转到真机时,它只显示一个空白页面。然后我使用 Safari 连接到应用程序,发现它抱怨 "Failed to load resource"。
然后我尝试先阅读 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,但没有帮助。)
这是我在我的一个项目(iOS 10,Swift 3)中用来加载本地文件的简化版本。根据 Raghuram 和 Fox5150 在 iOS 10.3.1 和 iPhone 7+ 上再次测试后,我刚刚 更新了我的代码 (7.5.2017)评论。
我刚刚创建了一个全新的项目,这是文件夹结构:
2018 年 4 月 19 日更新: 添加了一个新功能,可以下载包含 HTML、CSS、JS 文件的 .zip,在 /Documents 中解压/ (Alamofire + Zip) 然后将这些文件加载到 webView 中。您也可以在 GitHub sample project 中找到它。再次强调,请随意分叉和加注星标! :)
更新 08.02.2018: 终于添加了一个 GitHub sample project,其中还包括一个本地 JavaScript 文件。随意分叉和加星! :)
版本 1 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
}
}
版本 2 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">
而不是
鉴于 2 个版本和问题,这里是我的 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 示例项目,请在评论部分告诉我,我会上传它。
效果很好(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.")
}
}
}
}
这个解决方案帮助了我:
[configuration.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"];
Swift 4 方法
此方法允许 WKWebView 正确读取您的目录层次结构和 sub-directories 链接的 CSS/JS 文件。您 不需要 更改您的 HTML、CSS 或 JS 代码。
更新为 Xcode 9.3
第 1 步
将本地网络文件的文件夹导入到您的项目中。确保您:
☑️ Copy items if needed
☑️ Create folder references (not "Create groups")
☑️ Add to targets
第 2 步
使用 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)
}
}
如果大家对这个方法或代码还有其他疑问,我会尽力解答。 :)
检查那张票:
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)
}
这适用于文件 URL 或远程 URL,无论文件是在包中还是在文档中:
if url.isFileURL {
webView.loadFileURL(url, allowingReadAccessTo: url)
} else {
let request = URLRequest(url: url)
webView.load(request)
}
以这种方式构建 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)
文件必须在文档目录中。
我执行了以下操作来检索文档:
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)
}