如何在 SwiftUI 中根据用户的选择获取特定数据?

How to fetch specific data based on user's choice in SwiftUI?

提问人:Ft klee 提问时间:10/17/2023 最后编辑:Ft klee 更新时间:10/18/2023 访问量:50

问:

我正在研究一种逻辑,用户可以选择标记他/她想在下一个学习会话中学习的卡片类别,但是,我目前面临的问题是我将如何根据选择获取数据。复选框的要点是让用户在点击“继续学习”按钮后按下他们想要学习的类别(或多个类别)。例如,用户可能会选择 1 和 2 类别,程序将重定向到 FlashcardSetView,并且显示的卡片将是用户之前选择的类别。此外,为了更清楚起见,以前,每张卡片都分配有一个(CoreData 属性)编号,该编号表示卡片所在的类别,以便以后用户能够研究他/她想要的卡片。FlashCardSetViewcardStatus

下面是 EndView(用户在其中选择接下来要研究的卡片类别):

import SwiftUI
struct EndView: View {
var set: FlashSets
@ObservedObject var dataController = DataController.shared

@State private var continueFlashying = false
@State private var returnHome = false
@State private var selectedCategories: [CategorySelection] = [
    CategorySelection(category: 1, isSelected: false),
    CategorySelection(category: 2, isSelected: false),
    CategorySelection(category: 3, isSelected: false),
    CategorySelection(category: 4, isSelected: false)
]
var selectedCardStatuses: [Int] {
    return selectedCategories.filter { $0.isSelected }.map { $0.category }
}
var body: some View {
    NavigationStack {
        VStack {
            Text("Set finished👍")
                .bold()
                .font(.system(size: 36))
                .padding()
            
            // Display the number of cards in each category
            List {
                ForEach(1...4, id: \.self) { category in
                    CategoryRow(categoryName: "Category \(category) cards studied: \(set.cardsArray.filter { $0.cardStatus == category }.count)", category: category, isSelected: $selectedCategories[category - 1].isSelected)
                }
            }
            VStack {
                Button(action: {
                    continueFlashying = true
                }) {
                    Text("Continue studying")
                    NavigationLink(destination: FlashcardSetView(set: set, selectedCardStatuses: selectedCardStatuses), isActive: $continueFlashying) {
                        EmptyView()
                    }
                }
                Button(action: {
                    // Handle the "Start again" button
                }) {
                    Text("Start again")
                }
                Button(action: {
                    returnHome = true
                }) {
                    // Return home button
                }
            }
        }
    }
    .navigationBarBackButtonHidden(true)
}}

struct CategoryRow: View {
let categoryName: String
let category: Int
@Binding var isSelected: Bool

var body: some View {
    HStack {
        Text(categoryName)
        
        Spacer()
        
        CheckboxView(isSelected: $isSelected)
    }
}}
struct CheckboxView: View {
@Binding var isSelected: Bool

var body: some View {
    Button(action: {
        isSelected.toggle()
    }) {
        Image(systemName: isSelected ? "checkmark.circle.fill" : "circle")
            .foregroundColor(isSelected ? .green : .secondary)
    }
}}
struct CategorySelection {
var category: Int
var isSelected: Bool

}

下面是 FlashcardSetView:

