Change to stop recycling the article view controller when switching panel modes on the iPad. Issue #1570

This commit is contained in:
Maurice Parker 2020-01-21 11:05:47 -07:00
parent ed355ad614
commit deab8f8c6c
4 changed files with 47 additions and 28 deletions

View File

@ -53,6 +53,7 @@ class ArticleViewController: UIViewController {
updateUI() updateUI()
} }
} }
var restoreWindowScrollY = 0
private let keyboardManager = KeyboardManager(type: .detail) private let keyboardManager = KeyboardManager(type: .detail)
override var keyCommands: [UIKeyCommand]? { override var keyCommands: [UIKeyCommand]? {
@ -92,6 +93,7 @@ class ArticleViewController: UIViewController {
]) ])
let controller = createWebViewController(article) let controller = createWebViewController(article)
controller.restoreWindowScrollY = restoreWindowScrollY
articleExtractorButton.buttonState = controller.articleExtractorButtonState articleExtractorButton.buttonState = controller.articleExtractorButtonState
pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil) pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)
@ -246,6 +248,13 @@ class ArticleViewController: UIViewController {
// MARK: WebViewControllerDelegate // MARK: WebViewControllerDelegate
extension ArticleViewController: WebViewControllerDelegate { extension ArticleViewController: WebViewControllerDelegate {
func webViewController(_ webViewController: WebViewController, restoreWindowScrollYDidUpdate restoreWindowScrollY: Int) {
if webViewController === currentWebViewController {
self.restoreWindowScrollY = restoreWindowScrollY
}
}
func webViewController(_ webViewController: WebViewController, articleExtractorButtonStateDidUpdate buttonState: ArticleExtractorButtonState) { func webViewController(_ webViewController: WebViewController, articleExtractorButtonStateDidUpdate buttonState: ArticleExtractorButtonState) {
if webViewController === currentWebViewController { if webViewController === currentWebViewController {
articleExtractorButton.buttonState = buttonState articleExtractorButton.buttonState = buttonState

View File

@ -8,11 +8,13 @@
import UIKit import UIKit
import WebKit import WebKit
import RSCore
import Account import Account
import Articles import Articles
import SafariServices import SafariServices
protocol WebViewControllerDelegate: class { protocol WebViewControllerDelegate: class {
func webViewController(_: WebViewController, restoreWindowScrollYDidUpdate: Int)
func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState) func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState)
} }
@ -68,7 +70,12 @@ class WebViewController: UIViewController {
} }
} }
var restoreOffset = 0 let scrollPositionQueue = CoalescingQueue(name: "Article Scroll Position", interval: 0.3, maxInterval: 1.0)
var restoreWindowScrollY = 0 {
didSet {
delegate?.webViewController(self, restoreWindowScrollYDidUpdate: restoreWindowScrollY)
}
}
deinit { deinit {
if webView != nil { if webView != nil {
@ -109,6 +116,7 @@ class WebViewController: UIViewController {
// Configure the webview // Configure the webview
webView.navigationDelegate = self webView.navigationDelegate = self
webView.uiDelegate = self webView.uiDelegate = self
webView.scrollView.delegate = self
self.configureContextMenuInteraction() self.configureContextMenuInteraction()
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked) webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked)
@ -186,10 +194,7 @@ class WebViewController: UIViewController {
} }
func fullReload() { func fullReload() {
webView?.evaluateJavaScript("window.scrollY") { (scrollY, _) in self.reloadHTML()
self.restoreOffset = scrollY as! Int
self.reloadHTML()
}
} }
func showBars() { func showBars() {
@ -408,6 +413,22 @@ extension WebViewController: UIViewControllerTransitioningDelegate {
} }
} }
// MARK:
extension WebViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollPositionQueue.add(self, #selector(scrollPositionDidChange))
}
@objc func scrollPositionDidChange() {
webView?.evaluateJavaScript("window.scrollY") { (scrollY, _) in
self.restoreWindowScrollY = scrollY as! Int
}
}
}
// MARK: JSON // MARK: JSON
private struct TemplateData: Codable { private struct TemplateData: Codable {
@ -472,10 +493,10 @@ private extension WebViewController {
var render = "error();" var render = "error();"
if let data = try? encoder.encode(templateData) { if let data = try? encoder.encode(templateData) {
let json = String(data: data, encoding: .utf8)! let json = String(data: data, encoding: .utf8)!
render = "render(\(json), \(restoreOffset));" render = "render(\(json), \(restoreWindowScrollY));"
} }
restoreOffset = 0 restoreWindowScrollY = 0
WebViewProvider.shared.articleIconSchemeHandler.currentArticle = article WebViewProvider.shared.articleIconSchemeHandler.currentArticle = article
webView.scrollView.setZoomScale(1.0, animated: false) webView.scrollView.setZoomScale(1.0, animated: false)

View File

@ -28,7 +28,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
weak var coordinator: SceneCoordinator! weak var coordinator: SceneCoordinator!
var undoableCommands = [UndoableCommand]() var undoableCommands = [UndoableCommand]()
let scrollPositionQueue = CoalescingQueue(name: "Scroll Position", interval: 0.3, maxInterval: 1.0) let scrollPositionQueue = CoalescingQueue(name: "Timeline Scroll Position", interval: 0.3, maxInterval: 1.0)
private let keyboardManager = KeyboardManager(type: .timeline) private let keyboardManager = KeyboardManager(type: .timeline)
override var keyCommands: [UIKeyCommand]? { override var keyCommands: [UIKeyCommand]? {

View File

@ -1677,19 +1677,14 @@ private extension SceneCoordinator {
} }
@discardableResult @discardableResult
func installArticleController(_ recycledArticleController: ArticleViewController? = nil, animated: Bool) -> ArticleViewController { func installArticleController(restoreWindowScrollY: Int = 0, animated: Bool) -> ArticleViewController {
isArticleViewControllerPending = true isArticleViewControllerPending = true
let articleController: ArticleViewController = { let articleController = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self)
if let controller = recycledArticleController { articleController.coordinator = self
return controller articleController.article = currentArticle
} else { articleController.restoreWindowScrollY = restoreWindowScrollY
let controller = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self)
controller.coordinator = self
return controller
}
}()
if let subSplit = subSplitViewController { if let subSplit = subSplitViewController {
let controller = addNavControllerIfNecessary(articleController, showButton: false) let controller = addNavControllerIfNecessary(articleController, showButton: false)
@ -1701,12 +1696,6 @@ private extension SceneCoordinator {
rootSplitViewController.showDetailViewController(controller, sender: self) rootSplitViewController.showDetailViewController(controller, sender: self)
} }
// We have to do a full reload when installing an article controller. We may have changed color contexts
// and need to update the article colors. An example is in dark mode. Split screen doesn't use true black
// like darkmode usually does.
// TODO: This should probably only happen to recycled article controllers
articleController.fullReload()
return articleController return articleController
} }
@ -1758,7 +1747,7 @@ private extension SceneCoordinator {
} }
func configureThreePanelMode() { func configureThreePanelMode() {
let recycledArticleController = articleViewController let articleRestoreWindowScrollY = articleViewController?.restoreWindowScrollY ?? 0
defer { defer {
masterNavigationController.viewControllers = [masterFeedViewController] masterNavigationController.viewControllers = [masterFeedViewController]
} }
@ -1773,14 +1762,14 @@ private extension SceneCoordinator {
masterTimelineViewController?.navigationItem.leftBarButtonItem = rootSplitViewController.displayModeButtonItem masterTimelineViewController?.navigationItem.leftBarButtonItem = rootSplitViewController.displayModeButtonItem
masterTimelineViewController?.navigationItem.leftItemsSupplementBackButton = true masterTimelineViewController?.navigationItem.leftItemsSupplementBackButton = true
installArticleController(recycledArticleController, animated: false) installArticleController(restoreWindowScrollY: articleRestoreWindowScrollY, animated: false)
masterFeedViewController.restoreSelectionIfNecessary(adjustScroll: true) masterFeedViewController.restoreSelectionIfNecessary(adjustScroll: true)
masterTimelineViewController!.restoreSelectionIfNecessary(adjustScroll: false) masterTimelineViewController!.restoreSelectionIfNecessary(adjustScroll: false)
} }
func configureStandardPanelMode() { func configureStandardPanelMode() {
let recycledArticleController = articleViewController let articleRestoreWindowScrollY = articleViewController?.restoreWindowScrollY ?? 0
rootSplitViewController.preferredPrimaryColumnWidthFraction = UISplitViewController.automaticDimension rootSplitViewController.preferredPrimaryColumnWidthFraction = UISplitViewController.automaticDimension
// Set the is Pending flags early to prevent the navigation controller delegate from thinking that we // Set the is Pending flags early to prevent the navigation controller delegate from thinking that we
@ -1800,7 +1789,7 @@ private extension SceneCoordinator {
masterNavigationController.pushViewController(masterTimelineViewController!, animated: false) masterNavigationController.pushViewController(masterTimelineViewController!, animated: false)
} }
installArticleController(recycledArticleController, animated: false) installArticleController(restoreWindowScrollY: articleRestoreWindowScrollY, animated: false)
} }
// MARK: NSUserActivity // MARK: NSUserActivity