Always swap out the web view to prevent white flashing between articles and make loading faster.
This commit is contained in:
parent
3a69425752
commit
8cb28b63b8
|
@ -29,7 +29,10 @@ class WebViewController: UIViewController {
|
||||||
private var topShowBarsViewConstraint: NSLayoutConstraint!
|
private var topShowBarsViewConstraint: NSLayoutConstraint!
|
||||||
private var bottomShowBarsViewConstraint: 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 lazy var contextMenuInteraction = UIContextMenuInteraction(delegate: self)
|
||||||
private var isFullScreenAvailable: Bool {
|
private var isFullScreenAvailable: Bool {
|
||||||
return traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed
|
return traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed
|
||||||
|
@ -46,7 +49,7 @@ class WebViewController: UIViewController {
|
||||||
var isShowingExtractedArticle = false {
|
var isShowingExtractedArticle = false {
|
||||||
didSet {
|
didSet {
|
||||||
if isShowingExtractedArticle != oldValue {
|
if isShowingExtractedArticle != oldValue {
|
||||||
reloadHTML()
|
loadWebView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +71,7 @@ class WebViewController: UIViewController {
|
||||||
}
|
}
|
||||||
if article != oldValue {
|
if article != oldValue {
|
||||||
windowScrollY = 0
|
windowScrollY = 0
|
||||||
reloadHTML()
|
loadWebView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,14 +80,7 @@ class WebViewController: UIViewController {
|
||||||
var windowScrollY = 0
|
var windowScrollY = 0
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
if webView != nil {
|
recycleWebView(webView)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLoad() {
|
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(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||||
|
|
||||||
coordinator.webViewProvider.dequeueWebView() { webView in
|
|
||||||
|
|
||||||
// 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
|
// Configure the tap zones
|
||||||
self.configureTopShowBarsView()
|
configureTopShowBarsView()
|
||||||
self.configureBottomShowBarsView()
|
configureBottomShowBarsView()
|
||||||
|
|
||||||
// Configure the webview
|
loadWebView()
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool) {
|
override func viewWillDisappear(_ animated: Bool) {
|
||||||
super.viewWillDisappear(animated)
|
super.viewWillDisappear(animated)
|
||||||
|
|
||||||
stopMediaPlayback()
|
stopMediaPlayback()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,14 +131,17 @@ class WebViewController: UIViewController {
|
||||||
// MARK: API
|
// MARK: API
|
||||||
|
|
||||||
func focus() {
|
func focus() {
|
||||||
webView.becomeFirstResponder()
|
webView?.becomeFirstResponder()
|
||||||
}
|
}
|
||||||
|
|
||||||
func canScrollDown() -> Bool {
|
func canScrollDown() -> Bool {
|
||||||
|
guard let webView = webView else { return false }
|
||||||
return webView.scrollView.contentOffset.y < finalScrollPosition()
|
return webView.scrollView.contentOffset.y < finalScrollPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
func scrollPageDown() {
|
func scrollPageDown() {
|
||||||
|
guard let webView = webView else { return }
|
||||||
|
|
||||||
let scrollToY: CGFloat = {
|
let scrollToY: CGFloat = {
|
||||||
let fullScroll = webView.scrollView.contentOffset.y + webView.scrollView.layoutMarginsGuide.layoutFrame.height
|
let fullScroll = webView.scrollView.contentOffset.y + webView.scrollView.layoutMarginsGuide.layoutFrame.height
|
||||||
let final = finalScrollPosition()
|
let final = finalScrollPosition()
|
||||||
|
@ -191,7 +163,7 @@ class WebViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fullReload() {
|
func fullReload() {
|
||||||
self.reloadHTML()
|
self.loadWebView()
|
||||||
}
|
}
|
||||||
|
|
||||||
func showBars() {
|
func showBars() {
|
||||||
|
@ -269,7 +241,7 @@ extension WebViewController: ArticleExtractorDelegate {
|
||||||
func articleExtractionDidFail(with: Error) {
|
func articleExtractionDidFail(with: Error) {
|
||||||
stopArticleExtractor()
|
stopArticleExtractor()
|
||||||
articleExtractorButtonState = .error
|
articleExtractorButtonState = .error
|
||||||
reloadHTML()
|
loadWebView()
|
||||||
}
|
}
|
||||||
|
|
||||||
func articleExtractionDidComplete(extractedArticle: ExtractedArticle) {
|
func articleExtractionDidComplete(extractedArticle: ExtractedArticle) {
|
||||||
|
@ -352,10 +324,6 @@ extension WebViewController: WKNavigationDelegate {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
||||||
renderPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: WKUIDelegate
|
// MARK: WKUIDelegate
|
||||||
|
@ -454,8 +422,53 @@ private struct ImageClickMessage: Codable {
|
||||||
|
|
||||||
private extension WebViewController {
|
private extension WebViewController {
|
||||||
|
|
||||||
func reloadHTML() {
|
func loadWebView() {
|
||||||
webView?.loadFileURL(ArticleRenderer.page.url, allowingReadAccessTo: ArticleRenderer.page.baseURL)
|
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() {
|
func renderPage() {
|
||||||
|
@ -497,6 +510,7 @@ private extension WebViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func finalScrollPosition() -> CGFloat {
|
func finalScrollPosition() -> CGFloat {
|
||||||
|
guard let webView = webView else { return 0 }
|
||||||
return webView.scrollView.contentSize.height - webView.scrollView.bounds.height + webView.scrollView.safeAreaInsets.bottom
|
return webView.scrollView.contentSize.height - webView.scrollView.bounds.height + webView.scrollView.safeAreaInsets.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,7 +536,8 @@ private extension WebViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageWasClicked(body: String?) {
|
func imageWasClicked(body: String?) {
|
||||||
guard let body = body,
|
guard let webView = webView,
|
||||||
|
let body = body,
|
||||||
let data = body.data(using: .utf8),
|
let data = body.data(using: .utf8),
|
||||||
let clickMessage = try? JSONDecoder().decode(ImageClickMessage.self, from: data),
|
let clickMessage = try? JSONDecoder().decode(ImageClickMessage.self, from: data),
|
||||||
let range = clickMessage.imageURL.range(of: ";base64,")
|
let range = clickMessage.imageURL.range(of: ";base64,")
|
||||||
|
|
Loading…
Reference in New Issue