I am attempting to create an EPUB reader. Since the chapters in an EPUB are represented as whole HTML files with no page breaks, I will display one page at a time in a WKWebView
, loading the string with loadHTMLString
. Then, when the user swipes to the next page of the UIPageViewController
, I will make a new WKWebView
and scroll down a page using the setContentOffset
method. However, the setContentOffset
method has no effect on the displayed content. tldr - how can I tell when this setContentOffset
method is safe to call?
Here is my page view controller:
class MyPageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
var page = 0
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
setViewControllers([Page(page: 0)], direction: .forward, animated: false, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
return nil
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
return Page(page: page + 1)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
page += 1
}
}
}
and here is my Page
class:
class Page: UIViewController, WKNavigationDelegate, WKUIDelegate {
var page: Int
var webView: WKWebView!
let testString = "<h1>00000000000000000000000000000000000000000000000000</h1><br><h1>11111111111111111111111111111111111111111111111111</h1><br><h1>22222222222222222222222222222222222222222222222222</h1><br><h1>33333333333333333333333333333333333333333333333333</h1><br><h1>44444444444444444444444444444444444444444444444444</h1><br><h1>55555555555555555555555555555555555555555555555555</h1><br><h1>66666666666666666666666666666666666666666666666666</h1><br><h1>77777777777777777777777777777777777777777777777777</h1><br><h1>88888888888888888888888888888888888888888888888888</h1><br><h1>99999999999999999999999999999999999999999999999999</h1>"
init(page: Int) {
self.page = page
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView(frame: view.bounds)
webView.navigationDelegate = self
view.addSubview(webView)
load()
}
func load() {
webView.loadHTMLString(testString, baseURL: nil)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.scroll()
}
func scroll() {
let scrollPoint = CGPoint(x: 0, y: 100 * CGFloat(page))
webView.scrollView.setContentOffset(scrollPoint, animated: false)
}
}
I want the scroll to be instantaneous so that the user cannot tell it is scrolling, hence the animated: false
. However, this simply does not change the view in any way. If I use animated: true
it works fine, and if I wrap self.scroll()
in a DispatchQueue.main.asyncAfter(deadline: .now() + 0.1)
it works as well. But the user can see a bit of a jarring scroll if I do this. I have tried calling .setNeedsLayout()
and .layoutIfNeeded()
to no avail; the view still does not change.
So clearly there is something that is not ready to scroll the webView, even after the didFinish
delegate method is called. How can I tell when this webView
is actually ready to scroll? Or is there another reason that animated: false
is not working?
After researching the issue, I came to a similar conclusion as https://stackoverflow.com/a/76145772/8010366. It appears that there is an issue with the initial scrolling.
However, if you still need to manually scroll without the animation, you can use the following low-level code:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
let yOffset = 20 * page
webView.evaluateJavaScript("document.body.scrollTop = \(100 * yOffset)")
}
This executes a JavaScript code to scroll the HTML document. Additionally, you should add more content to enable scrolling.
References: