Home > OS >  Find an item and change value in custom object array - SwifUI
Find an item and change value in custom object array - SwifUI

Time:09-22

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)
        }
    }
}
  • Related