struct FlashcardSetView: View {
//    @FetchRequest(entity: FlashSets.entity(),
//                  sortDescriptors: [NSSortDescriptor(keyPath: \FlashSets.date, ascending: false)])
@FetchRequest(
    entity: FlashCardData.entity(),
    sortDescriptors: [NSSortDescriptor(keyPath: \FlashCardData.date, ascending: false)],
    predicate: NSPredicate(format: "cardStatus == %d", 3) // Fetch cards with category 3
)
var categoryThreeCards: FetchedResults<FlashCardData>

var set: FlashSets
@ObservedObject var dataController = DataController.shared
@Environment(\.managedObjectContext) var managedObjectContext
@State private var showTermDefinitionView = false
@Environment(\.dismiss) var dismiss
@State private var isTapped = false
@State private var isEdited = false
//@State private var selectedCards: [FlashCardData] = [] // Keep track of selected cards
@State var allSwiped = false
@State private var showEndView = false
@State private var offset = CGSize.zero
@State private var currentlySelectedCard: FlashCardData?
@State var isLearned = false //1
@State var isThink = false  //2
@State var isHard = false   // 3
@State var isRepeat = false // 4
@State var studyPhase: StudyPhase = .initial
@State var currentCardIndex = 0
@State private var toEndView = false
var selectedCardStatuses: [Int]

var body: some View {
    NavigationView {
        ZStack {
            NavigationLink(destination: EndView(set: set), isActive: $toEndView) {
                EmptyView()
            }
            .isDetailLink(false)
            Text("Card list is empty🙅‍♂️")
                .padding(.top, -50)
            if studyPhase == .initial {
                ForEach(set.cardsArray, id: \.self) { card in
                    //                    if let unwrappedCard = card {
                    //                        //Text(unwrappedCard.name)
                    //                    }
                    VStack {
                        Text("Cards studied: \(currentCardIndex)")
                            .font(.headline)
                            .offset(y: -20)
                        SingleFlashCard(cards: set.cardsArray,
                                        removal: {
                            withAnimation(.easeInOut) {
                                removeCurrentCard()
                            }
                            print("Removing card with animation")
                        }, set: set, currentCardIndex: $currentCardIndex, isLearned: $isLearned,
                                        isThink: $isThink,
                                        isHard: $isHard,
                                        isRepeat: $isRepeat)
                        .offset(y: -20)
                        .transition(.asymmetric(insertion: .opacity, removal: .opacity))
                    }
                    .onAppear {
                        currentlySelectedCard = card
                        // Automatically transition to the EndView when all cards are studied
                    }
                }
            }
            if studyPhase == .hard {
                ForEach(categoryThreeCards, id: \.self) { card in
                    //                    if let unwrappedCard = card {
                    //                        //Text(unwrappedCard.name)
                    //                    }
                    VStack {
                        Text(set.cardsArray.count.description)
                        SingleFlashCard(cards: set.cardsArray,
                                        removal: {
                            withAnimation(.easeInOut) {
                                removeCurrentCard()
                            }
                            print("Removing card with animation")
                        }, set: set, currentCardIndex: $currentCardIndex, isLearned: $isLearned,
                                        isThink: $isThink,
                                        isHard: $isHard,
                                        isRepeat: $isRepeat)
                        .transition(.asymmetric(insertion: .opacity, removal: .opacity))
                    }
                    .onAppear {
                        currentlySelectedCard = card
                    }
                }
            }
            NavigationLink(destination: EditFlashCardView(dataController: dataController, set: set), isActive: $isEdited) {
                EmptyView()
            }
            .toolbar(.hidden, for: .tabBar)
        }
    }
    .navigationBarBackButtonHidden(true)
    .navigationBarItems(trailing:
                            Menu("Options") {
        Button(action: {
            showTermDefinitionView = true
        }) {
            NavigationLink(destination: TermDefinitionView(currentSet: set), isActive: $showTermDefinitionView) {
                Text("Add cards")
            }
        }
        Button(action: {
            isEdited = true
        }) {
            Text("Edit cards")
        }
    }
    )
    
    HStack {
        Button(action: {
            currentlySelectedCard?.cardStatus = 1
            
            //removeCard(currentlySelectedCard)
            self.isLearned.toggle()
            removeCurrentCard()
        }) {
            Text("👍")
                .frame(width: 70, height: 50)
                .background(Color("Easy"))
                .clipShape(RoundedRectangle(cornerRadius: 8))
        }
        
        .padding(.trailing, 20)
        
        Button(action: {
            // Handle button action
            self.isThink.toggle()
            currentlySelectedCard?.cardStatus = 2
            removeCurrentCard()
        }) {
            Text("🤔")
                .frame(width: 70, height: 50)
                .background(Color("Think"))
                .clipShape(RoundedRectangle(cornerRadius: 8))
        }
        .padding(.trailing, 20)
        
        Button(action: {
            // Handle button action
            self.isHard.toggle()
            currentlySelectedCard?.cardStatus = 3
            removeCurrentCard()
        }) {
            Text("🤬")
                .frame(width: 70, height: 50)
                .background(Color("Hard"))
                .clipShape(RoundedRectangle(cornerRadius: 8))
        }
        .padding(.trailing, 20)
        Button(action: {
            // Handle button action
            self.isRepeat.toggle()
            currentlySelectedCard?.cardStatus = 4
            removeCurrentCard()
        }) {
            Image(systemName: "repeat.circle.fill")
                .frame(width: 70, height: 50)
                .foregroundColor(.white)
                .background(Color.red)
                .clipShape(RoundedRectangle(cornerRadius: 8))
        }
    }
}
private func removeCurrentCard() {
    if studyPhase == .initial {
        if currentCardIndex < set.cardsArray.count - 1 {
            currentCardIndex += 1 // Move to the next card
            currentlySelectedCard = set.cardsArray[currentCardIndex] // Update currentlySelectedCard
        } else {
            if let lastCard = set.cardsArray.last {
                // All cards in the set have been studied
                toEndView = true // Transition to the EndView
            } else {
                studyPhase = .hard // Transition to the "hard" study phase if there are more cards
            }
        }
    } else if studyPhase == .hard {
        if currentCardIndex < categoryThreeCards.count - 1 {
            currentCardIndex += 1 // Move to the next card
            currentlySelectedCard = categoryThreeCards[currentCardIndex] // Update currentlySelectedCard
        } else {
            if categoryThreeCards.isEmpty {
                // All "hard" cards are studied
                toEndView = true // Transition to the EndView
            } else {
                // Handle the case when there are more cards in category 3
            }
        }
    }
}


//    func removeMethod(at index: Int) {
//        set.cardsArray.remove(at: index)
//    }

}

