Add account switching animation

This commit is contained in:
Shadowfacts 2020-11-11 15:26:25 -05:00
parent 904ff4eecf
commit 75d26e613b
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
6 changed files with 131 additions and 26 deletions

View File

@ -76,7 +76,6 @@ public class Client {
return
}
guard let result = try? Client.decoder.decode(Result.self, from: data) else {
print(request)
completion(.failure(.invalidModel))
return
}

View File

@ -297,6 +297,7 @@
D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6F2E964249E8BFD005846BB /* CrashReporterViewController.xib */; };
D6F953EC212519E700CF0F2B /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */; };
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EF21251A2900CF0F2B /* MastodonController.swift */; };
D6FF9860255C717400845181 /* AccountSwitchingContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6FF985F255C717400845181 /* AccountSwitchingContainerViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -649,6 +650,7 @@
D6F2E964249E8BFD005846BB /* CrashReporterViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CrashReporterViewController.xib; sourceTree = "<group>"; };
D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
D6F953EF21251A2900CF0F2B /* MastodonController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonController.swift; sourceTree = "<group>"; };
D6FF985F255C717400845181 /* AccountSwitchingContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSwitchingContainerViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -975,6 +977,7 @@
D641C782213DD7F0004B4513 /* Main */ = {
isa = PBXGroup;
children = (
D6FF985F255C717400845181 /* AccountSwitchingContainerViewController.swift */,
D6F0B12A24A3071C001E48C3 /* MainSplitViewController.swift */,
04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */,
D6F0B17424A3A1AA001E48C3 /* MainSidebarViewController.swift */,
@ -1971,6 +1974,7 @@
D627FF76217E923E00CC0648 /* DraftsManager.swift in Sources */,
D64F80E2215875CC00BEF393 /* XCBActionType.swift in Sources */,
04586B4322B301470021BD04 /* AppearancePrefsView.swift in Sources */,
D6FF9860255C717400845181 /* AccountSwitchingContainerViewController.swift in Sources */,
D670F8B62537DC890046588A /* EmojiPickerWrapper.swift in Sources */,
D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */,
D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */,

View File

@ -130,50 +130,64 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func showAppOrOnboardingUI(session: UISceneSession? = nil) {
let session = session ?? window!.windowScene!.session
if LocalData.shared.onboardingComplete {
let account = LocalData.shared.getMostRecentAccount()!
if session.mastodonController == nil {
let account = LocalData.shared.getMostRecentAccount()!
session.mastodonController = MastodonController.getForAccount(account)
}
showAppUI()
activateAccount(account, animated: false)
} else {
showOnboardingUI()
window!.rootViewController = createOnboardingUI()
}
}
func activateAccount(_ account: LocalData.UserAccountInfo) {
func activateAccount(_ account: LocalData.UserAccountInfo, animated: Bool) {
let oldIndex = LocalData.shared.accounts.firstIndex(where: { $0.id == LocalData.shared.mostRecentAccountID })!
let newIndex = LocalData.shared.accounts.firstIndex(of: account)!
LocalData.shared.setMostRecentAccount(account)
window!.windowScene!.session.mastodonController = MastodonController.getForAccount(account)
showAppUI()
let newRoot = createAppUI()
if let container = window?.rootViewController as? AccountSwitchingContainerViewController {
let direction: AccountSwitchingContainerViewController.AnimationDirection
if animated {
direction = newIndex > oldIndex ? .upwards : .downwards
} else {
direction = .none
}
container.setRoot(newRoot, animating: direction)
} else {
window!.rootViewController = AccountSwitchingContainerViewController(root: newRoot)
}
}
func logoutCurrent() {
LocalData.shared.removeAccount(LocalData.shared.getMostRecentAccount()!)
if LocalData.shared.onboardingComplete {
activateAccount(LocalData.shared.accounts.first!)
activateAccount(LocalData.shared.accounts.first!, animated: false)
} else {
showOnboardingUI()
window!.rootViewController = createOnboardingUI()
}
}
func showAppUI() {
func createAppUI() -> TuskerRootViewController {
let mastodonController = window!.windowScene!.session.mastodonController!
mastodonController.getOwnAccount()
mastodonController.getOwnInstance()
let rootController: UIViewController
if #available(iOS 14.0, *) {
rootController = MainSplitViewController(mastodonController: mastodonController)
if #available(iOS 14.0, *),
UIDevice.current.userInterfaceIdiom != .phone {
return MainSplitViewController(mastodonController: mastodonController)
} else {
rootController = MainTabBarViewController(mastodonController: mastodonController)
return MainTabBarViewController(mastodonController: mastodonController)
}
window!.rootViewController = rootController
}
func showOnboardingUI() {
func createOnboardingUI() -> UIViewController {
let onboarding = OnboardingViewController()
onboarding.onboardingDelegate = self
window!.rootViewController = onboarding
return onboarding
}
@objc func themePrefChanged() {
@ -184,7 +198,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
extension SceneDelegate: OnboardingViewControllerDelegate {
func didFinishOnboarding(account: LocalData.UserAccountInfo) {
activateAccount(account)
activateAccount(account, animated: false)
}
}

View File

@ -86,15 +86,16 @@ class FastAccountSwitcherViewController: UIViewController {
}
}
func hide() {
func hide(completion: (() -> Void)? = nil) {
lastSelectedAccountViewIndex = nil
selectionChangedFeedbackGenerator = nil
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut) {
UIView.animate(withDuration: 0.15, delay: 0, options: .curveEaseInOut) {
self.view.alpha = 0
} completion: { (_) in
self.view.alpha = 1
self.view.isHidden = true
completion?()
}
}
@ -128,7 +129,12 @@ class FastAccountSwitcherViewController: UIViewController {
selectionChangedFeedbackGenerator?.impactOccurred()
}
selectionChangedFeedbackGenerator = nil
(view.window!.windowScene!.delegate as! SceneDelegate).activateAccount(account)
hide() {
(self.view.window!.windowScene!.delegate as! SceneDelegate).activateAccount(account, animated: true)
}
} else {
hide()
}
}
@ -152,7 +158,6 @@ class FastAccountSwitcherViewController: UIViewController {
let location = recognizer.location(in: view)
if let index = lastSelectedAccountViewIndex {
switchAccount(newIndex: index)
hide()
} else if !(delegate?.fastAccountSwitcher(self, triggerZoneContains: location) ?? false) {
hide()
}
@ -172,7 +177,6 @@ class FastAccountSwitcherViewController: UIViewController {
case .ended:
if let index = lastSelectedAccountViewIndex {
switchAccount(newIndex: index)
hide()
}
default:
@ -207,9 +211,9 @@ class FastAccountSwitcherViewController: UIViewController {
touchBeganFeedbackWorkItem = nil
switchAccount(newIndex: tappedIndex)
} else {
hide()
}
hide()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

View File

@ -0,0 +1,84 @@
//
// AccountSwitchingContainerViewController.swift
// Tusker
//
// Created by Shadowfacts on 11/11/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import UIKit
class AccountSwitchingContainerViewController: UIViewController {
private(set) var root: TuskerRootViewController
init(root: TuskerRootViewController) {
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, animating direction: AnimationDirection) {
let oldRoot = self.root
if direction == .none {
oldRoot.removeViewAndController()
}
self.root = newRoot
embedChild(newRoot)
if direction != .none {
if UIAccessibility.prefersCrossFadeTransitionsBackwardsCompat {
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
newRoot.view.transform = CGAffineTransform(translationX: 0, y: newInitialOffset)
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseInOut) {
newRoot.view.transform = .identity
oldRoot.view.transform = CGAffineTransform(translationX: 0, y: -newInitialOffset)
} completion: { (_) in
oldRoot.removeViewAndController()
}
}
}
}
}
extension AccountSwitchingContainerViewController {
enum AnimationDirection {
case none, downwards, upwards
}
}
extension AccountSwitchingContainerViewController: TuskerRootViewController {
func presentCompose() {
root.presentCompose()
}
func select(tab: MainTabBarViewController.Tab) {
root.select(tab: tab)
}
func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController? {
root.getTabController(tab: tab)
}
}

View File

@ -69,7 +69,7 @@ class PreferencesNavigationController: UINavigationController {
let account = notification.userInfo!["account"] as! LocalData.UserAccountInfo
isSwitchingAccounts = true
dismiss(animated: true) { // dismiss preferences
sceneDelegate.activateAccount(account)
sceneDelegate.activateAccount(account, animated: true)
}
}
@ -93,7 +93,7 @@ extension PreferencesNavigationController: OnboardingViewControllerDelegate {
let sceneDelegate = self.view.window!.windowScene!.delegate as! SceneDelegate
self.dismiss(animated: true) { // dismiss instance selector
self.dismiss(animated: true) { // dismiss preferences
sceneDelegate.activateAccount(account)
sceneDelegate.activateAccount(account, animated: false)
}
}
}