Here is the idea of what I would like to do: my app's navigation contains a list, which displays a view on tap gesture which is triggered with a ViewModifier to perform an animation.
In a nutshell:
MyList > MyViewModifier > MyView
Since I want to pass some data from the list to the final view in this hierarchy, I was thinking of using @StateObject/@EnvironmentObject property wrappers. But I just realized I can't pass an .environmentObject(xx) to a ViewModifier like I would do with a View.
Here is the code I have so far:
struct TestModel: Identifiable {
var id: Int
var name: String
//@Published var whateverData I would like to fetch later and see appear in other views?
}
class TestObject: ObservableObject {
@Published var elements: [TestModel] = [
TestModel(id: 1, name: "Element 1"),
TestModel(id: 2, name: "Element 2"),
TestModel(id: 3, name: "Element 3")
]
}
struct MyList: View {
@StateObject var testObject = TestObject()
@State var presenting = false
@State var tappedId: Int?
var body: some View {
List(testObject.elements) { element in
Text(element.name)
.onTapGesture {
tappedId = element.id
presenting = true
}
}
.modifier(
MyViewModifier(presented: $presenting, tappedId: tappedId)
// .environmentObject(testObject) ?
)
}
}
struct MyViewModifier: ViewModifier {
@Binding var presented: Bool
var tappedId: Int?
@EnvironmentObject var testObject: TestObject
func body(content: Content) -> some View {
content.overlay(
ZStack {
if presented {
MyView(testObject: _testObject, tappedId: tappedId)
}
}
)
}
}
struct MyView: View {
@EnvironmentObject var testObject: TestObject
var tappedId: Int?
var body: some View {
ZStack {
Color.white
Text(testObject.elements.first(where: { $0.id == tappedId })?.name ?? "-")
}
.frame(width: 250, height: 250)
}
}
Of course this throws an error when trying to display TestView:
No ObservableObject of type TestObject found. A View.environmentObject(_:) for TestObject may be missing as an ancestor of this view.
(FYI the reason why I want to use an environment object and pass it to the modifier and then the view is that I may want to call an API in TestView when it appears on screen, which would fetch more information of this object and dispatch this information both in MyList and MyView)
What are your recommendations here? Thank you for your help.
As it was suggested in the comments, you could pass the object as a parameter to your
ViewModifier.Another option is to call
.environmentObject()after.modifier()and you should be able to read that object from your modifier.