问题
Below is a simplified version of the code that I'm using. But whenever I resetKeyboard() it still shows the previous keyboard. Is there anyway to make it so when I call resetKeyboard() it replaces the keyboard with a fresh KeyboardView?
struct GameView: View {
@State var myKeyboard = KeyboardView()
var body: some View {
VStack{
Button("Change Keyboard") {
myKeyboard.change()
}
myKeyboard
Button("Reset Keyboard") {
resetKeyboard()
}
}
}
func resetKeyboard(){
self.myKeyboard = KeyboardView()
}
}
回答1:
SwiftUI constructs a view tree from View
objects in body
of their parents.
So, what SwiftUI got was the initial copy (remember, it's a value-type struct
) of myKeyboard
, not the copy you are changing.
Under normal usage, you don't keep instances of various View stored as variables (I mean, you can, but you'd need to understand in depth what's going on).
What you probably want is to change the data that drives the child view. Where does (should) this data live? It depends on what you want to do.
In most cases the parent "owns" the state, i.e. has the source of truth of some data that the child relies on. Then it's trivial to change the state and pass the state to the child via its init
:
struct ChildView: View {
let number: Int
var body: some View {
Text("\(number)")
}
}
struct ParentView: View {
@State var random: Int = Int.random(1...100)
var body: some View {
VStack() {
ChildView(number: random)
Button("randomize") {
self.random = Int.random(1...100)
}
}
}
}
But, say, the parent doesn't want to do the randomization - i.e. the child should deal with it.
The proper approach is to create a view model for the child, which the parent (or the parent's own view model) could own and pass via init
, and then the view model would deal with nuances of randomization.
class ChildVM: ObservableObject {
@Published var random = Int.random(1...100)
func change() {
random = Int.random(1...100)
}
}
The parent creates an instance of ChildVM
and passes it to the child:
struct ParentVuew: View {
let childVM = ChildVM()
var body: some View {
VStack() {
ChildView(vm: childVm)
Button("randomize") {
self.childVM.change()
}
}
}
}
And the child view is simply driven by the view model:
struct ChildView: View {
@ObservedObject let vm: ChildVM
var body: some View {
Text("\(vm.random)")
}
}
Obviously, this is a simplified example that could have been achieved in any number of ways.
And there are different ways for the parent to "message" the child.
But the general takeaway should be that View
s should be thought of as declarative structures - not living instances - and the data is what drives the changes in those views. You need to decide who is best to own the source of truth.
来源:https://stackoverflow.com/questions/63209921/how-to-reset-a-subview-in-swiftui