Tusker/Tusker/Screens/Main/AccountSwitchingContainerVi...

161 lines
5.9 KiB
Swift

//
// AccountSwitchingContainerViewController.swift
// Tusker
//
// Created by Shadowfacts on 11/11/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import UIKit
import ScreenCorners
class AccountSwitchingContainerViewController: UIViewController {
private var currentAccountID: String
private(set) var root: TuskerRootViewController
private var userActivities: [String: NSUserActivity] = [:]
init(root: TuskerRootViewController, for account: LocalData.UserAccountInfo) {
self.currentAccountID = account.id
self.root = root
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
embedChild(root)
}
func setRoot(_ newRoot: TuskerRootViewController, for account: LocalData.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
}
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
UIView.animate(withDuration: 0.4, delay: 0, options: .curveEaseInOut) {
newRoot.view.alpha = 1
oldRoot.view.alpha = 0
} completion: { (_) in
oldRoot.removeViewAndController()
}
} else {
let sign: CGFloat = direction == .downwards ? -1 : 1
let newInitialOffset = sign * view.bounds.height
let scale: CGFloat = 0.75
newRoot.view.transform = CGAffineTransform(translationX: 0, y: newInitialOffset).scaledBy(x: 0.9, y: 0.9)
newRoot.view.layer.masksToBounds = true
newRoot.view.layer.cornerCurve = .continuous
newRoot.view.layer.cornerRadius = view.window?.screen.displayCornerRadius ?? 0
oldRoot.view.layer.masksToBounds = true
oldRoot.view.layer.cornerCurve = .continuous
oldRoot.view.layer.cornerRadius = view.window?.screen.displayCornerRadius ?? 0
// only one edge is affected in each direction, i have no idea why
if direction == .upwards {
oldRoot.additionalSafeAreaInsets.bottom = view.safeAreaInsets.bottom
} else {
oldRoot.additionalSafeAreaInsets.top = view.safeAreaInsets.top
}
UIView.animate(withDuration: 0.4, delay: 0, options: .curveEaseInOut) {
oldRoot.view.transform = CGAffineTransform(translationX: 0, y: -newInitialOffset).scaledBy(x: scale, y: scale)
newRoot.view.transform = .identity
} completion: { (_) in
oldRoot.removeViewAndController()
newRoot.view.layer.masksToBounds = false
}
}
}
}
}
extension AccountSwitchingContainerViewController {
enum AnimationDirection {
case none, downwards, upwards
}
}
extension AccountSwitchingContainerViewController: TuskerRootViewController {
func stateRestorationActivity() -> NSUserActivity? {
loadViewIfNeeded()
return root.stateRestorationActivity()
}
func compose(editing draft: Draft?, animated: Bool, isDucked: Bool) {
loadViewIfNeeded()
root.compose(editing: draft, animated: animated, isDucked: isDucked)
}
func select(route: TuskerRoute, animated: Bool) {
loadViewIfNeeded()
root.select(route: route, animated: animated)
}
func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController? {
loadViewIfNeeded()
return root.getTabController(tab: tab)
}
func getNavigationDelegate() -> TuskerNavigationDelegate? {
loadViewIfNeeded()
return root.getNavigationDelegate()
}
func getNavigationController() -> NavigationControllerProtocol {
loadViewIfNeeded()
return root.getNavigationController()
}
func performSearch(query: String) {
loadViewIfNeeded()
root.performSearch(query: query)
}
func presentPreferences(completion: (() -> Void)?) {
loadViewIfNeeded()
root.presentPreferences(completion: completion)
}
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
loadViewIfNeeded()
// TODO: check if fast account switcher is being presented?
return root.handleStatusBarTapped(xPosition: xPosition)
}
}
extension AccountSwitchingContainerViewController: BackgroundableViewController {
func sceneDidEnterBackground() {
if let backgroundable = root as? BackgroundableViewController {
backgroundable.sceneDidEnterBackground()
}
}
}