Keep the split view mini player on pushed pages

The detail column absorbs its NavigationStack, so the mini player inset
on the stack only reached the root page and disappeared on push. Float
it over the split view, past the sidebar, and have every page reserve
its height as bottom safe area.
This commit is contained in:
Arkadiusz Fal
2026-10-13 23:58:49 +02:00
parent f289905dd8
commit 97288b4497
2 changed files with 87 additions and 18 deletions

View File

@@ -189,6 +189,7 @@ struct NavigationDestinationHandlerModifier: ViewModifier {
func body(content: Content) -> some View {
content
.opaqueWindowBackground()
.miniPlayerBottomClearance()
.navigationDestination(for: NavigationDestination.self) { destination in
Group {
if let transitionID = destination.transitionID {
@@ -199,10 +200,39 @@ struct NavigationDestinationHandlerModifier: ViewModifier {
}
}
.opaqueWindowBackground()
.miniPlayerBottomClearance()
}
}
}
extension EnvironmentValues {
/// Height of the split view's floating mini player, which every page in
/// the detail column reserves as bottom safe area. Zero elsewhere.
@Entry var miniPlayerBottomClearance: CGFloat = 0
}
/// Reserves the floating mini player's height at the bottom of a page.
private struct MiniPlayerBottomClearanceModifier: ViewModifier {
@Environment(\.miniPlayerBottomClearance) private var clearance
func body(content: Content) -> some View {
#if os(iOS)
content
.safeAreaInset(edge: .bottom, spacing: 0) {
Color.clear.frame(height: clearance)
}
#else
content
#endif
}
}
private extension View {
func miniPlayerBottomClearance() -> some View {
modifier(MiniPlayerBottomClearanceModifier())
}
}
extension View {
/// Adds navigation destination handlers for all app navigation destinations.
/// Use this on views within a NavigationStack that contain NavigationLink(value:).