I am writing a simple application (MacOS) to demonstrate to myself how to use Observable Objects. The application will also make the use of .sheets
The Observation is used in an external model.swift
file.
@Observable
class ApplicationData {
var isShowingSheet = false
}
In the ContentView I have placed a Button, which will toggle isShowingSheet
when the button is pressed.
When the application is Run, the app does what it is meant to do, display a sheet when a button is pressed:
struct ContentView: View {
@Bindable var appData = ApplicationData()
var body: some View {
Button ("Show more information.") {
appData.isShowingSheet.toggle()
}.sheet(isPresented: $appData.isShowingSheet, onDismiss: didDismiss) {
VStack{
Text("Brewing Database")
.font(.title)
.padding(32)
Text("Showing sheet")
.padding(16)
Button("Dismiss", action: {appData.isShowingSheet.toggle()})
}
}
}
}
#Preview {
ContentView()
}
func didDismiss() {
print("User dismissed the sheet.")
}
Now I would like to place the .sheet
into an Extracted SubView like shown:
struct ContentView: View {
@Bindable var appData = ApplicationData()
var body: some View {
Button ("Show more information.") {
appData.isShowingSheet.toggle()
}.sheet(isPresented: $appData.isShowingSheet, onDismiss: didDismiss) {
ExtractedView()
}
}
}
#Preview {
ContentView()
}
func didDismiss() {
print("User dismissed the sheet.")
}
struct ExtractedView: View {
@Bindable var appData = ApplicationData()
var body: some View {
VStack{
Text("Brewing Database")
.font(.title)
.padding(32)
Text("Showing sheet")
.padding(16)
Button("Dismiss", action: {appData.isShowingSheet.toggle()})
}
}
}
It displays the sheet, but when pressing the Dismiss button, it will not dismiss.
Why is this so?
Since ContentView
owns the instance of ApplicationData
, you should use @State
, not @Bindable
.
struct ContentView: View {
@State var appData = ApplicationData()
var body: some View {
...
}
}
This instance of appData
should be passed to ExtractedView
. ExtractedView
should not create its own instance.
struct ExtractedView: View {
// this could be '@Bindable var appData: ApplicationData' if you need a Binding
// to one of the properties, but you don't in this case
let appData: ApplicationData
var body: some View {
VStack{
Text("Brewing Database")
.font(.title)
.padding(32)
Text("Showing sheet")
.padding(16)
Button("Dismiss", action: {appData.isShowingSheet.toggle()})
}
}
}
.sheet(isPresented: $appData.isShowingSheet, onDismiss: didDismiss) {
ExtractedView(appData: appData)
}
That said, you don't need the ApplicationData
instance in order to dismiss a sheet. Use the dismiss
environment value instead.
struct ExtractedView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
VStack{
Text("Brewing Database")
.font(.title)
.padding(32)
Text("Showing sheet")
.padding(16)
Button("Dismiss", action: { dismiss() })
}
}
}