Here is how you can handle the sheets - the example below is fully functioning, just pass the view model to the environment before calling TabsView()
in the App
.
- Create an
Identifiable
object that will handle all the sheets in the program:
// This struct can manage all sheets
struct CustomSheet: Identifiable {
let id = UUID()
let screen: TypeOfSheet
// All sheets should fit here
@ViewBuilder
var content: some View {
switch screen {
case .type1:
SheetType1()
case .type2(let text):
SheetType2(text: text)
default:
EmptyView()
}
}
// All types of sheets should fit here
enum TypeOfSheet {
case type1
case type2(text: String)
case none
}
}
- Create one optional
@Published
var and one function in the view model; the var will tell the program what sheet is open:
// Code to be included in the view model, so it can
// handle AND track all the sheets
class MyViewModel: ObservableObject {
// This is THE variable that will tell the code whether a sheet is open
// (and also which one, if necessary)
@Published var sheetView: CustomSheet?
func showSheet(_ sheet: CustomSheet.TypeOfSheet) {
// Dismiss any sheet that is already open
sheetView = nil
switch sheet {
case .none:
break
default:
sheetView = CustomSheet(screen: sheet)
}
}
}
- Usage:
- open the sheets by calling the function
viewModel.showSheet(...)
- use
.sheet(item:)
to observe the type of sheet to open
- use
viewModel.sheet.screen
to know what sheet is open
- sheets can also be dismissed using
viewModel.showSheet(.none)
// Example: how to use the view model to present and track sheets
struct TabsView: View {
@EnvironmentObject var viewModel: MyViewModel
var body: some View {
TabView {
VStack {
Text("First tab. Sheet is \(String(describing: viewModel.sheetView?.screen ?? .none))")
.padding()
Button("Open sheet type 1") {
// Show a sheet of the first type
viewModel.showSheet(.type1)
}
}
.tabItem {Label("Tab 1", systemImage: "house")}
VStack {
Text("Second tab. Sheet is \(viewModel.sheetView == nil ? "Hidden" : "Shown")")
.padding()
Button("Open sheet type 2") {
// Show a sheet of the second type
viewModel.showSheet(.type2(text: "parameter"))
}
}
.tabItem {Label("Tab 2", systemImage: "plus")}
}
// Open a sheet - the one selected in the view model
.sheet(item: $viewModel.sheetView) { sheet in
sheet.content
.environmentObject(viewModel)
}
}
}
The following code completes the minimal reproducible example:
// Just some sample views for the sheets
struct SheetType1: View {
@EnvironmentObject var viewModel: MyViewModel
var body: some View {
Text("Takes no parameters. Sheet is \(viewModel.sheetView == nil ? "Hidden" : "Shown")")
}
}
struct SheetType2: View {
@EnvironmentObject var viewModel: MyViewModel
let text: String
var body: some View {
Text("Takes a string: \(text). Sheet is \(String(describing: viewModel.sheetView?.screen ?? .none))")
}
}
@main
struct MyApp: App {
let viewModel = MyViewModel()
var body: some Scene {
WindowGroup {
TabsView()
.environmentObject(viewModel)
}
}
}