问题
In the code below, the variable "yourVariable" is defined in a struct. Then I use a slider to change the "yourVariable" but when I try and use the variable in the SecondView, it uses the original variable and not the updated one. I have been told I need to update the property, should I do this? If yes can someone please explain why.
import SwiftUI
import PlaygroundSupport
struct MyVariables {
static var yourVariable = 500000.0
}
struct SecondView : View {
@Environment(\.presentationMode) var presentationMode
var string = MyVariables.yourVariable
var body: some View {
VStack {Button("\(string)") {
self.presentationMode.wrappedValue.dismiss()
}
}
}
}
struct ContentView : View {
@State private var showingSheet = false
@State public var step = 0
@State public var text = ""
@State public var slide = 20.0
@State public var slidetwo = MyVariables.yourVariable + 60000
var r0 : Double {
rvalues[(wearingMaskToggle ? 1:0) + (washingHandsToggle ? 2:0) + (quarantineToggle ? 8:0) + (coverMouthToggle ? 4:0)]
}
@State private var rvalues = [6.5, 6.1, 5.3, 4.8, 4.2, 3.5, 3.1, 2.9, 1.75, 1.5, 1.2, 1.0, 0.92, 0.82, 0.75, 0.7]
var body: some View {
ZStack {
Color.gray
.edgesIgnoringSafeArea(.all)
VStack {
HStack {
Button(action: {
if self.slidetwo > 10000{
self.slidetwo -= 1000 //When rand = 10050, it still works
}
}, label: {
Image(systemName: "minus")
})
Slider(value: $slidetwo, in: 10000...1000000, step: 1000)
.padding(20)
.accentColor(Color.green)
Button(action: {
if self.slidetwo < 1000000{
self.slidetwo += 1000
}
}, label: {
Image(systemName: "plus")
})
}.foregroundColor(Color.green) .padding(.horizontal, 100)
}
Text("Initial Population: \(Int(slidetwo))")
Button("Show Sheet") {
self.showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
SecondView()
}
}
}
}
PlaygroundPage.current.setLiveView(ContentView())
回答1:
First off, the slider is updating the variable slidetwo
witch has an initial value of 560000
.
When you open the second view you are getting the value of the struct into the variable string
witch will result in having always the value that's defined by yourVariable
, so moving the slider does not affect at all the second view.
In order to properly do this you could use a Binding
in case that you want the variable to be changed inside the second view and reflecting those changes in the ContentView
or a simple un-initialized @State var
Going with the second option it would result in replacing, in the SecondView
var string = MyVariables.yourVariable
with @State var string: String
Then in the ContentView
inside the sheet you call SecondView(string: String(slidetwo))
instead of SecondView()
I couldn't figure out why would you need a struct in here. All it does is hold a default value and nothing more, and you don't update the variable anywhere. You can't update it directly because it can't be binded.
Update 1:
Based on your comment, what I understand is that you want multiple variables to the SecondView, if that's the case you are better off using and ObservableObject
in conjunction of EnvironmentObject
And it's rather easy to do. First create a class that will hold all your variables. This class inherits from the ObservableObject
class MyVariables: ObservableObject {
@Published var sliderTwo: Double = 0.0
@Published var yourVariable = 500000.0 // <- I don't know the purpose of this
init() {
sliderTwo = yourVariable + 60000 // <- This is the original value, in the original code
}
}
Now on the first view you want to initialize the class and use it where you need it. At this point the slide two will not update the @State
variable, but the variable in the model, and we will need to pass the ObservedObject
to the second view like so.
struct ContentView: View {
@ObservedObject var model: MyVariables = MyVariables()
//@State public var slidetwo = MyVariables.yourVariable + 60000 <- no longer needed
// ... change every other reference of slidertwo to model.slidertwo
// except the following one witch instead of $slidertwo it will be $model.slidertwo
Slider(value: $model.slidertwo, in: 10000...1000000, step: 1000)
// ...
.sheet(isPresented: $showingSheet) {
SecondView().environmentObject(self.model) // send the model as an environment object
}
}
Now we are sending the model with the data updated on the first view, to the second view, and any changes will be reflected in both views, just one last thing, capture this EnvironmentObject
on the second view
struct SecondView: View {
// var string = MyVariables.yourVariable <- no longer needed
@EnvironmentObject var model: MyVariables
// ....
}
In this case the model.yourVariable
still doesn't change because we are updating the model.slidertwo
variable.
Changes made to the model will be reflected in both views.
From the experience that I have this is the correct way to do things in SwiftUI
回答2:
I'm afraid I'm too late but what you could also adjust in your code is to change the slideTwo variable to.
@Binding public var slidetwo: Double
You'd have to do the +60000 addition in the struct directly and then initialize your view as follows:
PlaygroundPage.current.setLiveView(ContentView(slidetwo: .init(get: { () -> Double in
return MyVariables.yourVariable
}, set: { (newValue) in
MyVariables.yourVariable = newValue
})))
That would result in applying all changes to slideTwo
to be applied to yourVariable
as well. However, as Pedro already mentioned, I wouldn't recommend that approach with the struct.
来源:https://stackoverflow.com/questions/61739670/how-to-update-variable-proper-in-different-struct-swift