I am really stuck on this. I want to make several button, each having independet state of ifButtonDisabled. The button disables when user clicks on it. Since, I want my app to remember the state of the button when the app reloads or refresh, I am using AppStorage. The problem is since each button has independent state, AppStorage variable should also be different. But since self is not avaialble, I am not able to have a dnamic name for my AppStorage variable.
Also, Is there any better alternative to achieve the same functionality???
struct ButtonData {
var id: UUID {
return UUID()
}
let level: String
let bgColor: String
let image: String
@AppStorage(level) var ifDisabled: Bool = false
}
I'm not sure why you would want to have an id
that changes every time you access it, but if you just want to initialise isDisabled
, you can write your own init
, and assign an AppStorage
to _isDisabled
there.
struct ButtonData {
let level: String
let bgColor: String
let image: String
// note that nothing is passed to @AppStorage here
// and this property is not initialised to anything - we'll do that in init
@AppStorage var isDisabled: Bool
init(level: String, bgColor: String, image: String) {
self.level = level
self.bgColor = bgColor
self.image = image
// this is where isDisabled is initialised to false,
// and the AppStorage with the level as key is created.
self._isDisabled = AppStorage(wrappedValue: false, level)
}
}
That said, your view won't update if you want to use it like this:
struct ContentView: View {
@State var buttonData = ButtonData(level: "foo", bgColor: "bar", image: "baz")
var body: some View {
Text("Foo")
.onTapGesture {
// buttonData.isDisabled.toggle()
}
Button("Button") { ... }.disabled(buttonData.isDisabled)
}
}
Because AppStorage
has a nonmutating
setter, so setting it won't cause buttonData
in the view to mutate, and so SwiftUI doesn't know that your view needs updating.
So if you want AppStorage
to update views, you need to put it directly in a View
. For example, you could make your own button view that takes in a ButtonData
, and sets its disabled status accordingly:
// ButtonData can simply be
struct ButtonData {
let level: String
let bgColor: String
let image: String
}
struct MyButton: View {
let data: ButtonData
@AppStorage var isDisabled: Bool
init(data: ButtonData) {
self.data = data
self._isDisabled = AppStorage(wrappedValue: false, data.level)
}
var body: some View {
// perhaps also provide init parameters for the action...
Button {
...
} label: {
Image(data.image)
}
.disabled(isDisabled)
}
}
Now whenever the superview updates the AppStorage
, MyButton
will update its disabled status too.