The following minimal code toggles the display of details by pressing a button.
struct ContentView: View {
@State var showDetails: Bool = false
var body: some View {
VStack {
DetailsButton(showDetails: $showDetails) // 1
if showDetails {
Text("This is my message!")
}
}
}
}
struct DetailsButton: View {
@Binding var showDetails: Bool
var body: some View {
Button("\(showDetails ? "Hide" : "Show") Details") {
showDetails.toggle()
}
}
}
I would like to achieve the same thing, but moving the showDetails
property into a ViewModel which I pass as an environment variable:
(Note that I'm using the new Observation framework of Swift 5.9 and iOS 17 / macOS 14.)
@Observable class ViewModel {
var showDetails: Bool
}
Ordinarily I would initialize it in the App class:
struct TestApp: App {
@State var vm = ViewModel()
var body: some Scene {
WindowGroup {
ContentView()
.environment(vm)
}
}
}
and passing the ViewModel into ContentView as an environment variable:
@Environment(ViewModel.self) private var vm
In this scenario, how can I pass the binding for vm.showDetails
from ContentView
to DetailsButton
as I did in the original example? (See the line marked "1")
To use your vm.showDetails
in DetailsButton
try this approach,
using a @Bindable
import SwiftUI
import Observation
@main
struct TestApp: App {
@State private var vm = ViewModel()
var body: some Scene {
WindowGroup {
ContentView()
.environment(vm)
}
}
}
@Observable class ViewModel {
var showDetails: Bool = false // <--- here
}
struct ContentView: View {
@Environment(ViewModel.self) var vm
var body: some View {
@Bindable var vm = vm // <--- here
VStack {
DetailsButton(showDetails: $vm.showDetails)
if vm.showDetails { // <--- here
Text("This is my message!")
}
}
}
}
struct DetailsButton: View {
@Binding var showDetails: Bool
var body: some View {
Button("\(showDetails ? "Hide" : "Show") Details") {
showDetails.toggle()
}
}
}