Reputation: 155
I use this code to load my html
file with text in 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")
}
and this code to load the page where the user stopped reading last time:
self.webView.scrollView.contentOffset.x = CGFloat(UserDefaults.standard.integer(forKey: "pageToLoad"))
I use code for loading last page in this method:
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"))
}
}
At first I used deadline: .now() + 0.1
, but that didn't work. Because the last read page was loaded initially, and after a few seconds I see my text on the first page. I change it to deadline: .now() + 0.5
and the text loads fine from the last page read. Its was 700 pages. But now I want to load another text with 1700 pages. And I have same problem like first time. I can change deadline: .now() + 1.0
and my text will load fine. But I think this is not the best solution. I run it on my iPhone X. But maybe if I run it on iPad mini 2 I should change deadline: .now() + 10.0
because iPad mini 2 not very powerful. How to solve the problem?
Update based on @DPrice code:
If I use this code:
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)"))
}
}
}
I have same bad result like in my code.
But if I use this code:
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)"))
}
}
}
}
Everything works fine. And my last page loading fine. But it does not solve the problem in my question.
Upvotes: 3
Views: 2057
Reputation: 77423
Here is a modified version of your ViewController
class:
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)
}
}
It uses an Observer to watch the contentSize
change in the web view's scroll view.
Note that it is called multiple times - with different values - during the load and layout process, but it may do the job for you.
Also note, though, that you'll need to account for changes in the web view size - for example, if the user rotates the device. So... more to do, but this may get you going.
Upvotes: 1
Reputation: 13768
Instead of observing WKWebView.estimatedProgress
you should observe UIScrollView.contentSize
because you need to scroll to an available position e.g.:
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)
}
}
}
}
Upvotes: 0
Reputation: 174
You can add a property observer and watch the estimated progress of the page load:
override func viewDidLoad() {
super.viewDidLoad()
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
....
}
and observe when the page is being loaded:
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")
}
}
}
You may be able to predict based on the page number how far into the loading process you need to be before you set your offset.
Upvotes: 1