diff --git a/ElementX/Sources/Application/Navigation/NavigationTabCoordinator.swift b/ElementX/Sources/Application/Navigation/NavigationTabCoordinator.swift index b286f3186..19daf9bed 100644 --- a/ElementX/Sources/Application/Navigation/NavigationTabCoordinator.swift +++ b/ElementX/Sources/Application/Navigation/NavigationTabCoordinator.swift @@ -16,6 +16,7 @@ import SwiftUI var dismissalCallback: (() -> Void)? } + @MainActor @Observable class TabDetails { /// A unique tab that identifies the tab for selection. let tag: Tag @@ -23,7 +24,11 @@ import SwiftUI let icon: KeyPath let selectedIcon: KeyPath var badgeCount = 0 - var barVisibility: Visibility = .automatic + var barVisibilityOverride: Visibility? + + /// Provide the tab's split coordinator in here to have the tab bar automatically hidden + /// when pushing a child into the split view's details on iPhone/compact iPad. + weak var navigationSplitCoordinator: NavigationSplitCoordinator? init(tag: Tag, title: String, icon: KeyPath, selectedIcon: KeyPath) { self.tag = tag @@ -31,6 +36,18 @@ import SwiftUI self.icon = icon self.selectedIcon = selectedIcon } + + func barVisibility(in horizontalSizeClass: UserInterfaceSizeClass?) -> Visibility { + if let barVisibilityOverride { + barVisibilityOverride + } else if horizontalSizeClass == .compact, navigationSplitCoordinator?.detailCoordinator != nil { + // Whilst we support pushing screens on the stack in the sidebarCoordinator, in practice + // we never do that, so simply checking that the detailCoordinator exists is enough. + .hidden + } else { + .automatic + } + } } // MARK: Tabs @@ -202,6 +219,8 @@ import SwiftUI } private struct NavigationTabCoordinatorView: View { + @Environment(\.horizontalSizeClass) private var horizontalSizeClass + @Bindable var navigationTabCoordinator: NavigationTabCoordinator @State private var standardAppearance = UITabBarAppearance() @@ -220,7 +239,7 @@ private struct NavigationTabCoordinatorView: View { } .tag(module.details.tag) .badge(module.details.badgeCount) - .toolbar(module.details.barVisibility, for: .tabBar) + .toolbar(module.details.barVisibility(in: horizontalSizeClass), for: .tabBar) } } .introspect(.tabView, on: .supportedVersions, customize: configureAppearance) diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 40cbe62d9..d81570fe7 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -106,9 +106,10 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { notificationManager: notificationManager, stateMachineFactory: stateMachineFactory) chatsTabDetails = .init(tag: HomeTab.chats, title: L10n.screenHomeTabChats, icon: \.chat, selectedIcon: \.chatSolid) + chatsTabDetails.navigationSplitCoordinator = chatsSplitCoordinator if !appSettings.spacesEnabled { - chatsTabDetails.barVisibility = .hidden + chatsTabDetails.barVisibilityOverride = .hidden } let spacesSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator()) @@ -116,6 +117,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { navigationSplitCoordinator: spacesSplitCoordinator, userIndicatorController: ServiceLocator.shared.userIndicatorController) spacesTabDetails = .init(tag: HomeTab.spaces, title: L10n.screenHomeTabSpaces, icon: \.space, selectedIcon: \.spaceSolid) + spacesTabDetails.navigationSplitCoordinator = spacesSplitCoordinator onboardingStackCoordinator = NavigationStackCoordinator() onboardingFlowCoordinator = OnboardingFlowCoordinator(userSession: userSession, @@ -272,8 +274,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { appSettings.$spacesEnabled .combineLatest(userSession.clientProxy.spaceService.joinedSpacesPublisher) - .map { $0 && !$1.isEmpty ? .automatic : .hidden } - .weakAssign(to: \.chatsTabDetails.barVisibility, on: self) + .map { $0 && !$1.isEmpty ? nil : .hidden } + .weakAssign(to: \.chatsTabDetails.barVisibilityOverride, on: self) .store(in: &cancellables) }