enum Niveau { case A case B case C case D case E case F case G } struct Carte: Identifiable { var id = UUID() var devant: String var derriere: String var session: [Session] = [] var dateDernierMalus = Date() var niveau: Niveau { get { switch score { case 0...5: return .G case 6...12: return .F case 12...18: return .E case 18...24: return .D case 24...30: return .C case 30...36: return .B case 37...42: return .A default: return .A // Si le score est supérieur à 42, le niveau est A par défaut } } set { // Ne fait rien, car on ne veut pas que la valeur de niveau soit modifiée directement } } private var _score: Int = 0 var score: Int { get { return _score } set { if newValue < 0 { _score = 0 } else { _score = newValue } } } private var _dateProchaineRevision: Date var dateProchaineRevision: Date { get { return _dateProchaineRevision } set { _dateProchaineRevision = newValue } } private static var dateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "dd/MM/yy" return formatter }() var formattedDate: String { Carte.dateFormatter.string(from: dateProchaineRevision) } var formattedDernierMalus: String { Carte.dateFormatter.string(from: dateDernierMalus) } init(devant: String, derriere: String, dateProchaineRevision: Date) { self.devant = devant self.derriere = derriere self.dateDernierMalus = Date() self._dateProchaineRevision = dateProchaineRevision self.score = 0 } } extension Carte { // Temps de ****** var tempsDeRetard: TimeInterval { let temps = dateProchaineRevision.timeIntervalSince(Date()) return max(0, temps) } // Temps entre dernier malus et aujourd'hui var tempsEntreDernierMalusEtAujourdhui: TimeInterval { let temps = Date().timeIntervalSince(dateDernierMalus) return temps } // Recalculer le score d'une carte mutating func recalculerScore() { let tempsDeRetard = self.tempsDeRetard if tempsDeRetard == 0 { return } if tempsDeRetard < 0 { let tempsEntreDernierMalusEtAujourdhui = self.tempsEntreDernierMalusEtAujourdhui if tempsEntreDernierMalusEtAujourdhui < 0 { let pointsASuspendre = Int(abs(tempsEntreDernierMalusEtAujourdhui) / 86400) // 1 jour = 86400 secondes score = max(0, score - pointsASuspendre) } } } // Vérifier si la carte doit être révisée func doitEtreRevisee() -> Bool { return tempsDeRetard <= 0 || score <= 3 } } // ## class AllListes: ObservableObject { @Published var listes: [Liste] = [] init(listes: [Liste]) { self.listes = listes } } class Liste: ObservableObject, Identifiable { var id = UUID() @Published var nom: String = "" @Published var cartes: [Carte] = [] init(nom: String, cartes: [Carte]) { self.nom = nom self.cartes = cartes } /* ## struct Liste: Identifiable { var id = UUID() var nom: String var cartes: [Carte] */ func compterCartes() -> Int { return cartes.count } func cartesAReviser() -> [Carte] { let maintenant = Date() return cartes.filter { carte in if carte.score <= 3 { return true } else { let tempsDeRetard = carte.dateProchaineRevision.timeIntervalSince(maintenant) return tempsDeRetard <= 0 } } } func prochaineRevisionDansMoinsDe(_ jours: Int) -> Int? { let maintenant = Date() var prochaineRevisionDansMoinsDeJours: Int? = nil for carte in cartes { let tempsDeRetard = carte.dateProchaineRevision.timeIntervalSince(maintenant) let joursDeRetard = Int(ceil(tempsDeRetard / (60 * 60 * 24))) // Convertit les secondes en jours en arrondissant à l'entier supérieur if joursDeRetard <= jours { if prochaineRevisionDansMoinsDeJours == nil || joursDeRetard < prochaineRevisionDansMoinsDeJours! { prochaineRevisionDansMoinsDeJours = joursDeRetard } } } return prochaineRevisionDansMoinsDeJours } } enum Etat { case Echec case Difficile case Bon case Facile } struct Session: Identifiable { var id = UUID() var date = Date() var etat: Etat private static var dateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "dd/MM/yy" return formatter }() var formattedDate: String { Session.dateFormatter.string(from: date) } } struct ContentView: View { @State private var selection: Int? = nil @State private var showNavigationBar = false // Etat pour suivre l'état de la navigation bar /* ## @State listes: [Liste] = [ Liste(nom: "Liste 1", cartes: [ Carte(devant: "Avant 1-1", derriere: "Derriere 1-1", dateProchaineRevision: Date()), Carte(devant: "Avant 1-2", derriere: "Derriere 1-2", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 3, to: Date())!), Carte(devant: "Avant 1-3", derriere: "Derriere 1-3", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 5, to: Date())!) ]), Liste(nom: "Liste 2", cartes: [ Carte(devant: "Avant 2-1", derriere: "Derriere 2-1", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 2, to: Date())!), Carte(devant: "Avant 2-2", derriere: "Derriere 2-2", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 4, to: Date())!), Carte(devant: "Avant 2-3", derriere: "Derriere 2-3", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 7, to: Date())!), Carte(devant: "Avant 2-4", derriere: "Derriere 2-4", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 10, to: Date())!), Carte(devant: "Avant 2-5", derriere: "Derriere 2-5", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 14, to: Date())!) ]), Liste(nom: "Liste 3", cartes: [ Carte(devant: "Avant 3-1", derriere: "Derriere 3-1", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 1, to: Date())!), Carte(devant: "Avant 3-2", derriere: "Derriere 3-2", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 6, to: Date())!) ]), Liste(nom: "Liste 4", cartes: [ Carte(devant: "Avant 4-1", derriere: "Derriere 4-1", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 8, to: Date())!), Carte(devant: "Avant 4-2", derriere: "Derriere 4-2", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 12, to: Date())!), Carte(devant: "Avant 4-3", derriere: "Derriere 4-3", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 15, to: Date())!) ]) ] */ @StateObject var allListes: AllListes = AllListes(listes: [ Liste(nom: "Liste 1", cartes: [ Carte(devant: "Avant 1-1", derriere: "Derriere 1-1", dateProchaineRevision: Date()), Carte(devant: "Avant 1-2", derriere: "Derriere 1-2", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 3, to: Date())!), Carte(devant: "Avant 1-3", derriere: "Derriere 1-3", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 5, to: Date())!) ]), Liste(nom: "Liste 2", cartes: [ Carte(devant: "Avant 2-1", derriere: "Derriere 2-1", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 2, to: Date())!), Carte(devant: "Avant 2-2", derriere: "Derriere 2-2", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 4, to: Date())!), Carte(devant: "Avant 2-3", derriere: "Derriere 2-3", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 7, to: Date())!), Carte(devant: "Avant 2-4", derriere: "Derriere 2-4", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 10, to: Date())!), Carte(devant: "Avant 2-5", derriere: "Derriere 2-5", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 14, to: Date())!) ]), Liste(nom: "Liste 3", cartes: [ Carte(devant: "Avant 3-1", derriere: "Derriere 3-1", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 1, to: Date())!), Carte(devant: "Avant 3-2", derriere: "Derriere 3-2", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 6, to: Date())!) ]), Liste(nom: "Liste 4", cartes: [ Carte(devant: "Avant 4-1", derriere: "Derriere 4-1", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 8, to: Date())!), Carte(devant: "Avant 4-2", derriere: "Derriere 4-2", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 12, to: Date())!), Carte(devant: "Avant 4-3", derriere: "Derriere 4-3", dateProchaineRevision: Calendar.current.date(byAdding: .day, value: 15, to: Date())!) ]) ]) // ## var body: some View { TabView(selection: $selection) { NavigationView { ScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: 150))], spacing: 20) { ForEach($allListes.listes) { $liste in // ## NavigationLink(destination: AfficherUneListe(listeID: $liste.id, showNavigationBar: $showNavigationBar).environmentObject(allListes)) { // ## VStack { RoundedRectangle(cornerRadius: 10) .foregroundColor(.white) .shadow(radius: 5) .overlay( VStack { Spacer() Text("\(liste.cartes.count)") .font(.headline) .foregroundColor(.blue) Text(liste.nom) .font(.footnote) .foregroundColor(.gray) Spacer() Text(liste.prochaineRevisionDansMoinsDe(99).map { "\($0) jours" } ?? "") .foregroundColor(.red) .font(.system(size: 14)) Spacer() } ) } .frame(width: 150, height: 150) } } } .navigationBarTitle("Accueil") .onAppear { withAnimation { showNavigationBar = true } } } } .tabItem { Image(systemName: "house") Text("Accueil") } .tag(0) NavigationView { SentrainerUIView() .navigationBarTitle("Créer") } .tabItem { Image(systemName: "play.circle") Text("S'entrainer") } .tag(1) NavigationView { CreeUneListeView(listes: $allListes.listes) .navigationBarTitle("Crée") } .tabItem { Image(systemName: "plus.circle") Text("Créer") } .tag(2) } .navigationBarTitle("LearnIO") } } struct ModifierUneCarte: View { @Binding var carte: Carte @State private var avant: String @State private var arriere: String init(carte: Binding) { self._carte = carte _avant = State(initialValue: carte.wrappedValue.devant) _arriere = State(initialValue: carte.wrappedValue.derriere) } var body: some View { VStack { TextEditor(text: $avant) .frame(height: 200) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) ) .padding() TextEditor(text: $arriere) .frame(height: 200) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) ) .padding() Button(action: { carte.devant = avant; carte.derriere = arriere; avant = "" arriere = "" }) { Text("Modifier la carte") .fontWeight(.bold) .font(.system(size: 20)) .foregroundColor(.white) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) .background( RoundedRectangle(cornerRadius: 10) .fill(Color.blue) ) ) } Button(action: { }) { Text("Modifier la carte") .fontWeight(.bold) .font(.system(size: 20)) .foregroundColor(.white) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) .background( RoundedRectangle(cornerRadius: 10) .fill(Color.blue) ) ) } Spacer() } .navigationTitle("Modifier une carte") } } struct ModifierUneListeView: View { @Binding var liste : Liste var body: some View { VStack { Spacer() TextField("Nom de la liste", text: $liste.nom) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) ) .padding() Spacer() Button(action: { }) { Text("Modifier la liste") .fontWeight(.bold) .font(.system(size: 20)) .foregroundColor(.white) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) .background( RoundedRectangle(cornerRadius: 10) .fill(Color.blue) ) ) } Spacer() } .navigationBarTitle("Modifier : " + liste.nom) } } struct AfficherUneListe: View { @EnvironmentObject var allListes: AllListes // ## var listeID: UUID? // ## // ## @Binding var liste : Liste @State var liste : Liste = Liste(nom: "", cartes: []) @Binding var showNavigationBar: Bool /* Do not do in init, environmentObject not yet there ; do in .onAppear https://www.hackingwithswift.com/forums/swiftui/environmentobject-usage-in-init-of-a-view/5795 init(listeID: UUID, showNavigationBar: Binding) { self._showNavigationBar = showNavigationBar for aList in allListes.listes where aList.id == listeID { self._liste = State(initialValue: aList) } }*/ var body: some View { Spacer() Text("Les cartes") .font(.headline) ScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: 5) { ForEach($liste.cartes) { $c in NavigationLink(destination: ModifierUneCarte(carte: $c)) { VStack { RoundedRectangle(cornerRadius: 10) .foregroundColor(.white) .shadow(radius: 3) .padding(5) // Ajouter un padding supplémentaire .overlay( VStack { Text(c.devant) .font(.system(size: 14)) Divider() Text(c.derriere) .font(.system(size: 14)) } ) } .frame(width: 100, height: 100) } } } } .navigationBarItems(trailing: HStack { Button(action: { // Code pour le premier bouton }) { Image(systemName: "play") } NavigationLink(destination: ModifierUneListeView(liste: $liste)) { Image(systemName: "gear") } Button(action: { // Code pour le deuxième bouton }) { Image(systemName: "trash") } }) .navigationTitle(liste.nom) .environmentObject(allListes) // ## .onAppear { // ## self.showNavigationBar = showNavigationBar for aList in allListes.listes where aList.id == listeID { self.liste = aList } } } } struct SentrainerUIView: View { var body: some View { NavigationView { VStack { Text("S'entainer") } } } } struct AjouterUneCarteView: View { @State private var avant = "" @State private var arriere = "" @Binding var liste: Liste var body: some View { VStack { TextEditor(text: $avant) .frame(height: 200) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) ) .padding() TextEditor(text: $arriere) .frame(height: 200) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) ) .padding() Button(action: { liste.cartes.append(Carte(devant: avant, derriere: arriere, dateProchaineRevision: Date())) avant = "" arriere = "" }) { Text("Ajouter la carte") .fontWeight(.bold) .font(.system(size: 20)) .foregroundColor(.white) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) .background( RoundedRectangle(cornerRadius: 10) .fill(Color.blue) ) ) } Spacer() } .navigationBarTitle("Crée") } } struct CreeUneListeView: View { @State private var nom = "" @State private var liste = Liste(nom: "", cartes: []) @Binding var listes: [Liste] var body: some View { VStack { Spacer() TextField("Nom de la liste", text: $nom) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) ) .padding() Spacer() Button(action: { listes.append(Liste(nom: nom, cartes: [])) nom = "" }) { Text("Créer la liste") .fontWeight(.bold) .font(.system(size: 20)) .foregroundColor(.white) .padding() .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.blue, lineWidth: 2) .background( RoundedRectangle(cornerRadius: 10) .fill(Color.blue) ) ) } Spacer() } .navigationBarTitle("Crée") } }