You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中卡片数量动态更新问题求助

卡片数量动态更新解决方案

问题核心

卡片数量通过Text("Number of Cards: \(set.cardsArray.count)")展示,但移除卡片时数值不更新,根源有两点:

  • set未被SwiftUI监听,无法感知关联卡片集合的变化
  • 当前removeCurrentCard方法仅切换卡片索引,未真正从集合中移除卡片

修复步骤

1. 监听Core Data实体变化

如果FlashSets是Core Data实体,将var set: FlashSets改为@ObservedObject var set: FlashSets,SwiftUI会自动监听实体关联数据的变化并刷新视图:

@ObservedObject var set: FlashSets // 替换原有的var set: FlashSets

2. 真正移除卡片(更新Core Data数据)

修改removeCurrentCard方法,从Core Data上下文删除当前卡片并保存,set.cardsArray会自动同步更新:

private func removeCurrentCard() {
    guard studyPhase == .initial, let currentCard = currentlySelectedCard else { return }
    
    // 从Core Data中删除卡片
    managedObjectContext.delete(currentCard)
    do {
        try managedObjectContext.save()
    } catch {
        print("删除卡片失败: \(error.localizedDescription)")
    }
    
    // 处理后续逻辑
    if set.cardsArray.isEmpty {
        toEndView = true
    } else if currentCardIndex < set.cardsArray.count - 1 {
        currentCardIndex += 1
        currentlySelectedCard = set.cardsArray[currentCardIndex]
    } else {
        studyPhase = .hard
    }
}

3. 调整数量显示位置

原代码中数量文本在ForEach内部会重复渲染,将其移到ForEach外部仅显示一次:

if studyPhase == .initial {
    VStack {
        Text("Number of Cards: \(set.cardsArray.count)")
            .font(.headline)
            .padding(.top)
        
        ForEach(set.cardsArray, id: \.self) { card in
            SingleFlashCard(card: card, // 修正参数匹配问题
                            removal: {
                withAnimation(.easeInOut) {
                    removeCurrentCard()
                }
                print("Removing card with animation")
            }, isLearned: $isLearned,
               isThink: $isThink,
               isHard: $isHard,
               isRepeat: $isRepeat)
            .transition(.asymmetric(insertion: .opacity, removal: .opacity))
            .onAppear {
                currentlySelectedCard = card
            }
        }
    }
}

4. 修正SingleFlashCard参数不匹配问题

原SingleFlashCard定义接收单个FlashCardData实例,调用时需传递单个卡片而非数组:

// 修正后的SingleFlashCard调用
SingleFlashCard(card: card,
                removal: {
    withAnimation(.easeInOut) {
        removeCurrentCard()
    }
    print("Removing card with animation")
}, isLearned: $isLearned,
   isThink: $isThink,
   isHard: $isHard,
   isRepeat: $isRepeat)

完整修改后代码片段

FlashcardSetView 关键修改部分

struct FlashcardSetView: View {

    @FetchRequest(
        entity: FlashCardData.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \FlashCardData.date, ascending: false)],
        predicate: NSPredicate(format: "cardStatus == %d", 3)
    )
    var categoryThreeCards: FetchedResults<FlashCardData>

    @ObservedObject var set: FlashSets // 改为@ObservedObject
    @ObservedObject var dataController = DataController.shared
    @Environment(\.managedObjectContext) var managedObjectContext
    @State private var showTermDefinitionView = false
    @Environment(\.dismiss) var dismiss

    @State private var currentlySelectedCard: FlashCardData?
    @State var isLearned = false
    @State var isThink = false
    @State var isHard = false
    @State var isRepeat = false
    @State private var studyPhase: StudyPhase = .initial
    @State var currentCardIndex = 0
    @State private var toEndView = false


    var body: some View {
        NavigationView {
            ZStack {
                if set.cardsArray.isEmpty {
                    Text("Card list is empty😕")
                        .offset(y: -50)
                }

                if studyPhase == .initial {
                    VStack {
                        Text("Number of Cards: \(set.cardsArray.count)")
                            .font(.headline)
                            .padding(.top)

                        ForEach(set.cardsArray, id: \.self) { card in
                            SingleFlashCard(card: card,
                                            removal: {
                                withAnimation(.easeInOut) {
                                    removeCurrentCard()
                                }
                                print("Removing card with animation")
                            }, isLearned: $isLearned,
                               isThink: $isThink,
                               isHard: $isHard,
                               isRepeat: $isRepeat)
                            .transition(.asymmetric(insertion: .opacity, removal: .opacity))
                            .onAppear {
                                currentlySelectedCard = card
                            }
                        }
                    }
                }
            }
        }
    }

    private func removeCurrentCard() {
        guard studyPhase == .initial, let currentCard = currentlySelectedCard else { return }
        
        managedObjectContext.delete(currentCard)
        do {
            try managedObjectContext.save()
        } catch {
            print("删除卡片失败: \(error.localizedDescription)")
        }
        
        if set.cardsArray.isEmpty {
            toEndView = true
        } else if currentCardIndex < set.cardsArray.count - 1 {
            currentCardIndex += 1
            currentlySelectedCard = set.cardsArray[currentCardIndex]
        } else {
            studyPhase = .hard
        }
    }
}

SingleFlashCard 补充缺失状态

struct SingleFlashCard: View {
    let card: FlashCardData
    var removal: (() -> Void)? = nil

    @State var term = ""
    @State var definition = ""
    @Binding var isLearned: Bool
    @Binding var isThink: Bool
    @Binding var isHard: Bool
    @Binding var isRepeat: Bool
    @State private var isTapped = false // 补充原代码缺失的点击状态

    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 25, style: .continuous)
                .fill(Color.white)
                .shadow(radius: 3)

            VStack {
                Text(card.term ?? "Unnamed Card")
                    .offset(y: -100)
                Divider()
                if isTapped {
                    Text(card.definition ?? "Unnamed Card")
                        .offset(y: 100)
                }
            }
        }
        .frame(width: 300, height: 500)
        .onTapGesture {
            isTapped.toggle()
        }
    }
}

内容的提问来源于stack exchange,提问作者Ft klee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 01:21:04