I have this specific data that I want to use for my app.
struct AssetData : Identifiable {
var id: Int
var title: String
var image: Image
var price: Int
var pricePerSec: Int
var own: Bool
}
class AssetDatas : ObservableObject {
@Published var assetData: [AssetData] = []
init() {
getAssetData()
}
func getAssetData() {
let asset1 = AssetData(id: 0, title: "株",image: Image("asset1"), price: 1000, pricePerSec: 10000, own: false)
let asset2 = AssetData(id: 1, title: "時計",image: Image("asset2"), price: 2000, pricePerSec: 20000, own: false)
let asset3 = AssetData(id: 2, title: "車",image: Image("asset3"), price: 3000 , pricePerSec: 50000, own: false)
let asset4 = AssetData(id: 3, title: "家",image: Image("asset4"), price: 4000, pricePerSec: 50000, own: false)
let asset5 = AssetData(id: 4, title: "ダイヤモンド",image: Image("asset5"), price: 5000, pricePerSec: 50000, own: false)
let asset6 = AssetData(id: 5, title: "船",image: Image("asset6"), price: 6000, pricePerSec: 50000, own: false)
let asset7 = AssetData(id: 6, title: "飛行機",image: Image("asset7"), price: 7000, pricePerSec: 50000, own: false)
let asset8 = AssetData(id: 7, title: "人工衛星",image: Image("asset8"), price: 8000, pricePerSec: 50000, own: false)
let asset9 = AssetData(id: 8, title: "月",image: Image("asset9"), price: 9000, pricePerSec: 50000, own: false)
let asset10 = AssetData(id: 9, title: "宇宙人",image: Image("asset10"), price: 10000, pricePerSec: 50000, own: false)
self.assetData.append(contentsOf: [
asset1,
asset2,
asset3,
asset4,
asset5,
asset6,
asset7,
asset8,
asset9,
asset10
])
}
}
I am looking for a way to change a value of an item in the array such as "price" or "own" when the user clicks a button in a view. Is this possible?
my view is something like this
struct AssetListView : View {
var level = 1
@EnvironmentObject var user : UserData
@StateObject var asset = AssetDatas()
@State private var buttonBackColor:Color = .yellow
@State private var buttonText: String = "買う"
let timer = Timer.publish(every: 1, on: .current, in: .common).autoconnect()
var body: some View {
ScrollView{
VStack(alignment: .leading){
ForEach(asset.assetData) { assets in
HStack{
assets.image
.resizable()
.frame(width:50, height: 50)
Text(assets.title)
.font(.headline)
Spacer()
Text("金額: \(assets.price)円")
Button(action: {
if user.pocketMoney >= assets.price && buttonText == "買う"{
user.pocketMoney -= assets.price
self.buttonText = "売る"
self.buttonBackColor = .blue
};if buttonText == "売る"{
buttonText = "買う"
self.buttonBackColor = .blue
user.pocketMoney = assets.price
} else {
}
}) {
Text(buttonText)
.padding(10)
.background(buttonBackColor)
.cornerRadius(15)
}
}
}
.foregroundColor(.white)
.padding()
.background(Color("Color3").cornerRadius(10).opacity(0.8))
.padding(.horizontal)
}
}
}
}
In the view, I have a button, and when I click the button, I want the price of the button to change, and I also want to change the bool of "own". How can I do that?
CodePudding user response:
In iOS 15 , you can use a simple syntax to get a binding to the element in your ForEach
that you wish to modify:
struct ContentView : View {
@StateObject var assets = AssetDatas()
var body: some View {
ForEach($assets.assetData) { $asset in
HStack {
Text("\(asset.price)")
Button("Change price") {
asset.price = 0
}
}
}
}
}
Prior to the introduction of that feature, you could provide a custom binding:
//Inside AssetDatas
func bindingForAsset(id: Int) -> Binding<AssetData> {
.init {
self.assetData.first(where: { $0.id == id })!
} set: { newValue in
self.assetData = self.assetData.map { $0.id == id ? newValue : $0 }
}
}
struct ContentView : View {
@StateObject var assets = AssetDatas()
var body: some View {
ForEach(assets.assetData) { asset in
HStack {
Text("\(asset.price)")
Button("Change price") {
assets.bindingForAsset(id: asset.id).wrappedValue.price = 0
}
}
}
}
}
CodePudding user response:
You can use a ForEach element binding. See here for more information on what that is. In simple terms, it allows you to pass in a Binding
into your ForEach
, and then you can get a Binding
for each element.
In this example, you give $asset.assetData
(Binding<[AssetData]>
) and get $asset
(Binding<AssetData>
), asset
(AssetData
), and _asset
(not needed).
The three changes are marked with <- HERE
. Code:
var body: some View {
ScrollView{
VStack(alignment: .leading){
ForEach($asset.assetData) { $assets in // <- HERE
HStack{
assets.image
.resizable()
.frame(width:50, height: 50)
Text(assets.title)
.font(.headline)
Spacer()
Text("金額: \(assets.price)円")
Button(action: {
assets.price = 10 // <- HERE
assets.own = true // <- HERE
if user.pocketMoney >= assets.price && buttonText == "買う"{
user.pocketMoney -= assets.price
self.buttonText = "売る"
self.buttonBackColor = .blue
};if buttonText == "売る"{
buttonText = "買う"
self.buttonBackColor = .blue
user.pocketMoney = assets.price
} else {
}
}) {
Text(buttonText)
.padding(10)
.background(buttonBackColor)
.cornerRadius(15)
}
}
}
.foregroundColor(.white)
.padding()
.background(Color("Color3").cornerRadius(10).opacity(0.8))
.padding(.horizontal)
}
}
}