Reputation: 16141
I have a view(MainView
) with another view(FooGroupView
) inside. Each time I click the item in FooGroupView
then pass the value changes to MainView
.
Like below:
The white view is MainView
, the red is FooGroupView
. If click foo1
then the text should Current:foo1
, then if click foo2
then Current:foo2
.
My code is here:
Test.Playground
import SwiftUI
import PlaygroundSupport
struct FooRowView: View {
var foo: Foo
var body: some View {
VStack {
Color(.red)
Text(foo.name)
}
}
}
class Foo: Identifiable, ObservableObject {
var name: String
var id: String
init(name: String, id: String) {
self.name = name
self.id = id
}
}
final class FooGroupViewModel: ObservableObject {
var foos: [Foo] {
didSet {
self.selectedFoo = foos.first
}
}
@Published var selectedFoo: Foo? {
didSet {
print("You selected: \(selectedFoo?.name)")
}
}
init(foos: [Foo] = []) {
self.foos = foos
self.selectedFoo = foos.first
}
}
struct FooGroupView: View {
var viewModel: FooGroupViewModel
var body: some View {
ScrollView(.horizontal) {
HStack(alignment: .center, spacing: 32, content: {
// Error: error: Execution was interrupted, reason: signal SIGABRT.
//The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
/*ForEach(viewModel.foos) { foo in
Text(foo.name)
}*/
Text(viewModel.foos[0].name).onTapGesture {
viewModel.selectedFoo = viewModel.foos[0]
}
Text(viewModel.foos[1].name).onTapGesture {
viewModel.selectedFoo = viewModel.foos[1]
}
})
}
}
}
final class MainViewModel: ObservableObject {
@ObservedObject var fooGroupViewModel: FooGroupViewModel
@Published var currentFoo: Foo?
init() {
fooGroupViewModel = FooGroupViewModel(foos: [Foo(name: "foo1", id: "1"), Foo(name: "foo2", id: "2")])
currentFoo = self.fooGroupViewModel.selectedFoo
}
}
struct MainView: View {
@ObservedObject var viewModel = MainViewModel()
var body: some View {
VStack {
FooGroupView(viewModel: viewModel.fooGroupViewModel)
.background(Color.red)
Spacer()
Text("Current:\(viewModel.currentFoo!.name)")
}.frame(width: 200, height: 200)
}
}
PlaygroundPage.current.liveView = UIHostingController(rootView: MainView())
In MainViewModel
if currentFoo
has changes, then the UI should be updated.
When click foo1
or foo2
, the selectedFoo
in FooGroupViewModel
was updated, then in MainViewModel
should get the changes (as @ObservedObject var fooGroupViewModel: FooGroupViewModel
)
My question is how to let currentFoo
knows about the changes of selectedFoo
in fooGroupViewModel
? I was thought this line could observe the changes of the selectedFoo
and if any update then trigger the currentFoo
changes and update the UI.
currentFoo = self.fooGroupViewModel.selectedFoo
But actually it doesn't work.
Any help? thanks!
I also have another question, its the comment in above code(Line 56 if paste my code into Playground)
ForEach(viewModel.foos) { foo in
Text(foo.name)
}
this code make the playground error:
// Error: error: Execution was interrupted, reason: signal SIGABRT.
//The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
without any other information in console. Not sure why. Thanks for any help.
Upvotes: 1
Views: 3493
Reputation: 49580
Just doing currentFoo = self.fooGroupViewModel.selectedFoo
definitely wouldn't work - it just assigns to currentFoo
whatever selectedFoo
is at the time.
With your setup, you would need to subscribe to changes in fooGroupViewModel.selectedFoo
, which could be done via a Published
publisher, available because it's a @Published
property.
Also, bear in mind that @ObservedObject
only makes sense inside a View, so I removed it.
import Combine
// ...
final class MainViewModel: ObservableObject {
var fooGroupViewModel: FooGroupViewModel
@Published var currentFoo: Foo?
private var cancellables: Set<AnyCancellable> = []
init() {
fooGroupViewModel = FooGroupViewModel(foos:
[Foo(name: "foo1", id: "1"), Foo(name: "foo2", id: "2")])
fooGroupViewModel.$selectedFoo
.assign(to: \.currentFoo, on: self)
.store(in: &cancellables)
}
}
Upvotes: 9