Add account switching animation

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

View File

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

View File

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

View File

@ -130,50 +130,64 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func showAppOrOnboardingUI(session: UISceneSession? = nil) { func showAppOrOnboardingUI(session: UISceneSession? = nil) {
let session = session ?? window!.windowScene!.session let session = session ?? window!.windowScene!.session
if LocalData.shared.onboardingComplete { if LocalData.shared.onboardingComplete {
if session.mastodonController == nil {
let account = LocalData.shared.getMostRecentAccount()! let account = LocalData.shared.getMostRecentAccount()!
if session.mastodonController == nil {
session.mastodonController = MastodonController.getForAccount(account) session.mastodonController = MastodonController.getForAccount(account)
} }
showAppUI() activateAccount(account, animated: false)
} else { } 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) LocalData.shared.setMostRecentAccount(account)
window!.windowScene!.session.mastodonController = MastodonController.getForAccount(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() { func logoutCurrent() {
LocalData.shared.removeAccount(LocalData.shared.getMostRecentAccount()!) LocalData.shared.removeAccount(LocalData.shared.getMostRecentAccount()!)
if LocalData.shared.onboardingComplete { if LocalData.shared.onboardingComplete {
activateAccount(LocalData.shared.accounts.first!) activateAccount(LocalData.shared.accounts.first!, animated: false)
} else { } else {
showOnboardingUI() window!.rootViewController = createOnboardingUI()
} }
} }
func showAppUI() { func createAppUI() -> TuskerRootViewController {
let mastodonController = window!.windowScene!.session.mastodonController! let mastodonController = window!.windowScene!.session.mastodonController!
mastodonController.getOwnAccount() mastodonController.getOwnAccount()
mastodonController.getOwnInstance() mastodonController.getOwnInstance()
let rootController: UIViewController if #available(iOS 14.0, *),
if #available(iOS 14.0, *) { UIDevice.current.userInterfaceIdiom != .phone {
rootController = MainSplitViewController(mastodonController: mastodonController) return MainSplitViewController(mastodonController: mastodonController)
} else { } else {
rootController = MainTabBarViewController(mastodonController: mastodonController) return MainTabBarViewController(mastodonController: mastodonController)
} }
window!.rootViewController = rootController
} }
func showOnboardingUI() { func createOnboardingUI() -> UIViewController {
let onboarding = OnboardingViewController() let onboarding = OnboardingViewController()
onboarding.onboardingDelegate = self onboarding.onboardingDelegate = self
window!.rootViewController = onboarding return onboarding
} }
@objc func themePrefChanged() { @objc func themePrefChanged() {
@ -184,7 +198,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
extension SceneDelegate: OnboardingViewControllerDelegate { extension SceneDelegate: OnboardingViewControllerDelegate {
func didFinishOnboarding(account: LocalData.UserAccountInfo) { 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 lastSelectedAccountViewIndex = nil
selectionChangedFeedbackGenerator = 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 self.view.alpha = 0
} completion: { (_) in } completion: { (_) in
self.view.alpha = 1 self.view.alpha = 1
self.view.isHidden = true self.view.isHidden = true
completion?()
} }
} }
@ -128,7 +129,12 @@ class FastAccountSwitcherViewController: UIViewController {
selectionChangedFeedbackGenerator?.impactOccurred() selectionChangedFeedbackGenerator?.impactOccurred()
} }
selectionChangedFeedbackGenerator = nil 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) let location = recognizer.location(in: view)
if let index = lastSelectedAccountViewIndex { if let index = lastSelectedAccountViewIndex {
switchAccount(newIndex: index) switchAccount(newIndex: index)
hide()
} else if !(delegate?.fastAccountSwitcher(self, triggerZoneContains: location) ?? false) { } else if !(delegate?.fastAccountSwitcher(self, triggerZoneContains: location) ?? false) {
hide() hide()
} }
@ -172,7 +177,6 @@ class FastAccountSwitcherViewController: UIViewController {
case .ended: case .ended:
if let index = lastSelectedAccountViewIndex { if let index = lastSelectedAccountViewIndex {
switchAccount(newIndex: index) switchAccount(newIndex: index)
hide()
} }
default: default:
@ -207,10 +211,10 @@ class FastAccountSwitcherViewController: UIViewController {
touchBeganFeedbackWorkItem = nil touchBeganFeedbackWorkItem = nil
switchAccount(newIndex: tappedIndex) switchAccount(newIndex: tappedIndex)
} } else {
hide() hide()
} }
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if touches.count == 1, if touches.count == 1,

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 let account = notification.userInfo!["account"] as! LocalData.UserAccountInfo
isSwitchingAccounts = true isSwitchingAccounts = true
dismiss(animated: true) { // dismiss preferences 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 let sceneDelegate = self.view.window!.windowScene!.delegate as! SceneDelegate
self.dismiss(animated: true) { // dismiss instance selector self.dismiss(animated: true) { // dismiss instance selector
self.dismiss(animated: true) { // dismiss preferences self.dismiss(animated: true) { // dismiss preferences
sceneDelegate.activateAccount(account) sceneDelegate.activateAccount(account, animated: false)
} }
} }
} }