How would you make the .sheet presented above the tab bar wihtout covering it in SwiftUi? I've seen a somewhat solution but not one in SwiftUi.
So far anything I try just places the sheet overlaying the tab bar
import SwiftUI
struct ContentView: View {
@State private var showSheet = true
var body: some View {
TabView {
HomeView(showSheet: $showSheet)
.tabItem {
Label("Home", systemImage: "house")
}
Text("Second Tab")
.tabItem {
Label("Second", systemImage: "2.circle")
}
}
}
}
struct HomeView: View {
@Binding var showSheet: Bool
var body: some View {
VStack {
Button("Show TabView Sheet") {
showSheet.toggle()
}
.sheet(isPresented: $showSheet) {
SheetContent()
.presentationDetents([.medium, .large])
.interactiveDismissDisabled()
.presentationBackgroundInteraction(.enabled(upThrough: .large))
}
}
}
}
struct SheetContent: View {
var body: some View {
VStack {
Text("First Tab Content")
Text("More Content in the Sheet")
// Add more content here as needed
}
}
}
#Preview {
ContentView()
}
In your example, the sheet cannot be dismissed interactively, but background interaction has been enabled. So I would suggest, an overlay could be used to replace the sheet:
// ContentView
HomeView(showSheet: $showSheet)
.tabItem {
Label("Home", systemImage: "house")
}
.toolbarBackground(.visible, for: .tabBar)
.toolbarBackground(.background, for: .tabBar)
struct HomeView: View {
@Binding var showSheet: Bool
var body: some View {
VStack {
Button("Show TabView Sheet") {
showSheet.toggle()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.overlay(alignment: .bottom) {
if showSheet {
SheetContent()
.transition(.move(edge: .bottom))
}
}
.animation(.easeInOut, value: showSheet)
}
}
struct SheetContent: View {
var body: some View {
VStack {
Text("First Tab Content")
Text("More Content in the Sheet")
// Add more content here as needed
}
.frame(maxWidth: .infinity, minHeight: 200)
.background {
UnevenRoundedRectangle(cornerRadii: .init(topLeading: 20, topTrailing: 20))
.fill(.yellow)
}
}
}
EDIT You were asking in a comment if it would be possible to resize the overlay. You would need to implement this yourself using a drag gesture. Perhaps something like this:
struct SheetContent: View {
let minHeight: CGFloat = 200
let maxHeight: CGFloat = 450
@State private var extraHeight = CGFloat.zero
@State private var dragHeight = CGFloat.zero
var body: some View {
VStack {
Text("First Tab Content")
Text("More Content in the Sheet")
// Add more content here as needed
}
.frame(maxWidth: .infinity, maxHeight: minHeight + extraHeight)
.offset(y: -dragHeight / 2)
.background {
UnevenRoundedRectangle(cornerRadii: .init(topLeading: 20, topTrailing: 20))
.padding(.bottom, -300)
.foregroundStyle(.yellow)
.offset(y: -dragHeight)
}
.overlay(alignment: .top) {
Capsule()
.frame(width: 36, height: 5)
.foregroundStyle(.secondary)
.padding(5)
.offset(y: -dragHeight)
.gesture(
DragGesture()
.onChanged { val in
let dy = -val.translation.height
let minDragHeight = minHeight - (minHeight + extraHeight)
let maxDragHeight = maxHeight - (minHeight + extraHeight)
dragHeight = min(max(dy, minDragHeight), maxDragHeight)
}
.onEnded { val in
extraHeight = extraHeight + dragHeight
dragHeight = 0
}
)
}
}
}