Home > database >  Binding value changes only once
Binding value changes only once

Time:11-09

I have the following View structure and run into the problem of the button working the first time I click and disabling the button like it should, but once I clicked one button it does not work for the other buttons. The Item is always the right one, I checked that by printing it out.

My ReelsView:

struct ReelsView: View {
    @State var currentReel = ""
    @State var items: [Item] = [
        Item(chance: "1:1m", tradeIn: 2000, name: "Apple Watch", price: "$200", rarityType: "common", description: "The Apple Watch", reel: Reel(player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "apple-watch", ofType: "mp4") ?? "")), bid: false)),
        Item(chance: "1:20m", tradeIn: 27500, name: "Ibiza vacation", price: "$2750,00", rarityType: "superRare", description: "Such a wonderful place for a vacation", reel: Reel(player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "ibiza", ofType: "mp4") ?? "")), bid: false)),
    ]
    var body: some View {
        TabView(selection: $currentReel) {
            ForEach($items) { $reel in
                ReelsPlayer(reel: $reel.reel, currentReel: $currentReel, item: $reel)
                    .tag(reel.reel.id)
            }
        }
    }
}

My ReelsPlayer:

struct ReelsPlayer: View {
    @Binding var reel: Reel
    @Binding var currentReel: String
    @Binding var item: Item
    var body: some View {
        ZStack {
            if let player = reel.player {
                CustomVideoPlayer(player: player)
                    .allowsHitTesting(false)
            }
        }
        .overlay {
            BottomOverlay(item: $item)
                .allowsHitTesting(true)
        }
    }
}

My BottomOverlay:

struct BottomOverlay: View {
    @Binding var item: Item
    var body: some View {
        Button() {
            item.reel.bid.toggle()
            print("item: ", item)
            print("item: ", $item)
        } label: {
            Text(item.reel.bid ? "Already Bid" : "Bid")
        }
    }
}
struct Reel: Identifiable {
    var id = UUID().uuidString
    var player: AVPlayer
    var bid: Bool
}
struct Item: Identifiable, Hashable {
    static func == (lhs: Item, rhs: Item) -> Bool {
        lhs.id == rhs.id
    }
    
    func hash(into hasher: inout Hasher) {
            hasher.combine(chance)
            hasher.combine(name)
            hasher.combine(price)
            hasher.combine(tradeIn)
            hasher.combine(rarityType)
            hasher.combine(description)
        }
    
    var id: String = UUID().uuidString
    var chance: String
    var tradeIn: Int
    var name: String
    var price: String
    var rarityType: String
    var description: String
    var reel: Reel
}

CodePudding user response:

Here is the code I used in my test, to show that the two buttons acts separately. Click on one, and it print the item id and state. Click on the other, and same for that item. If you un-comment .disabled(item.clicked), then it only works once, because the Button (for that item) is now disabled.

struct Item: Identifiable {
    let id = UUID()
    var name: String
    var clicked: Bool
}

struct ContentView: View {
    var body: some View {
        MainView()
    }
}

struct MainView: View {
    @State var items: [Item] = [Item(name: "item1", clicked: false),Item(name: "item2", clicked: false)]
    
    var body: some View {
        ForEach($items) { $item in
            ItemView(item: $item)
        }
    }
}

struct ItemView: View {
    @Binding var item: Item
    var body: some View {
        VStack {
            Color.green
        }.overlay {
            OverlayView(item: $item)
        }
    }
}

struct OverlayView: View {
    @Binding var item: Item
    var body: some View {
        VStack (spacing: 33){
            Button() {
                item.clicked.toggle()
                print("--> item.id: \(item)  item.clicked: \(item.clicked)")
            } label: {
                Text(item.name)
            }//.disabled(item.clicked)
            //            Button() {
            //                item.clicked.toggle()
            //            } label: {
            //                Text("enable again")
            //            }
        }
    }
}

EDIT-1: in view of your new code. Try this example code, works well for me

import Foundation
import SwiftUI
import UIKit
import AVFoundation
import AVKit


struct ContentView: View {
    var body: some View {
        ReelsView()
    }
}

struct ReelsView: View {
    @State var currentReel = ""
    @State var items: [Item] = [
        Item(chance: "1:1m", tradeIn: 2000, name: "Apple Watch", price: "$200", rarityType: "common", description: "The Apple Watch", reel: Reel(player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "apple-watch", ofType: "mp4") ?? "")), bid: false)),
        Item(chance: "1:20m", tradeIn: 27500, name: "Ibiza vacation", price: "$2750,00", rarityType: "superRare", description: "Such a wonderful place for a vacation", reel: Reel(player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "ibiza", ofType: "mp4") ?? "")), bid: false)),
    ]
    
    var body: some View {
        TabView(selection: $currentReel) {
            ForEach($items) { $item in
                ReelsPlayer(currentReel: $currentReel, item: $item) // <-- here
                    .tag(item.reel.id)
            }
        }.tabViewStyle(.page)  // <-- here
    }
}

struct ReelsPlayer: View {
    @Binding var currentReel: String
    @Binding var item: Item  // <-- here
    var body: some View {
        ZStack {
            if let player = item.reel.player {  // <-- here
                //  CustomVideoPlayer(player: player)
                // for testing
                VStack {
                    if item.name == "Apple Watch" { Color.yellow } else { Color.green }
                }
                .allowsHitTesting(false)
            }
        }
        .overlay {
            BottomOverlay(item: $item)
                .allowsHitTesting(true)
        }
    }
}

struct BottomOverlay: View {
    @Binding var item: Item
    var body: some View {
        Button() {
            item.reel.bid.toggle()
            print("----> BottomOverlay item.reel.bid: ", item.reel.bid)  // <-- here
        } label: {
            Text(item.reel.bid ? "Already Bid" : "Bid")
        }
    }
}

struct Reel: Identifiable, Hashable {  // <-- here
    var id = UUID().uuidString
    var player: AVPlayer
    var bid: Bool
}

struct Item: Identifiable, Hashable {  // <-- here
    var id: String = UUID().uuidString
    var chance: String
    var tradeIn: Int
    var name: String
    var price: String
    var rarityType: String
    var description: String
    var reel: Reel
}
  • Related