Store more article view controller state when destroying and recreating them. Issue #1729

This commit is contained in:
Maurice Parker 2020-01-29 11:30:52 -07:00
parent e9989d6f24
commit f96bb9c3e0
3 changed files with 50 additions and 27 deletions

View File

@ -14,6 +14,11 @@ import SafariServices
class ArticleViewController: UIViewController { class ArticleViewController: UIViewController {
typealias State = (extractedArticle: ExtractedArticle?,
isShowingExtractedArticle: Bool,
articleExtractorButtonState: ArticleExtractorButtonState,
windowScrollY: Int)
@IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem! @IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var prevArticleBarButtonItem: UIBarButtonItem! @IBOutlet private weak var prevArticleBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var nextArticleBarButtonItem: UIBarButtonItem! @IBOutlet private weak var nextArticleBarButtonItem: UIBarButtonItem!
@ -49,7 +54,16 @@ class ArticleViewController: UIViewController {
updateUI() updateUI()
} }
} }
var restoreWindowScrollY = 0
var currentState: State? {
guard let controller = currentWebViewController else { return nil}
return State(extractedArticle: controller.extractedArticle,
isShowingExtractedArticle: controller.isShowingExtractedArticle,
articleExtractorButtonState: controller.articleExtractorButtonState,
windowScrollY: controller.windowScrollY)
}
var restoreState: State?
private let keyboardManager = KeyboardManager(type: .detail) private let keyboardManager = KeyboardManager(type: .detail)
override var keyCommands: [UIKeyCommand]? { override var keyCommands: [UIKeyCommand]? {
@ -89,7 +103,12 @@ class ArticleViewController: UIViewController {
]) ])
let controller = createWebViewController(article) let controller = createWebViewController(article)
controller.restoreWindowScrollY = restoreWindowScrollY if let state = restoreState {
controller.extractedArticle = state.extractedArticle
controller.isShowingExtractedArticle = state.isShowingExtractedArticle
controller.articleExtractorButtonState = state.articleExtractorButtonState
controller.windowScrollY = state.windowScrollY
}
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)
@ -239,18 +258,16 @@ class ArticleViewController: UIViewController {
currentWebViewController?.fullReload() currentWebViewController?.fullReload()
} }
func stopArticleExtractorIfProcessing() {
currentWebViewController?.stopArticleExtractorIfProcessing()
}
} }
// 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

