'@State var not updating SwiftUI View
I want to have my content view display data that is global to the app and manipulated outside of the content view itself.
Does swift have a binding to allow outside variables?
I have created what I think is the most basic of applications:
//
// myTestxApp.swift
// myTestx
import SwiftUI
var myStng = "Hello\n"
var myArray = ["Hello\n"]
func myTest(){
myStng.append("Hello\n")
myArray.append("Hello\n")
print(myStng,myArray)
}
@main
struct myTestxApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
//
// ContentView.swift
// myTestx
import SwiftUI
struct ContentView: View {
@State var i = myStng
@State var j = myArray
var body: some View {
VStack{
Button( action: myTest ){ Text("Update") }
List{ Text(i).padding() }
List{ ForEach(myArray, id: \.self)
{ i in Text(i).padding()} }
} //end VStack
} //end View
} //end ContentView
I declare two app global variables, have an external function where they are updated, and for this example, a view with a calling button to the function and List areas for the updated results tied via @State variables. In my planned app, the update functions would be part of the data processing activity. I want to be able to edit data and have the content view(s) update displayed data when that data item is updated. In this example:
Code compiles and runs, with the console showing two variables being updated, but the view controller is not responding to the state change? Is @State the appropriate binding to use or should I use some other method to cause the content view items to recognize content change?
Any pointers would be greatly appreciated.
Solution 1:[1]
As other fellows suggested, you have to be very careful about using global variables, you should expose them only to the scope needed.
The problem is that you are treating @State var i = myStng thinking this would create a reactive connection between i and myStng, but that is not true. This line is creating a reactive connection between i and a memory address that SwiftUI manages for you, with its first value being what myStng is at that exact moment.
Anyway, I am posting an example of how can you achieve your goal using Environment Object with your provided code.
import SwiftUI
class GlobalVariables: ObservableObject{
@Published var myStng = "Hello\n"
@Published var myArray = ["Hello\n"]
}
func myTest(variables: GlobalVariables){
variables.myStng.append("Hello\n")
variables.myArray.append("Hello\n")
}
@main
struct myTestxApp: App {
@StateObject var globalEnvironment = GlobalVariables()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(globalEnvironment)
}
}
}
//
// ContentView.swift
// myTestx
//import SwiftUI
struct ContentView: View {
@EnvironmentObject var global: GlobalVariables
var body: some View {
VStack{
Button {
myTest(variables: global)
} label: {
Text("Update")
}
List{ Text(global.myStng).padding() }
List{ ForEach(global.myArray, id: \.self)
{ i in Text(i).padding()} }
} //end VStack
} //end View
} //end ContentView
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | kildos |
