首页 > 解决方案 > 使用 ForEach 创建视图,但如何让 onTapGesture 立即更新?

问题描述

这段代码的工作方式与我想要的随机播放动画完全一样,除了当用户点击一个矩形(在随机播放之间)时,onTapGesture 中的颜色变化在下一次随机播放之前不会更新。我需要看到它被点击时的颜色变化。我已经尝试了几种不同的方法来做到这一点,每种方法都给我带来了死胡同。是不是我的数据模型偏离了轨道?有没有更好的方法来实现这一点?提前致谢!我对 SwiftUI 还是很陌生,所以期待从中学习。

import SwiftUI

class EachSpace: ObservableObject, Identifiable {
    
    let id = UUID()
    @Published var num: Int
    @Published var tapped: Bool
    @Published var highlight: Bool
    
    init(number: Int) {
        num = number
        tapped = false
        highlight = false
    }
    
}

class Column: ObservableObject, Identifiable {
    
    @Published var spaces = [EachSpace]()
    @Published var space0: EachSpace
    @Published var space1: EachSpace
    @Published var space2: EachSpace
    @Published var space3: EachSpace
    @Published var space4: EachSpace
    let id = UUID()
    
    init(low: Int, high: Int) {
        var randIntArray = [Int]()
        var tempNum = 0
        var found = false
        for _ in 0..<5 {
            repeat {
                tempNum = Int.random(in: low...high)
                if (randIntArray.firstIndex(where: {$0 == tempNum}) != nil) {
                    found = true
                } else { found = false }
            } while found
            randIntArray.append(tempNum)
        }
        space0 = EachSpace(number: randIntArray[0])
        space1 = EachSpace(number: randIntArray[1])
        space2 = EachSpace(number: randIntArray[2])
        space3 = EachSpace(number: randIntArray[3])
        space4 = EachSpace(number: randIntArray[4])

        spaces.append(space0)
        spaces.append(space1)
        spaces.append(space2)
        spaces.append(space3)
        spaces.append(space4)

    }
}

class Card: ObservableObject {

    @Published var columns = [Column]()
    @Published var columnX = Column(low: 1, high: 25)
    @Published var columnY = Column(low: 26, high: 50)
    @Published var columnZ = Column(low: 51, high: 75)
    
    init() {
        columns.append(columnX)
        columns.append(columnY)
        columns.append(columnZ)
    }
    
}

struct ContentView: View {

    @State private var rotate = false
    @ObservedObject var card = Card()
    
    var body: some View {

        VStack {
            
            HStack {
                ForEach(card.columns) { col in
                    VStack {
                        ForEach(col.spaces) { space in
                            Text("\(String(space.num))")
                                .font(.system(size: 20))
                                .fontWeight(.black)
                                .frame(width: 40, height: 40, alignment: .center)
                                .background(space.tapped ? Color.blue : Color.red)
                                .clipShape(Rectangle())
                                .foregroundColor(.white)
                                .padding(10)
                                .rotationEffect(Angle(degrees: rotate ? 0 : 360))
                                .onTapGesture {
                                    space.tapped.toggle()
                                }
                        }
                    }
                }
            }
            Button(action: {
                withAnimation(Animation.linear(duration: 1.0)) {
                    self.rotate.toggle()
                    self.card.columnX.spaces.shuffle()
                    self.card.columnY.spaces.shuffle()
                    self.card.columnZ.spaces.shuffle()
                }
            })
            {
                Text("Shuffle")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

标签: swiftui

解决方案


原因是@ObservedObject/ObservableObject不会跨越嵌套的可观察对象 - 即EachSpace.tapped观察根Card对象的视图不会观察到变化。

最简单的解决方法是将最里面的Text视图移动到一个单独的视图中,该视图将EachSpace直接观察每个对象:

struct SpaceView: View {
   @ObservedObject var space: EachSpace

   var body: some View {
       Text("\(String(space.num))")
            .font(.system(size: 20))
            .fontWeight(.black)
            .frame(width: 40, height: 40, alignment: .center)
            .background(space.tapped ? Color.blue : Color.red)
            .clipShape(Rectangle())
            .foregroundColor(.white)
            .padding(10)
            .rotationEffect(Angle(degrees: rotate ? 0 : 360))
            .onTapGesture {
                space.tapped.toggle()
            }
   }
}

并将其中的更改ForEachContentView

ForEach(col.spaces) { space in
   SpaceView(space: space)
}

推荐阅读