单抽认卡:

struct SingleFlashCard: View {
let cards: [FlashCardData]
var removal: (() -> Void)? = nil
var set: FlashSets
@State private var isTapped = false
@Binding var currentCardIndex: Int
@Binding var isLearned: Bool
@Binding var isThink: Bool
@Binding var isHard: Bool
@Binding var isRepeat: Bool

var body: some View {
    ZStack {
        RoundedRectangle(cornerRadius: 25, style: .continuous)
            .fill(Color.white)
        //.offset(x: isLearned ? 500 : 0)
        //.overlay(RoundedRectangle(cornerRadius: 25).stroke(getColor(), lineWidth: 2))// Here we change the border color based on the swipe direction
            .shadow(radius: 3)
        
        VStack {
            NavigationStack {
                Text(currentCardIndex < set.cardsArray.count ? (set.cardsArray[currentCardIndex].term ?? "Unnamed Card") : "No more cards")

                    .offset(y: -100)
                Divider()
                
                if isTapped {
                    Text(currentCardIndex < set.cardsArray.count ? (set.cardsArray[currentCardIndex].definition ?? "Unnamed Card") : "No more cards")
                        .offset(y: 100)
                }
            }
            
            
        }
        
        
    }
    .frame(width: 300, height: 500)
}

}

DataController 文件来控制 CoreData:

class DataController:  ObservableObject {
static let shared = DataController()
@Published var termdefpairs: [TermAndDefinition] =          

[TermAndDefinition(术语:“”,定义:“”)] @Published var savedFlash: [FlashCardData] = []

@Published var dataUpdated: Bool = false


let container: NSPersistentContainer

init(inMemory: Bool = false) {
    container = NSPersistentContainer(name: "CoreData")
    
    if inMemory {
        container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
    }
    // super.init()
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
        self.container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
    })
    
    container.viewContext.automaticallyMergesChangesFromParent = true
    //fetchRequest()
}
func addNew() {
    termdefpairs.append(TermAndDefinition(term: "", definition: ""))
}
func fetchRequest() {
    let request: NSFetchRequest<FlashCardData> = FlashCardData.fetchRequest()
    let sort = NSSortDescriptor(keyPath: \FlashCardData.date, ascending: false)
    request.sortDescriptors = [sort]
    
    do {
        self.savedFlash = try container.viewContext.fetch(request)
    } catch {
        print("Failed to fetch FlashCardData: \(error)")
    }
}


func save() {
    do {
        try container.viewContext.save()
        print("Data saved")
        self.dataUpdated.toggle()  // This will trigger the view to update
    } catch {
        print("We could not save the data...")
    }
}

func add(term: String, definition: String, number: Int16, date: Date) {
    let data = FlashCardData(context: self.container.viewContext)
    data.id = UUID()
    data.definition = definition
    data.term = term
    data.number = number
    //   data.tag = tag
    data.date = date
    //      data.name = name
    // fetchRequest()
}

func addFlashcardSet(name: String, tag: String, date: Date)  {
    let newSet = FlashSets(context: self.container.viewContext)
    newSet.id = UUID()
    newSet.name = name
    newSet.tag = tag
    newSet.date = Date()
}
func update(data: FlashCardData, term: String, defintion: String, date: Date, context: NSManagedObjectContext) {
    data.term = term
    data.definition = defintion
    data.date = Date()
    save()
}
func deleteFlashCard(data: FlashCardData) {
    container.viewContext.delete(data)
    save()
}
func updateFlashSet(set: FlashSets, name: String, tag: String, date: Date) {
    set.name = name
    set.tag = tag
    set.date = date
    save()
}

}

FlashCardData entity

FlashSets entity

iOS Swift SwiftUI 核心数据

评论

0赞 Stoic 10/17/2023
请尽量把这个问题做成一个最小的例子,这样更容易理解。
0赞 Ft klee 10/18/2023
感谢您的回复,我添加了可能有助于您控制 CoreData 的其他代码

答: 暂无答案