在 WKWebView 中加载 html 文本
Posted
技术标签:
【中文标题】在 WKWebView 中加载 html 文本【英文标题】:Load html text in WKWebView 【发布时间】:2020-10-27 19:46:03 【问题描述】:我使用此代码加载我的html
文件,其中包含WKWebView
中的文本:
do
guard let filePath = Bundle.main.path(forResource: "\(readBookNumber)", ofType: "html")
else
print ("File reading error")
return
var content = try String(contentsOfFile: filePath, encoding: .utf8)
let baseUrl = URL(fileURLWithPath: filePath)
content.changeHtmlStyle(font: "Iowan-Old-Style", fontSize: UserDefaults.standard.integer(forKey: "textSize"), fontColor: textColor)
webView.loadHTMLString(headerString+content, baseURL: baseUrl)
catch
print ("File HTML error")
这段代码加载用户上次停止阅读的页面:
self.webView.scrollView.contentOffset.x = CGFloat(UserDefaults.standard.integer(forKey: "pageToLoad"))
我在此方法中使用代码加载最后一页:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)
self.webView.scrollView.contentOffset.x = CGFloat(UserDefaults.standard.integer(forKey: "pageToLoad"))
一开始我用deadline: .now() + 0.1
,但没用。因为最初加载了最后一个阅读页面,几秒钟后,我在第一页上看到了我的文本。我将其更改为deadline: .now() + 0.5
,文本从阅读的最后一页加载正常。它有 700 页。但现在我想加载另一个 1700 页的文本。我和第一次一样有同样的问题。我可以更改deadline: .now() + 1.0
,我的文字会正常加载。但我认为这不是最好的解决方案。我在我的 iPhone X 上运行它。但如果我在 iPad mini 2 上运行它,我应该更改deadline: .now() + 10.0
,因为 iPad mini 2 不是很强大。如何解决问题?
根据@DPrice 代码更新:
如果我使用此代码:
override func viewDidLoad()
super.viewDidLoad()
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
....
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
if (keyPath == "estimatedProgress")
if webView.estimatedProgress == 1.0
self.webView.scrollView.contentOffset.x = CGFloat(UserDefaults.standard.integer(forKey: "pageToLoad\(self.readBookNumber)"))
我的代码和我的代码一样,结果很糟糕。
但如果我使用这段代码:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
if (keyPath == "estimatedProgress")
if webView.estimatedProgress == 1.0
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)
self.webView.scrollView.contentOffset.x = CGFloat(UserDefaults.standard.integer(forKey: "pageToLoad\(self.readBookNumber)"))
一切正常。我的最后一页加载正常。但这并不能解决我的问题。
【问题讨论】:
“我想加载另一个 1700 页的文本” ...这是要加载到 Web 视图中的大量文本,正如您所见,它可能需要很长时间来渲染。如果您正在尝试创建类似“书籍”阅读器的东西,您可能需要考虑另一种方法。 @DonMag 渲染时间对我来说不是很重要。重要的是时间量是准确的。我只是不能确定 5 秒是否适用于所有设备。也许某些设备会更长时间地处理文本,我错了。因此,我希望设备能够自动选择正确的渲染时间。例如,iPhone 12
1.0 秒就足够了,iPad mini 2
- 4.0 秒。我只想在设备中自动检测到它。而不是自己猜测正确的时间。因为它总是不准确,并可能导致不良行为。
您正在加载的 html 中是否有任何脚本正在运行?您如何“分页”“1700”页?如果您能提供一个示例 html 文件,将会很有帮助。
@DonMag 测试项目 - gofile.io/d/4wzUhA 见 style.css
行 overflow: -webkit-paged-x !important;
direction: ltr !important;
【参考方案1】:
这是您的 ViewController
类的修改版本:
import UIKit
import WebKit
class ViewController: UIViewController, UIScrollViewDelegate, WKNavigationDelegate
@IBOutlet weak var webView: WKWebView!
@IBOutlet weak var pagesLabel: UILabel!
var readBookNumber = 0
let headerString = "<meta name=\"viewport\" content=\"initial-scale=1.0\" />"
var textSize = 3
var contentSize: CGSize = .zero
override func viewDidLoad()
super.viewDidLoad()
// Web View Delegate
webView.scrollView.delegate = self
webView.navigationDelegate = self
webView.scrollView.isPagingEnabled = true
webView.scrollView.alwaysBounceVertical = false
webView.scrollView.showsHorizontalScrollIndicator = true
webView.scrollView.showsVerticalScrollIndicator = false
webView.scrollView.panGestureRecognizer.isEnabled = false
webView.scrollView.pinchGestureRecognizer?.isEnabled = false
webView.scrollView.bouncesZoom = false
self.webView.isOpaque = false;
self.webView.backgroundColor = .clear
// Load File
do
guard let filePath = Bundle.main.path(forResource: "0", ofType: "html")
else
print ("File reading error")
return
var content = try String(contentsOfFile: filePath, encoding: .utf8)
let baseUrl = URL(fileURLWithPath: filePath)
content.changeHtmlStyle(font: "Iowan-Old-Style", fontSize: 4, fontColor: "black")
webView.loadHTMLString(headerString+content, baseURL: baseUrl)
// add content size Observer
webView.scrollView.addObserver(self, forKeyPath: #keyPath(UIScrollView.contentSize), options: .new, context: nil)
catch
print ("File HTML error")
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
if (keyPath == #keyPath(UIScrollView.contentSize))
let contentSize = webView.scrollView.contentSize
if contentSize != self.contentSize
self.contentSize = contentSize
DispatchQueue.main.async
self.webView.scrollView.contentOffset.x = CGFloat(UserDefaults.standard.integer(forKey: "pageToLoad"))
// MARK: - webView Scroll View
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
self.stoppedScrolling()
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool)
if !decelerate
self.stoppedScrolling()
func scrollViewDidScroll(_ scrollView: UIScrollView)
var currentPage = Int((webView.scrollView.contentOffset.x / webView.scrollView.frame.size.width) + 1)
let pageCount = Int(webView.scrollView.contentSize.width / webView.scrollView.frame.size.width)
if currentPage == 0
currentPage = 1
else
if !webView.isHidden
pagesLabel.text = "\( currentPage ) из \( pageCount )"
else
pagesLabel.text = ""
func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?)
webView.scrollView.pinchGestureRecognizer?.isEnabled = false
func stoppedScrolling()
let pageToLoad = Int((webView.scrollView.contentOffset.x))
UserDefaults.standard.set(pageToLoad, forKey: "pageToLoad")
// MARK: - loading webView
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!)
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
// Маленькая задержка, которую мне хотелось бы использовать
/*DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)
self.webView.scrollView.contentOffset.x = CGFloat(UserDefaults.standard.integer(forKey: "pageToLoad"))
*/
// Большая задержка, которую мне приходится использовать
// don't do this here... we'll do the "auto-scroll" inside the change contentSize Observer
//DispatchQueue.main.asyncAfter(deadline: .now() + 2.0)
// self.webView.scrollView.contentOffset.x = CGFloat(UserDefaults.standard.integer(forKey: "pageToLoad"))
//
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error)
extension String
mutating func changeHtmlStyle(font: String, fontSize: Int, fontColor: String)
let style = "<font face='\(font)' size='\(fontSize)' color= '\(fontColor)'>%@"
self = String(format: style, self)
它使用 Observer 来观察 Web 视图的滚动视图中 contentSize
的变化。
请注意,它在加载和布局过程中被多次调用 - 使用不同的值 - 但它可能会为您完成这项工作。
另外请注意,您需要考虑 Web 视图大小的变化 - 例如,如果用户旋转设备。所以...还有更多工作要做,但这可能会让你继续前进。
【讨论】:
【参考方案2】:您可以添加一个属性观察者并观察页面加载的估计进度:
override func viewDidLoad()
super.viewDidLoad()
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
....
并观察页面何时被加载:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
if (keyPath == "estimatedProgress")
if webView.estimatedProgress == 1.0
print ("page loaded")
您可以根据页码预测在设置偏移量之前需要进入加载过程多远。
【讨论】:
我的结果与我的代码相同。我更新了我的问题并添加了有关我如何使用您的代码的详细信息。【参考方案3】:您应该观察UIScrollView.contentSize
,而不是观察WKWebView.estimatedProgress
,因为您需要滚动到可用位置,例如:
var positionY: CGFloat = 1000
var contentSize = CGSize(width: 0, height: 0)
override func viewDidLoad()
super.viewDidLoad()
...
webView?.scrollView.addObserver(self, forKeyPath: #keyPath(UIScrollView.contentSize), options: .new, context: nil)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
if (keyPath == #keyPath(UIScrollView.contentSize))
if let contentSize = webView?.scrollView.contentSize, contentSize != self.contentSize
self.contentSize = contentSize
if contentSize.height > positionY
webView?.scrollView.setContentOffset(CGPoint(x: 0, y: positionY), animated: true)
【讨论】:
我有一个像电子书阅读器一样的水平滚动视图。但是你的代码不起作用。因为,正如我所说,首先我看到最后阅读的页面,然后几秒钟后最后阅读页面变为第一页。以上是关于在 WKWebView 中加载 html 文本的主要内容,如果未能解决你的问题,请参考以下文章
在 WKwebView (SWIFT) 中加载本地资源以及 HTML 字符串?
在ios中加载html时如何在wkwebview中隐藏带有src的图像?