WKWebView 无法使用 loadHTMLString(_, baseURL:) 加载图像和 CSS
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39901982/
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 fails to load images and CSS using loadHTMLString(_, baseURL:)
提问by Mischa
In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView.
在 iOS 8 及更高版本中运行的应用程序中,使用 WKWebView 类而不是使用 UIWebView。
Thus, I have replaced my good old UIWebView
with a shiny new WKWebView
. But what I thought to be an easy exercise (simply swapping the classes and replacing the delegate methods) turned out to be a real mess.
因此,我UIWebView
用闪亮的新WKWebView
. 但我认为是一个简单的练习(简单地交换类并替换委托方法)结果却是一团糟。
The Problem
问题
When I load an HTML string using
当我使用加载 HTML 字符串时
loadHTMLString(String, baseURL: URL?)
the web view loads and renders the pure HTML but it doesn't load any images or CSS files referenced inside the htmlString
.
Web 视图加载并呈现纯 HTML,但不会加载 .html 文件中引用的任何图像或 CSS 文件htmlString
。
This happens only on a real device!
In Simultor all referenced resources are loaded correctly.
这仅发生在真实设备上!
在 Simultor 中,所有引用的资源都正确加载。
Example
例子
I have defined a simple htmlString
in my view controller class:
我htmlString
在我的视图控制器类中定义了一个简单的:
let imageName = "image.png"
let libraryURL: URL // The default Library URL
var htmlString: String {
return "<html> ... <img src=\"\(imageName)\" /> ... </html>"
// "..." represents more valid HTML code incl. header and body tags
}
The image is stored in the root Library folder so its URL is:
该图像存储在根库文件夹中,因此其 URL 为:
let imageURL = libraryURL.appendingPathComponent(imageName)
Now I load the htmlString
into the web view:
现在我将其加载htmlString
到 Web 视图中:
webView.loadHTMLString(htmlString, baseURL: libraryURL)
and it doesn't load the image even though the baseURL
is set correctly.
即使baseURL
设置正确,它也不会加载图像。
Ideas for a Solution
解决方案的想法
Maybe
WKWebView
has a problem with resolving relative paths so my first idea was to use absolute pathsinside the HTML string instead.
→ ? Doesn't work.Two answers to another SO postsuggested that using
loadFileURL(URL, allowingReadAccessTo: URL)
instead ofloadHTMLString(...)
works in iOS 9+.
→ ? That works.
也许
WKWebView
解析相对路径有问题,所以我的第一个想法是在 HTML 字符串中使用绝对路径。
→ ? 不起作用。另一个 SO 帖子的两个答案建议使用
loadFileURL(URL, allowingReadAccessTo: URL)
而不是loadHTMLString(...)
在 iOS 9+ 中工作。
→ ? 那个有效。
However, I cannot use solution 2 because my HTML files are encrypted and the decrypted files must not be stored on the disk.
但是,我无法使用解决方案 2,因为我的 HTML 文件已加密,并且解密后的文件不得存储在磁盘上。
Question
题
Is there any way to load local resources like images and styles using the WKWebView
's
有没有办法使用WKWebView
's加载本地资源,如图像和样式
loadHTMLString(String, baseURL: URL?)
function? Or is still a bug in iOS 9+?
功能?或者仍然是 iOS 9+ 中的错误?
(I just cannot believe that Apple provides and recommends using a web view that cannot load any local web content from inside an HTML string?!)
(我简直不敢相信 Apple 提供并推荐使用无法从 HTML 字符串中加载任何本地 Web 内容的 Web 视图?!)
回答by Petro Korienev
Without taking a look at your actual project it's difficult to give some hundreed percent sure advices.
如果不查看您的实际项目,就很难给出百分之百肯定的建议。
However:
然而:
class ViewController: UIViewController {
var webView = WKWebView()
override func viewDidLoad() {
super.viewDidLoad()
webView.translatesAutoresizingMaskIntoConstraints = false
let views = [
"webView" : webView
]
view.addSubview(webView)
var constraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|[webView]|", options: [.AlignAllLeading, .AlignAllTrailing], metrics: nil, views: views)
constraints.appendContentsOf(NSLayoutConstraint.constraintsWithVisualFormat("V:|[webView]|", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: views))
NSLayoutConstraint.activateConstraints(constraints)
let path = NSBundle.mainBundle().pathForResource("ios - WKWebView fails to load images and CSS using loadHTMLString(_, baseURL_) - Stack Overflow", ofType: "htm")
let url = NSURL(fileURLWithPath: path!)
webView.loadHTMLString(try! String(contentsOfURL: url), baseURL: url.URLByDeletingLastPathComponent)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I think the key point here is baseUrl
parameter, you should setup it correctly. In my case i've used html's url without last path component - e.g. containing folder. This works fine on both device & simulator - check device snapshot. I've uploaded sample project to https://github.com/soxjke/WKWebViewTestso you can take a look (i've removed codesigning info from git)
我认为这里的关键点是baseUrl
参数,您应该正确设置它。在我的情况下,我使用了没有最后一个路径组件的 html url - 例如包含文件夹。这在设备和模拟器上都可以正常工作 - 检查设备快照。我已将示例项目上传到 https://github.com/soxjke/WKWebViewTest,因此您可以查看(我已从 git 中删除了代码设计信息)
So, to recap - method is working, functionality is working, just you do something wrong with it. To help you get what's wrong with your solutions, i'll add some suggestions:
1. Remember, that simulator filesystem is case-insensitive, device filesystem is case-sensitive. So if you have your filenames in html in lowercase - this won't work on device. 8fFsD.png != 8ffsd.png
2. Remember, that when copying resources, XCode ignores your folder structure. So if your html has <img src="./img/1.png">
and your XCOde project has folder structure like
所以,总结一下 - 方法有效,功能有效,只是你做错了什么。为了帮助您找出解决方案的问题,我将添加一些建议: 1. 请记住,模拟器文件系统不区分大小写,设备文件系统区分大小写。因此,如果您的文件名是小写的 html - 这将无法在设备上运行。8fFsD.png != 8ffsd.png
2. 请记住,在复制资源时,XCode 会忽略您的文件夹结构。因此,如果您的 html 具有<img src="./img/1.png">
并且您的 XCOde 项目具有类似的文件夹结构
test.htm
img/
1.png
2.png
After build it will be flattened, so test.htm and 1.png and 2.png will reside on same level
构建后它将被展平,因此 test.htm 和 1.png 和 2.png 将驻留在同一级别
test.htm
1.png
2.png
I'm almost sure, after you verify these two assumptions, you'll get this method working.
我几乎可以肯定,在您验证了这两个假设之后,您将可以使用此方法。
回答by Scott Browne
I had this problem today, I've found the solution and potentially the cause:
我今天遇到了这个问题,我找到了解决方案和可能的原因:
loadHTMLString(String, baseURL: URL?)
This function doesn't allow the rendered HTML to access local media, as far as I'm aware, this is because it would be an injection risk, this could allow rendered HTML to access and manipulate your local file system. With a html string, that could come from anywhere or anyone.
这个函数不允许渲染的 HTML 访问本地媒体,据我所知,这是因为它会存在注入风险,这可能允许渲染的 HTML 访问和操作您的本地文件系统。使用 html 字符串,它可以来自任何地方或任何人。
loadFileURL(URL, allowingReadAccessTo: URL)
With this function, you point the WKWebview to the html file in your FileManager, and to the containing folder with 'allowingReadAccessTo'. Because the html is stored within the FileManager, it will allow the rendered HTML to access locally stored media.
使用此函数,您将 WKWebview 指向 FileManager 中的 html 文件,并指向包含“allowingReadAccessTo”的文件夹。因为 html 存储在 FileManager 中,它将允许呈现的 HTML 访问本地存储的媒体。
If you don't have the html file stored locally for some reason(I assume you do), You could write the html sting into a .html file, then point to the URL of that file. However, this is just subverting Apple's protection, so do it at your own peril (don't do it).
如果由于某种原因您没有在本地存储 html 文件(我假设您有),您可以将 html 字符串写入 .html 文件,然后指向该文件的 URL。但是,这只是在破坏 Apple 的保护,因此请自担风险(不要这样做)。
This is just the solution that worked for me and my understanding of why we're having the problem to begin with.
这只是对我有用的解决方案,也是我对为什么我们开始遇到问题的理解。
Edit #1: Typo.
编辑#1:错别字。
Edit #2: I've since found another nuance, When stating the 'allowingReadAccessTo:' URL, if the HTML itself needs to access things in parent folders (ie: .css, .js files), you need to specify the parent folder, not necessarily the location of the HTML itself, this will then implicitly allow access to the child folders as required also. For me, this problem was only apparent on a physical device, this didn't seem to have an effect whilst running in simulator, likely another discrepancy between how permissions work on simulator and a physical device.
编辑 #2:我发现了另一个细微差别,当声明“allowingReadAccessTo:”URL 时,如果 HTML 本身需要访问父文件夹(即:.css、.js 文件)中的内容,则需要指定父文件夹,不一定是 HTML 本身的位置,这也将隐式允许访问子文件夹。对我来说,这个问题只在物理设备上很明显,这在模拟器中运行时似乎没有影响,这可能是模拟器和物理设备上的权限工作方式之间的另一个差异。
回答by ianyh
I've been experimenting with this as well, with similar restrictions, and the problem appears to be that paths aren't resolved unless baseURL
references the application bundle. It doesn't work if you, for example, have something in the application's documents.
我也一直在试验这个,有类似的限制,问题似乎是除非baseURL
引用应用程序包,否则路径不会被解析。例如,如果您在应用程序的文档中有某些内容,则它不起作用。
Edit: I have filed a radar for this rdar://29130863
编辑:我已经为此rdar://29130863提交了一个雷达
回答by tech4242
Well you should be able to use local images and CSS files (and JavaScript files for that matter) with WKWebViews with the function that you have already found. My guess is that the problem is with your baseURL
variable.
好吧,您应该能够将本地图像和 CSS 文件(以及与此相关的 JavaScript 文件)与 WKWebViews 与您已经找到的功能一起使用。我的猜测是问题出在你的baseURL
变量上。
Update 7.5.2017:
2017 年 5 月 7 日更新:
I have completely updated the code from another SO answerof mine that used to be linked to my answer here. I have a working project for loadHTMLString()
and .loadFileURL()
我已经完全更新了我的另一个 SO 答案中的代码,该答案曾经链接到我的答案here。我有一个工作项目loadHTMLString()
和.loadFileURL()
回答by Chris Brandsma
Personally, I had to switch to using XWebViewas the out-of-the-box behavior of WKWebView does not allow loading of local files. XWebView tricks it by loading up a local web server in the background and directing local traffic thru it. (XWebView is based on top of WKWebView)
就个人而言,我不得不改用XWebView,因为WKWebView的开箱即用行为不允许加载本地文件。XWebView 通过在后台加载本地 Web 服务器并通过它引导本地流量来欺骗它。(XWebView 基于 WKWebView 之上)
Seems a bit overkill, but that is what I ended up having to do.
似乎有点矫枉过正,但这就是我最终不得不做的。
回答by moliveira
You can base64 encode the images... I know that works. Not sure if it will be appropriate for your use case though.
您可以对图像进行 base64 编码...我知道这可行。不确定它是否适合您的用例。
Kind of funny, I just ran into this problem while doing the opposite - moving from base64 encoded to image files.
有点有趣,我只是在做相反的事情时遇到了这个问题 - 从 base64 编码到图像文件。
回答by kowal
Try to create baseURL
using:
尝试创建baseURL
使用:
let baseURL = URL(fileURLWithPath: "#path#")
let baseURL = URL(fileURLWithPath: "#path#")
instead of:
代替:
let baseURL = URL(string: "#path#")
let baseURL = URL(string: "#path#")
The main difference is that the first method adds file://
prefix before the path.
主要区别在于第一种方法file://
在路径前添加前缀。
回答by Keith Manilla
I was able to reproduce a similar issue. WKWebView loads my images specially if they are located remotely, apart from my app server.
我能够重现类似的问题。WKWebView 特别加载我的图像,如果它们位于远程,除了我的应用程序服务器。
For servers that are not SSL-secured (http instead of https), you can set your info.plist
as per below:
对于未受 SSL 保护的服务器(http 而不是 https),您可以info.plist
按以下方式设置:
App Transport Security Settings
- Allow Arbitrary Loads in Web Content (Set to YES)
- Allow Arbitrary Loads (Set to YES)
The problem was actually in the server. The server application was either:
问题实际上出在服务器上。服务器应用程序是:
- Changing the image src from
"http://IP-or-domain/uploads/file.jpg"
to"../../uploads/file.jpg"
- 将图像 src 从 更改
"http://IP-or-domain/uploads/file.jpg"
为"../../uploads/file.jpg"
- OR -
- 或者 -
- The image src was
"http://localhost/uploads/file.jpg"
or"http://127.0.0.1/uploads/file.jpg"
instead of"http://YOUR-SERVER-IP-ADDRESS/uploads/file.jpg"
- 图像 src 是
"http://localhost/uploads/file.jpg"
或"http://127.0.0.1/uploads/file.jpg"
代替"http://YOUR-SERVER-IP-ADDRESS/uploads/file.jpg"
In these cases, the actual device wont be able to locate the image. This only works with iOS Simulator because the virtual device is the same as the server and development machine. It can read LOCALHOST
and 127.0.0.1
.
在这些情况下,实际设备将无法定位图像。这仅适用于 iOS 模拟器,因为虚拟设备与服务器和开发机器相同。它可以读取LOCALHOST
和127.0.0.1
。
In my server, I was using a Rich Text Editor (TinyMCE) and it automatically removes the IP address after it detects that it's the same source.
在我的服务器中,我使用的是富文本编辑器 (TinyMCE),它在检测到 IP 地址是同一来源后会自动删除 IP 地址。