Trying to build a simple MacOS app using SwiftUI. I have a View that contains a Picker bound to a State var. As a sanity check I have added a Text Views (the dwarves one and the volumes...itemName) that should also change with the Picker changes. And they do, but the View I want to rerender (the FileList) does not.
I suspect it has something to do with how I am trying to pass the new FileSystemItem (internal class) to the FileList. Like when the FilePanel rerenders the volumeSelection is back to 0 and the state is applied afterwards. So my problem is that I seem to be missing a fundamental concept on how this data is supposed to flow. I have gone through the WWDC info again and been reading other articles but I am not seeing the answer.
The desired behavior is changing the selection on the picker should cause a new FileSystemItem to be displayed in the FileList view. What is the right way to get this to happen? To state it more generically, how to you get a child view to display new data when a Picker selection changes?
struct FilePanel: View
{
@State var volumeSelection = 0
@State var pathSelection = 0
var volumes = VolumesModel() //should be passed in?
var dwarves = ["Schlumpy","Wheezy","Poxxy","Slimy","Pervy","Drooly"]
var body: some View {
VStack {
Picker(selection: $volumeSelection, label:
Text("Volume")
, content: {
ForEach (0 ..< volumes.count()) {
Text(self.volumes.volumeAtIndex(index: $0).itemName).tag($0)
}
})
FileList(item:volumes.volumeAtIndex(index: volumeSelection)).frame(minWidth: CGFloat(100.0), maxHeight: .infinity)
Text(dwarves[volumeSelection])
Text(volumes.volumeAtIndex(index: volumeSelection).itemName)
}
}
}
struct FileList: View {
@State var item : FileSystemItem
var body: some View {
VStack {
List(item.childItems){fsi in
FileCell(expanded:false, item: fsi)
}
Text(item.itemName)
}
}
}
@State
is a state private to to the owning view, FileList
will never see any change.
If VolumesModel
was a simple struct turning FileList.item
into a binding (in-out-state) may already work (the caller still needs to turn it's @State
into a binding when passing it to the dependent using the $
):
struct FileList: View {
@Binding var item : FileSystemItem
...
}
However, it feels as if VolumesModel
was a more complex object having an array of members.
If this is the case the above will not suffice:
VolumesModel
needs to be a class adopting ObservableObject
VolumesModel
's important members need a bindable wrapper (@Published
)FileList.item
should be turned into @ObservedObject
(instead of @State or @Binding)FilePanel.volumes
also to be @ObservedObject
wrappedHope that helps or at least points you into the right direction.
I figured something like that was going on with @State so thanks for the confirmation. I had been looking at ObservableObject but it seemed like it was kind of overkill for the (seemingly) simple thing I am trying to accomplish. But I guess it is something I should pursue. Thanks for the advice, I appreciate it!
You were correct that the @State in FileList was wrong. You also got me to look at ObservableObject which I think in the long run is what I need. Thanks!