From 3d0de5af0493da5a85ae2ac000c1b97fcc53e14d Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 24 May 2024 14:03:51 -0400 Subject: [PATCH] Persist more state when switching accounts Closes #486 --- Tusker/Scenes/MainSceneDelegate.swift | 5 +-- ...ountSwitchingContainerViewController.swift | 38 ++++++++++++++----- .../Utilities/UIViewController+Children.swift | 2 +- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/Tusker/Scenes/MainSceneDelegate.swift b/Tusker/Scenes/MainSceneDelegate.swift index 503a83a2..fd88a828 100644 --- a/Tusker/Scenes/MainSceneDelegate.swift +++ b/Tusker/Scenes/MainSceneDelegate.swift @@ -225,7 +225,6 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate window!.windowScene!.title = account.instanceURL.host! } - let newRoot = createAppUI() if let container = window?.rootViewController as? AccountSwitchingContainerViewController { let direction: AccountSwitchingContainerViewController.AnimationDirection if animated, @@ -235,9 +234,9 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate } else { direction = .none } - container.setRoot(newRoot, for: account, animating: direction) + container.setRoot(createAppUI, for: account, animating: direction) } else { - window!.rootViewController = AccountSwitchingContainerViewController(root: newRoot, for: account) + window!.rootViewController = AccountSwitchingContainerViewController(root: createAppUI(), for: account) } } diff --git a/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift b/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift index 6f7a5b8b..4c467981 100644 --- a/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift +++ b/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift @@ -23,7 +23,7 @@ class AccountSwitchingContainerViewController: UIViewController { private(set) var currentAccountID: String private(set) var root: AccountSwitchableViewController - private var userActivities: [String: NSUserActivity] = [:] + private var viewControllers: [String: (AccountSwitchableViewController?, NSUserActivity)] = [:] init(root: AccountSwitchableViewController, for account: UserAccountInfo) { self.currentAccountID = account.id @@ -42,27 +42,43 @@ class AccountSwitchingContainerViewController: UIViewController { embedChild(root) } - func setRoot(_ newRoot: AccountSwitchableViewController, for account: UserAccountInfo, animating direction: AnimationDirection) { + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + viewControllers = viewControllers.mapValues { (_, activity) in + (nil, activity) + } + } + + func setRoot(_ newRootProvider: () -> AccountSwitchableViewController, for account: UserAccountInfo, animating direction: AnimationDirection) { let oldRoot = self.root if direction == .none { oldRoot.removeViewAndController() } if let activity = oldRoot.stateRestorationActivity() { stateRestorationLogger.debug("AccountSwitchingContainer: saving \(activity.activityType, privacy: .public) for \(self.currentAccountID, privacy: .public)") - userActivities[currentAccountID] = activity + viewControllers[currentAccountID] = (oldRoot, activity) + } + + let newRoot: AccountSwitchableViewController + if let (existingRoot, activity) = viewControllers.removeValue(forKey: account.id) { + if let existingRoot { + newRoot = existingRoot + stateRestorationLogger.debug("AccountSwitchingContainer: reusing existing VC for \(account.id, privacy: .public)") + } else { + newRoot = newRootProvider() + stateRestorationLogger.debug("AccountSwitchingContainer: restoring \(activity.activityType, privacy: .public) for \(account.id, privacy: .public)") + let context = StateRestorationUserActivityHandlingContext(root: newRoot) + _ = activity.handleResume(manager: UserActivityManager(scene: view.window!.windowScene!, context: context)) + context.finalize(activity: activity) + } + } else { + newRoot = newRootProvider() } self.currentAccountID = account.id self.root = newRoot embedChild(newRoot) - if let activity = userActivities.removeValue(forKey: account.id) { - stateRestorationLogger.debug("AccountSwitchingContainer: restoring \(activity.activityType, privacy: .public) for \(account.id, privacy: .public)") - let context = StateRestorationUserActivityHandlingContext(root: newRoot) - _ = activity.handleResume(manager: UserActivityManager(scene: view.window!.windowScene!, context: context)) - context.finalize(activity: activity) - } - if direction != .none { if UIAccessibility.prefersCrossFadeTransitions { newRoot.view.alpha = 0 @@ -92,6 +108,7 @@ class AccountSwitchingContainerViewController: UIViewController { #endif // only one edge is affected in each direction, i have no idea why + let origAdditionalSafeAreaInsets = oldRoot.additionalSafeAreaInsets if direction == .upwards { oldRoot.additionalSafeAreaInsets.bottom = view.safeAreaInsets.bottom } else { @@ -102,6 +119,7 @@ class AccountSwitchingContainerViewController: UIViewController { oldRoot.view.transform = CGAffineTransform(translationX: 0, y: -newInitialOffset).scaledBy(x: scale, y: scale) newRoot.view.transform = .identity } completion: { (_) in + oldRoot.additionalSafeAreaInsets = origAdditionalSafeAreaInsets oldRoot.removeViewAndController() newRoot.view.layer.masksToBounds = false } diff --git a/Tusker/Screens/Utilities/UIViewController+Children.swift b/Tusker/Screens/Utilities/UIViewController+Children.swift index 6f79415f..d54f2fd1 100644 --- a/Tusker/Screens/Utilities/UIViewController+Children.swift +++ b/Tusker/Screens/Utilities/UIViewController+Children.swift @@ -68,7 +68,7 @@ extension UIView { if layout { subview.frame = bounds - + subview.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ subview.leadingAnchor.constraint(equalTo: leadingAnchor), subview.trailingAnchor.constraint(equalTo: trailingAnchor),