mirror of
https://github.com/mastodon/mastodon-ios
synced 2025-04-11 22:58:02 +02:00
feat: update navigation stack when transition from .regular to .compact
This commit is contained in:
parent
c30da6533e
commit
275cce88b2
@ -2536,6 +2536,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DB852D1B26FB021500FC9D81 /* RootSplitViewController.swift */,
|
DB852D1B26FB021500FC9D81 /* RootSplitViewController.swift */,
|
||||||
|
DB852D1A26FAED0100FC9D81 /* Sidebar */,
|
||||||
|
DB8AF54E25C13703002E6C99 /* MainTab */,
|
||||||
);
|
);
|
||||||
path = Root;
|
path = Root;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2652,8 +2654,6 @@
|
|||||||
2D7631A425C1532200929FB9 /* Share */,
|
2D7631A425C1532200929FB9 /* Share */,
|
||||||
DB6180E426391A500018D199 /* Transition */,
|
DB6180E426391A500018D199 /* Transition */,
|
||||||
DB852D1D26FB021900FC9D81 /* Root */,
|
DB852D1D26FB021900FC9D81 /* Root */,
|
||||||
DB852D1A26FAED0100FC9D81 /* Sidebar */,
|
|
||||||
DB8AF54E25C13703002E6C99 /* MainTab */,
|
|
||||||
DB01409B25C40BB600F9F3CF /* Onboarding */,
|
DB01409B25C40BB600F9F3CF /* Onboarding */,
|
||||||
DB9F58ED26EF435800E7BBE9 /* Account */,
|
DB9F58ED26EF435800E7BBE9 /* Account */,
|
||||||
2D38F1D325CD463600561493 /* HomeTimeline */,
|
2D38F1D325CD463600561493 /* HomeTimeline */,
|
||||||
|
@ -18,6 +18,8 @@ final public class SceneCoordinator {
|
|||||||
|
|
||||||
let id = UUID().uuidString
|
let id = UUID().uuidString
|
||||||
|
|
||||||
|
weak var splitViewController: RootSplitViewController?
|
||||||
|
|
||||||
init(scene: UIScene, sceneDelegate: SceneDelegate, appContext: AppContext) {
|
init(scene: UIScene, sceneDelegate: SceneDelegate, appContext: AppContext) {
|
||||||
self.scene = scene
|
self.scene = scene
|
||||||
self.sceneDelegate = sceneDelegate
|
self.sceneDelegate = sceneDelegate
|
||||||
@ -119,6 +121,7 @@ extension SceneCoordinator {
|
|||||||
|
|
||||||
func setup() {
|
func setup() {
|
||||||
let splitViewController = RootSplitViewController(context: appContext, coordinator: self)
|
let splitViewController = RootSplitViewController(context: appContext, coordinator: self)
|
||||||
|
self.splitViewController = splitViewController
|
||||||
sceneDelegate.window?.rootViewController = splitViewController
|
sceneDelegate.window?.rootViewController = splitViewController
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,8 +175,14 @@ extension SceneCoordinator {
|
|||||||
|
|
||||||
switch transition {
|
switch transition {
|
||||||
case .show:
|
case .show:
|
||||||
presentingViewController.show(viewController, sender: sender)
|
if let splitViewController = splitViewController, !splitViewController.isCollapsed,
|
||||||
|
let supplementaryViewController = splitViewController.viewController(for: .supplementary) as? UINavigationController,
|
||||||
|
(supplementaryViewController === presentingViewController || supplementaryViewController.viewControllers.contains(presentingViewController))
|
||||||
|
{
|
||||||
|
fallthrough
|
||||||
|
} else {
|
||||||
|
presentingViewController.show(viewController, sender: sender)
|
||||||
|
}
|
||||||
case .showDetail:
|
case .showDetail:
|
||||||
let navigationController = AdaptiveStatusBarStyleNavigationController(rootViewController: viewController)
|
let navigationController = AdaptiveStatusBarStyleNavigationController(rootViewController: viewController)
|
||||||
presentingViewController.showDetailViewController(navigationController, sender: sender)
|
presentingViewController.showDetailViewController(navigationController, sender: sender)
|
||||||
|
@ -111,10 +111,10 @@ extension StatusProviderFacade {
|
|||||||
if provider.navigationController == nil {
|
if provider.navigationController == nil {
|
||||||
let from = provider.presentingViewController ?? provider
|
let from = provider.presentingViewController ?? provider
|
||||||
provider.dismiss(animated: true) {
|
provider.dismiss(animated: true) {
|
||||||
provider.coordinator.present(scene: .thread(viewModel: threadViewModel), from: from, transition: .showDetail)
|
provider.coordinator.present(scene: .thread(viewModel: threadViewModel), from: from, transition: .show)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
provider.coordinator.present(scene: .thread(viewModel: threadViewModel), from: provider, transition: .showDetail)
|
provider.coordinator.present(scene: .thread(viewModel: threadViewModel), from: provider, transition: .show)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class MainTabBarController: UITabBarController {
|
|||||||
|
|
||||||
let wizard = Wizard()
|
let wizard = Wizard()
|
||||||
|
|
||||||
var currentTab = Tab.home
|
var currentTab = CurrentValueSubject<Tab, Never>(.home)
|
||||||
|
|
||||||
enum Tab: Int, CaseIterable {
|
enum Tab: Int, CaseIterable {
|
||||||
case home
|
case home
|
||||||
@ -361,10 +361,10 @@ extension MainTabBarController: UITabBarControllerDelegate {
|
|||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: select %s", ((#file as NSString).lastPathComponent), #line, #function, viewController.debugDescription)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: select %s", ((#file as NSString).lastPathComponent), #line, #function, viewController.debugDescription)
|
||||||
defer {
|
defer {
|
||||||
if let tab = Tab(rawValue: tabBarController.selectedIndex) {
|
if let tab = Tab(rawValue: tabBarController.selectedIndex) {
|
||||||
currentTab = tab
|
currentTab.value = tab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
guard currentTab.rawValue == tabBarController.selectedIndex,
|
guard currentTab.value.rawValue == tabBarController.selectedIndex,
|
||||||
let navigationController = viewController as? UINavigationController,
|
let navigationController = viewController as? UINavigationController,
|
||||||
navigationController.viewControllers.count == 1,
|
navigationController.viewControllers.count == 1,
|
||||||
let scrollViewContainer = navigationController.topViewController as? ScrollViewContainer else {
|
let scrollViewContainer = navigationController.topViewController as? ScrollViewContainer else {
|
||||||
@ -535,7 +535,7 @@ extension MainTabBarController {
|
|||||||
let previousTab = Tab(rawValue: selectedIndex)
|
let previousTab = Tab(rawValue: selectedIndex)
|
||||||
selectedIndex = index
|
selectedIndex = index
|
||||||
if let tab = Tab(rawValue: index) {
|
if let tab = Tab(rawValue: index) {
|
||||||
currentTab = tab
|
currentTab.value = tab
|
||||||
}
|
}
|
||||||
|
|
||||||
if let previousTab = previousTab {
|
if let previousTab = previousTab {
|
@ -7,9 +7,12 @@
|
|||||||
|
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
|
||||||
final class RootSplitViewController: UISplitViewController, NeedsDependency {
|
final class RootSplitViewController: UISplitViewController, NeedsDependency {
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
|
||||||
@ -22,6 +25,13 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency {
|
|||||||
return sidebarViewController
|
return sidebarViewController
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var currentSupplementaryTab: MainTabBarController.Tab = .home
|
||||||
|
private(set) lazy var supplementaryViewControllers: [UIViewController] = {
|
||||||
|
return MainTabBarController.Tab.allCases.map { tab in
|
||||||
|
tab.viewController(context: context, coordinator: coordinator)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
private(set) lazy var mainTabBarController = MainTabBarController(context: context, coordinator: coordinator)
|
private(set) lazy var mainTabBarController = MainTabBarController(context: context, coordinator: coordinator)
|
||||||
|
|
||||||
init(context: AppContext, coordinator: SceneCoordinator) {
|
init(context: AppContext, coordinator: SceneCoordinator) {
|
||||||
@ -32,6 +42,7 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency {
|
|||||||
primaryBackgroundStyle = .sidebar
|
primaryBackgroundStyle = .sidebar
|
||||||
preferredDisplayMode = .oneBesideSecondary
|
preferredDisplayMode = .oneBesideSecondary
|
||||||
preferredSplitBehavior = .tile
|
preferredSplitBehavior = .tile
|
||||||
|
delegate = self
|
||||||
|
|
||||||
if #available(iOS 14.5, *) {
|
if #available(iOS 14.5, *) {
|
||||||
displayModeButtonVisibility = .always
|
displayModeButtonVisibility = .always
|
||||||
@ -40,7 +51,7 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setViewController(sidebarViewController, for: .primary)
|
setViewController(sidebarViewController, for: .primary)
|
||||||
setViewController(mainTabBarController.viewControllers!.first, for: .supplementary)
|
setViewController(supplementaryViewControllers[0], for: .supplementary)
|
||||||
setViewController(UIViewController(), for: .secondary)
|
setViewController(UIViewController(), for: .secondary)
|
||||||
setViewController(mainTabBarController, for: .compact)
|
setViewController(mainTabBarController, for: .compact)
|
||||||
}
|
}
|
||||||
@ -61,6 +72,18 @@ extension RootSplitViewController {
|
|||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
updateBehavior(size: view.frame.size)
|
updateBehavior(size: view.frame.size)
|
||||||
|
|
||||||
|
mainTabBarController.currentTab
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] tab in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard tab != self.currentSupplementaryTab else { return }
|
||||||
|
guard let index = MainTabBarController.Tab.allCases.firstIndex(of: tab) else { return }
|
||||||
|
self.currentSupplementaryTab = tab
|
||||||
|
self.setViewController(self.supplementaryViewControllers[index], for: .supplementary)
|
||||||
|
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
@ -86,16 +109,76 @@ extension RootSplitViewController {
|
|||||||
extension RootSplitViewController: SidebarViewControllerDelegate {
|
extension RootSplitViewController: SidebarViewControllerDelegate {
|
||||||
func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab) {
|
func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab) {
|
||||||
|
|
||||||
// FIXME: remove hard code
|
guard let index = MainTabBarController.Tab.allCases.firstIndex(of: tab) else {
|
||||||
switch tab {
|
assertionFailure()
|
||||||
case .home:
|
return
|
||||||
setViewController(mainTabBarController._viewControllers[0], for: .supplementary)
|
}
|
||||||
case .search:
|
currentSupplementaryTab = tab
|
||||||
setViewController(mainTabBarController._viewControllers[1], for: .supplementary)
|
setViewController(supplementaryViewControllers[index], for: .supplementary)
|
||||||
case .notification:
|
}
|
||||||
setViewController(mainTabBarController._viewControllers[2], for: .supplementary)
|
}
|
||||||
case .me:
|
|
||||||
setViewController(mainTabBarController._viewControllers[3], for: .supplementary)
|
// MARK: - UISplitViewControllerDelegate
|
||||||
|
extension RootSplitViewController: UISplitViewControllerDelegate {
|
||||||
|
|
||||||
|
// .regular to .compact
|
||||||
|
// move navigation stack from .supplementary & .secondary to .compact
|
||||||
|
func splitViewController(
|
||||||
|
_ svc: UISplitViewController,
|
||||||
|
topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column
|
||||||
|
) -> UISplitViewController.Column {
|
||||||
|
switch proposedTopColumn {
|
||||||
|
case .compact:
|
||||||
|
guard let index = MainTabBarController.Tab.allCases.firstIndex(of: currentSupplementaryTab) else {
|
||||||
|
assertionFailure()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
mainTabBarController.selectedIndex = index
|
||||||
|
mainTabBarController.currentTab.value = currentSupplementaryTab
|
||||||
|
|
||||||
|
guard let navigationController = mainTabBarController.selectedViewController as? UINavigationController else { break }
|
||||||
|
navigationController.popToRootViewController(animated: false)
|
||||||
|
var viewControllers = navigationController.viewControllers // init navigation stack with topMost
|
||||||
|
|
||||||
|
if let supplementaryNavigationController = viewController(for: .supplementary) as? UINavigationController {
|
||||||
|
// append supplementary
|
||||||
|
viewControllers.append(contentsOf: supplementaryNavigationController.popToRootViewController(animated: true) ?? [])
|
||||||
|
}
|
||||||
|
if let secondaryNavigationController = viewController(for: .secondary) as? UINavigationController {
|
||||||
|
// append secondary
|
||||||
|
viewControllers.append(contentsOf: secondaryNavigationController.popToRootViewController(animated: true) ?? [])
|
||||||
|
}
|
||||||
|
// set navigation stack
|
||||||
|
navigationController.setViewControllers(viewControllers, animated: false)
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
return proposedTopColumn
|
||||||
|
}
|
||||||
|
|
||||||
|
// .compact to .regular
|
||||||
|
// restore navigation stack to .supplementary & .secondary
|
||||||
|
func splitViewController(
|
||||||
|
_ svc: UISplitViewController,
|
||||||
|
displayModeForExpandingToProposedDisplayMode proposedDisplayMode: UISplitViewController.DisplayMode
|
||||||
|
) -> UISplitViewController.DisplayMode {
|
||||||
|
|
||||||
|
return proposedDisplayMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitViewController(
|
||||||
|
_ splitViewController: UISplitViewController,
|
||||||
|
show vc: UIViewController,
|
||||||
|
sender: Any?
|
||||||
|
) -> Bool {
|
||||||
|
if !splitViewController.isCollapsed {
|
||||||
|
// display in .secondary when expand
|
||||||
|
splitViewController.showDetailViewController(vc, sender: sender)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user