3

I'm using SwiftUI's TabView and I want to add a custom bottomSheet() modifier which accepts a view and displays it like the standard sheet() modifier, but without occupying the entire screen.

Current Behaviour: I managed to create the custom modifier and show a sheet, but the sheet comes up behind the bottom tab bar (since it is displayed from within the NavigationView).

Expected Behavior: I'm looking for a way to cover the tab bar with the sheet in front.

enter image description here

Minimal Reproducible Example

Here's the custom modifier that I've created.

struct BottomSheet<SheetContent: View>: ViewModifier {
    
    let sheetContent: SheetContent
    
    @Binding var isPresented: Bool
    
    init(isPresented: Binding<Bool>, @ViewBuilder content: () -> SheetContent) {
        self.sheetContent = content()
        _isPresented = isPresented
    }
    
    func body(content: Content) -> some View {
        ZStack {
            content
            if isPresented {
                ZStack {
                    Color.black.opacity(0.1)
                    VStack {
                        Spacer()
                        sheetContent
                            .padding()
                            .frame(maxWidth: .infinity)
                            .background(
                                Rectangle()
                                    .foregroundColor(.white)
                            )
                    }
                }
            }
        }
    }
}

extension View {
    func bottomSheet<SheetContent: View>(isPresented: Binding<Bool>, @ViewBuilder content: @escaping () -> SheetContent) -> some View {
        self.modifier(BottomSheet(isPresented: isPresented, content: content))
    }
}

Here's how I'm using it.

struct ScheduleTab: View {
    
    @State private var showSheet = false
    
    var body: some View {
        NavigationView {
            Button("Open Sheet") {
                showSheet.toggle()
            }
        }
        .navigationTitle("Today")
        .navigationBarTitleDisplayMode(.inline)
        .bottomSheet(isPresented: $showSheet) {
            Text("Hello, World")
        }
    }
}
Siddharth Kamaria
  • 2,448
  • 2
  • 17
  • 37
  • It looks you applied `bottomSheet` modifier to internal tab view, but should apply it to `TabView` itself to have it above. – Asperi Jun 28 '21 at 14:38
  • @Asperi Yes that I'm aware of, but I was looking for a solution which can do it from within the tab. As a last resort, I will put it on the `TabView` and then have an enum to show different sheets for different tabs. – Siddharth Kamaria Jun 28 '21 at 15:03
  • Does it have to be that small? With the new `adaptiveSheetController` you can [easily achieve a half modal](https://stackoverflow.com/questions/56700752/swiftui-half-modal/67994666#67994666) that show above it all. But it is only available iOS and macCatalyst 15+. You might be able to create it with your [custom size with a `UIViewController`](https://stackoverflow.com/questions/54737884/changing-the-size-of-a-modal-view-controller) – lorem ipsum Jun 29 '21 at 14:58
  • @loremipsum That is a good solution, but as of now I'm targetting iOS 14+ – Siddharth Kamaria Jun 29 '21 at 15:07
  • Have you found a solution yet? I have the same problem and couldn't solve it yet – aliyasineser Nov 09 '21 at 15:23
  • @aliyasineser Not yet, but I ended up putting the `bottomSheet` modifier on the `TabView` block (i.e. parent view) and passing an enum `Binding` down to individual tabs. So, whenever the binding enum changes, I switch to a different bottom sheet in the parent view. Works well for now, but not the most elegant approach :) – Siddharth Kamaria Nov 09 '21 at 15:29
  • 1
    Thank you for the answer, until someone came up with a cleaner solution I would like to use yours. It was a valuable answer, appreciated. – aliyasineser Nov 12 '21 at 20:16

0 Answers0