How to reset a subview in SwiftUI?
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.
Child subviews not resetting when a new parent is created in SwiftUI
I'll repeat what I said in an answer to your previous question - under most normal use cases you shouldn't instantiate views as variables, so if you find yourself doing that, you might be on the wrong track.
Whenever there's any state change, SwiftUI recomputes the body and reconstructs the view tree, and matches the child view states to the new tree.
When it detects that something has changed, it realizes that the new child view is truly new, so it resets its state, fires .onAppear
and so forth. But when there's no change that it can detect, then it just keeps the same state for all the descendent views.
That's what you're observing.
Specifically, in your situation nothing structurally has changed - i.e. it's still:
GameView
--> KeyboardView
--> ButtonView
ButtonView
ButtonView
...
so, it keeps the state of ButtonView
s as is.
You can signal to SwiftUI that the view has actually changed and that it should be updated by using an .id
modifier (documentation isn't great, but you can find more info in blogs), where you need to supply any Hashable
variable to it that's different than the current one, in order to reset it.
I'll use a new Bool
variable as an example:
struct GameView {
@State var id: Bool = false // initial value doesn't matter
var body: some View {
VStack() {
KeyboardView()
.id(id) // use the id here
Button("new game") {
self.id.toggle() // changes the id
}
}
}
}
Every time the id
changes, SwiftUI resets the state, so all the child views', like ButtonView
s', states are reset.
Reset main content view - Swift UI
The possible approach is to use global app state
class AppState: ObservableObject {
static let shared = AppState()
@Published var gameID = UUID()
}
and have root content view be dependent on that gameID
@main
struct SomeApp: App {
@StateObject var appState = AppState.shared // << here
var body: some Scene {
WindowGroup {
ContentView().id(appState.gameID) // << here
}
}
}
and now to reset everything to initial state from any place we just set new gameID
:
Button("New Game"){
AppState.shared.gameID = UUID()
}
Reset View on Button Click SwiftUI
you could try this approach using a ObservableObject
model to keep track of your Cell state. For example:
class CellViewModel: ObservableObject {
@Published var cells = [Cell]()
func resetAll() {
for i in cells.indices {
cells[i].isDisabled = false
}
}
}
struct CellView: View {
@Binding var cell: Cell
var body: some View {
Button {
cell.isDisabled = true
} label: {
Text("\(cell.value)").frame(width: 100, height: 100)
}.disabled(cell.isDisabled)
.border(Color.black)
}
}
struct ContentView: View {
@StateObject var cellModel = CellViewModel()
var body: some View {
VStack {
List {
ForEach($cellModel.cells) { $cell in
CellView(cell: $cell)
}
}
Button("Reset all") {
cellModel.resetAll()
}
}
.onAppear {
cellModel.cells.append(Cell(value: 1))
cellModel.cells.append(Cell(value: 2))
}
}
}
struct Cell: Identifiable, Hashable {
let id = UUID()
let value: Int
var isDisabled = false
}
Related Topics
Rotation Gesture Produces Undesired Rotation
Swift Generics and Protocols Not Working on Uikit [Possible Bug]
Collection View Layout Bug When Selectitem (Swift 5)
Swift: Filter a Dictionary with Array as Value
Undefined Symbol Objc_Class_$ When Creating iOS Framework
How to Loop All Firebase Children at Once in the Same Loop
Autoscrolling Infinite Effect in .Linear Type of Icarousel in Swift
Use of or Operator on Cloudkit Predicate
Disable Email Detection in Swiftui's Text
Replacing Multiple Different Occurences with Multiple Different Replacements - Swift4.2
Stopping Timer at Defined Amount of Time in Swift
Swiftui Tabview Gives an Error Message During Add/Delete the Element of Coredata
How to Increment Exponentially in Swift
Elegant 'Bounded' Methodology in Swift
Swift Deleting Table View Cell When Timer Expires
Any Way to Get a Gif as a Background with Swiftui
Simple Swift Class Does Not Compile
What Does an Exclamation Mark in a Property in Swift Language