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
相关产品推荐
相关产品推荐

