How to reset a subview in SwiftUI?

有些话、适合烂在心里 提交于 2021-02-17 07:11:23

问题


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 Views 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!