Always swap out the web view to prevent white flashing between articles and make loading faster.

This commit is contained in:
Maurice Parker 2020-01-31 21:07:08 -07:00
parent 3a69425752
commit 8cb28b63b8
1 changed files with 72 additions and 57 deletions

View File

@ -29,7 +29,10 @@ class WebViewController: UIViewController {
private var topShowBarsViewConstraint: NSLayoutConstraint!
private var bottomShowBarsViewConstraint: NSLayoutConstraint!
private var webView: WKWebView!
private var webView: WKWebView? {
return view.subviews[0] as? WKWebView
}
private lazy var contextMenuInteraction = UIContextMenuInteraction(delegate: self)
private var isFullScreenAvailable: Bool {
return traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed
@ -46,7 +49,7 @@ class WebViewController: UIViewController {
var isShowingExtractedArticle = false {
didSet {
if isShowingExtractedArticle != oldValue {
reloadHTML()
loadWebView()
}
}
}
@ -68,7 +71,7 @@ class WebViewController: UIViewController {
}
if article != oldValue {
windowScrollY = 0
reloadHTML()
loadWebView()
}
}
}
@ -77,14 +80,7 @@ class WebViewController: UIViewController {
var windowScrollY = 0
deinit {
if webView != nil {
webView?.evaluateJavaScript("cancelImageLoad();")
webView.configuration.userContentController.removeScriptMessageHandler(forName: MessageName.imageWasClicked)
webView.configuration.userContentController.removeScriptMessageHandler(forName: MessageName.imageWasShown)
webView.removeFromSuperview()
coordinator.webViewProvider.enqueueWebView(webView)
webView = nil
}
recycleWebView(webView)
}
override func viewDidLoad() {
@ -95,43 +91,16 @@ class WebViewController: UIViewController {
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
coordinator.webViewProvider.dequeueWebView() { webView in
// Configure the tap zones
configureTopShowBarsView()
configureBottomShowBarsView()
// Add the webview
self.webView = webView
webView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(webView)
NSLayoutConstraint.activate([
self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor),
self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor),
self.view.topAnchor.constraint(equalTo: webView.topAnchor),
self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor)
])
// Configure the tap zones
self.configureTopShowBarsView()
self.configureBottomShowBarsView()
// Configure the webview
webView.navigationDelegate = self
webView.uiDelegate = self
webView.scrollView.delegate = self
self.configureContextMenuInteraction()
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked)
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown)
self.reloadHTML()
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
loadWebView()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopMediaPlayback()
}
@ -162,14 +131,17 @@ class WebViewController: UIViewController {
// MARK: API
func focus() {
webView.becomeFirstResponder()
webView?.becomeFirstResponder()
}
func canScrollDown() -> Bool {
guard let webView = webView else { return false }
return webView.scrollView.contentOffset.y < finalScrollPosition()
}
func scrollPageDown() {
guard let webView = webView else { return }
let scrollToY: CGFloat = {
let fullScroll = webView.scrollView.contentOffset.y + webView.scrollView.layoutMarginsGuide.layoutFrame.height
let final = finalScrollPosition()
@ -191,7 +163,7 @@ class WebViewController: UIViewController {
}
func fullReload() {
self.reloadHTML()
self.loadWebView()
}
func showBars() {
@ -269,7 +241,7 @@ extension WebViewController: ArticleExtractorDelegate {
func articleExtractionDidFail(with: Error) {
stopArticleExtractor()
articleExtractorButtonState = .error
reloadHTML()
loadWebView()
}
func articleExtractionDidComplete(extractedArticle: ExtractedArticle) {
@ -352,10 +324,6 @@ extension WebViewController: WKNavigationDelegate {
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
renderPage()
}
}
// MARK: WKUIDelegate
@ -454,8 +422,53 @@ private struct ImageClickMessage: Codable {
private extension WebViewController {
func reloadHTML() {
webView?.loadFileURL(ArticleRenderer.page.url, allowingReadAccessTo: ArticleRenderer.page.baseURL)
func loadWebView() {
guard isViewLoaded else { return }
coordinator.webViewProvider.dequeueWebView() { webView in
let webViewToRecycle = self.webView
// Add the webview
webView.translatesAutoresizingMaskIntoConstraints = false
self.view.insertSubview(webView, at: 0)
NSLayoutConstraint.activate([
self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor),
self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor),
self.view.topAnchor.constraint(equalTo: webView.topAnchor),
self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor)
])
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
// Configure the webview
webView.navigationDelegate = self
webView.uiDelegate = self
webView.scrollView.delegate = self
self.configureContextMenuInteraction()
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked)
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown)
self.renderPage()
self.recycleWebView(webViewToRecycle)
}
}
func recycleWebView(_ webView: WKWebView?) {
guard let webView = webView else { return }
webView.removeFromSuperview()
webView.evaluateJavaScript("cancelImageLoad();")
webView.configuration.userContentController.removeScriptMessageHandler(forName: MessageName.imageWasClicked)
webView.configuration.userContentController.removeScriptMessageHandler(forName: MessageName.imageWasShown)
webView.interactions.removeAll()
coordinator.webViewProvider.enqueueWebView(webView)
}
func renderPage() {
@ -497,6 +510,7 @@ private extension WebViewController {
}
func finalScrollPosition() -> CGFloat {
guard let webView = webView else { return 0 }
return webView.scrollView.contentSize.height - webView.scrollView.bounds.height + webView.scrollView.safeAreaInsets.bottom
}
@ -522,7 +536,8 @@ private extension WebViewController {
}
func imageWasClicked(body: String?) {
guard let body = body,
guard let webView = webView,
let body = body,
let data = body.data(using: .utf8),
let clickMessage = try? JSONDecoder().decode(ImageClickMessage.self, from: data),
let range = clickMessage.imageURL.range(of: ";base64,")