@ -14,7 +14,6 @@ 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)
} }
@ -39,8 +38,12 @@ class WebViewController: UIViewController {
private var clickedImageCompletion: (() -> Void)? private var clickedImageCompletion: (() -> Void)?
private var articleExtractor: ArticleExtractor? = nil private var articleExtractor: ArticleExtractor? = nil
private var extractedArticle: ExtractedArticle? var extractedArticle: ExtractedArticle? {
private var isShowingExtractedArticle = false { didSet {
windowScrollY = 0
}
}
var isShowingExtractedArticle = false {
didSet { didSet {
if isShowingExtractedArticle != oldValue { if isShowingExtractedArticle != oldValue {
reloadHTML() reloadHTML()
@ -64,18 +67,14 @@ class WebViewController: UIViewController {
startArticleExtractor() startArticleExtractor()
} }
if article != oldValue { if article != oldValue {
restoreWindowScrollY = 0 windowScrollY = 0
reloadHTML() reloadHTML()
} }
} }
} }
let scrollPositionQueue = CoalescingQueue(name: "Article Scroll Position", interval: 0.3, maxInterval: 1.0) let scrollPositionQueue = CoalescingQueue(name: "Article Scroll Position", interval: 0.3, maxInterval: 1.0)
var restoreWindowScrollY = 0 { var windowScrollY = 0
didSet {
delegate?.webViewController(self, restoreWindowScrollYDidUpdate: restoreWindowScrollY)
}
}
deinit { deinit {
if webView != nil { if webView != nil {
@ -247,6 +246,12 @@ class WebViewController: UIViewController {
} }
func stopArticleExtractorIfProcessing() {
if articleExtractor?.state == .processing {
stopArticleExtractor()
}
}
func showActivityDialog(popOverBarButtonItem: UIBarButtonItem? = nil) { func showActivityDialog(popOverBarButtonItem: UIBarButtonItem? = nil) {
guard let preferredLink = article?.preferredLink, let url = URL(string: preferredLink) else { guard let preferredLink = article?.preferredLink, let url = URL(string: preferredLink) else {
return return
@ -423,7 +428,7 @@ extension WebViewController: UIScrollViewDelegate {
@objc func scrollPositionDidChange() { @objc func scrollPositionDidChange() {
webView?.evaluateJavaScript("window.scrollY") { (scrollY, _) in webView?.evaluateJavaScript("window.scrollY") { (scrollY, _) in
self.restoreWindowScrollY = scrollY as? Int ?? 0 self.windowScrollY = scrollY as? Int ?? 0
} }
} }
@ -481,10 +486,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), \(restoreWindowScrollY));" render = "render(\(json), \(windowScrollY));"
} }
restoreWindowScrollY = 0 windowScrollY = 0
webView.scrollView.setZoomScale(1.0, animated: false) webView.scrollView.setZoomScale(1.0, animated: false)
webView.evaluateJavaScript(render) webView.evaluateJavaScript(render)

View File

@ -1778,14 +1778,14 @@ private extension SceneCoordinator {
} }
@discardableResult @discardableResult
func installArticleController(restoreWindowScrollY: Int = 0, animated: Bool) -> ArticleViewController { func installArticleController(state: ArticleViewController.State? = nil, animated: Bool) -> ArticleViewController {
isArticleViewControllerPending = true isArticleViewControllerPending = true
let articleController = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self) let articleController = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self)
articleController.coordinator = self articleController.coordinator = self
articleController.article = currentArticle articleController.article = currentArticle
articleController.restoreWindowScrollY = restoreWindowScrollY articleController.restoreState = state
if let subSplit = subSplitViewController { if let subSplit = subSplitViewController {
let controller = addNavControllerIfNecessary(articleController, showButton: false) let controller = addNavControllerIfNecessary(articleController, showButton: false)
@ -1848,12 +1848,12 @@ private extension SceneCoordinator {
} }
func configureThreePanelMode() { func configureThreePanelMode() {
let articleRestoreWindowScrollY = articleViewController?.restoreWindowScrollY ?? 0 articleViewController?.stopArticleExtractorIfProcessing()
let articleViewControllerState = articleViewController?.currentState
defer { defer {
masterNavigationController.viewControllers = [masterFeedViewController] masterNavigationController.viewControllers = [masterFeedViewController]
} }
if rootSplitViewController.viewControllers.last is InteractiveNavigationController { if rootSplitViewController.viewControllers.last is InteractiveNavigationController {
_ = rootSplitViewController.viewControllers.popLast() _ = rootSplitViewController.viewControllers.popLast()
} }
@ -1863,14 +1863,15 @@ private extension SceneCoordinator {
masterTimelineViewController?.navigationItem.leftBarButtonItem = rootSplitViewController.displayModeButtonItem masterTimelineViewController?.navigationItem.leftBarButtonItem = rootSplitViewController.displayModeButtonItem
masterTimelineViewController?.navigationItem.leftItemsSupplementBackButton = true masterTimelineViewController?.navigationItem.leftItemsSupplementBackButton = true
installArticleController(restoreWindowScrollY: articleRestoreWindowScrollY, animated: false) installArticleController(state: articleViewControllerState, animated: false)
masterFeedViewController.restoreSelectionIfNecessary(adjustScroll: true) masterFeedViewController.restoreSelectionIfNecessary(adjustScroll: true)
masterTimelineViewController!.restoreSelectionIfNecessary(adjustScroll: false) masterTimelineViewController!.restoreSelectionIfNecessary(adjustScroll: false)
} }
func configureStandardPanelMode() { func configureStandardPanelMode() {
let articleRestoreWindowScrollY = articleViewController?.restoreWindowScrollY ?? 0 articleViewController?.stopArticleExtractorIfProcessing()
let articleViewControllerState = articleViewController?.currentState
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
@ -1890,7 +1891,7 @@ private extension SceneCoordinator {
masterNavigationController.pushViewController(masterTimelineViewController!, animated: false) masterNavigationController.pushViewController(masterTimelineViewController!, animated: false)
} }
installArticleController(restoreWindowScrollY: articleRestoreWindowScrollY, animated: false) installArticleController(state: articleViewControllerState, animated: false)
} }
// MARK: NSUserActivity // MARK: NSUserActivity