/
mariamokeeva
/
myApp
Обзор
Документация
Войти
/
mariamokeeva
/
myApp
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Task.swift
346 строк
11 KB
mariamokeeva
upload files
15 апр 2025, 12:56
15 апр 2025, 12:56
095b37f
Код
Авторство
О чём код?
import SwiftUI import SwiftData @Model final class Habit { var id: UUID var name: String var reminderText: String? var reminderTime: Date? var completedDates: [Date] var selectedDate: Date? var color: Color { return .green } init( id: UUID = UUID(), name: String, reminderText: String? = nil, reminderTime: Date? = nil, completedDates: [Date] = [], selectedDate: Date? = nil ) { self.id = id self.name = name self.reminderText = reminderText self.reminderTime = reminderTime self.completedDates = completedDates self.selectedDate = selectedDate } } struct HabitsView: View { @Environment(\.modelContext) private var modelContext @Query private var habits: [Habit] @State private var showingAddHabit = false @State private var currentWeekStart: Date = Date().startOfWeek @State private var editingHabit: Habit? @State private var showingDeleteConfirmation = false @State private var habitToDelete: Habit? var body: some View { NavigationView { ZStack { Color(.systemGroupedBackground) .ignoresSafeArea() ScrollView { VStack(spacing: 16) { ForEach(habits) { habit in HabitCardView( habit: habit, weekStart: $currentWeekStart, onEdit: { editingHabit = $0 }, onDelete: { confirmDelete($0) } ) .transition(.opacity.combined(with: .slide)) } } .padding() } } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .principal) { Text("Привычки") .font(.system(size: 22, weight: .bold)) } ToolbarItem(placement: .navigationBarLeading) { weekNavigationButtons() } ToolbarItem(placement: .navigationBarTrailing) { addButton() } } .sheet(isPresented: $showingAddHabit) { AddEditHabitView { habit in modelContext.insert(habit) } } .sheet(item: $editingHabit) { habit in AddEditHabitView(habitToEdit: habit) { updatedHabit in // Обновление автоматически сохраняется SwiftData } } .alert("Удалить привычку?", isPresented: $showingDeleteConfirmation, presenting: habitToDelete) { habit in deleteAlertButtons(for: habit) } } } private func weekNavigationButtons() -> some View { HStack { Button(action: { changeWeek(by: -1) }) { Image(systemName: "chevron.left") } Button(action: { changeWeek(by: 1) }) { Image(systemName: "chevron.right") } } } private func addButton() -> some View { Button(action: { showingAddHabit = true }) { Image(systemName: "plus") } } private func deleteAlertButtons(for habit: Habit) -> some View { Group { Button("Удалить", role: .destructive) { modelContext.delete(habit) } Button("Отмена", role: .cancel) {} } } private func confirmDelete(_ habit: Habit) { habitToDelete = habit showingDeleteConfirmation = true } private func changeWeek(by weeks: Int) { withAnimation { currentWeekStart = Calendar.current.date(byAdding: .weekOfYear, value: weeks, to: currentWeekStart) ?? currentWeekStart } } } struct HabitCardView: View { @Bindable var habit: Habit @Binding var weekStart: Date var onEdit: (Habit) -> Void var onDelete: (Habit) -> Void var body: some View { VStack(alignment: .leading, spacing: 12) { headerView() weekView() } .background(Color(.secondarySystemBackground)) .cornerRadius(12) .shadow(color: Color.white.opacity(0.5), radius: 5, x: 0, y: 2) } private func headerView() -> some View { HStack { Text(habit.name) .font(.headline) Spacer() if habit.reminderTime != nil { Image(systemName: "bell.fill") .foregroundColor(habit.color) .font(.caption) } Menu { Button(action: { onEdit(habit) }) { Label("Редактировать", systemImage: "pencil") } Button(role: .destructive, action: { onDelete(habit) }) { Label("Удалить", systemImage: "trash") } } label: { Image(systemName: "ellipsis.circle") .foregroundColor(.secondary) } } .padding(.horizontal, 16) .padding(.top, 16) } private func weekView() -> some View { HStack { ForEach(0..<7, id: \.self) { day in dayView(for: day) } } .padding(.horizontal, 8) .padding(.bottom, 16) } private func dayView(for day: Int) -> some View { let date = Calendar.current.date(byAdding: .day, value: day, to: weekStart)! let isCompleted = habit.completedDates.contains { Calendar.current.isDate($0, inSameDayAs: date) } let isSelected = habit.selectedDate.map { Calendar.current.isDate($0, inSameDayAs: date) } ?? false return VStack(spacing: 4) { Text(dayOfWeekLetter(for: day)) .font(.caption2) .foregroundColor(.secondary) Button(action: { habit.selectedDate = date toggleCompletion(for: date) }) { ZStack { Circle() .fill(isCompleted ? habit.color : Color(.systemGray5)) .frame(width: 30, height: 30) Text(String(Calendar.current.component(.day, from: date))) .font(.system(size: 14, weight: .medium)) .foregroundColor(isCompleted ? .white : .primary) if isSelected { Circle() .stroke(Color.primary.opacity(0.3), lineWidth: 2) .frame(width: 36, height: 36) } } } } .frame(maxWidth: .infinity) } private func toggleCompletion(for date: Date) { if let index = habit.completedDates.firstIndex(where: { Calendar.current.isDate($0, inSameDayAs: date) }) { habit.completedDates.remove(at: index) } else { habit.completedDates.append(date) } } private func dayOfWeekLetter(for day: Int) -> String { let date = Calendar.current.date(byAdding: .day, value: day, to: weekStart)! let formatter = DateFormatter() formatter.locale = Locale(identifier: "ru_RU") formatter.dateFormat = "E" return String(formatter.string(from: date).prefix(1)) } } struct AddEditHabitView: View { @Environment(\.dismiss) var dismiss var onSave: (Habit) -> Void @State private var id: UUID @State private var name: String @State private var selectedColor: Color @State private var showReminder: Bool @State private var reminderTime: Date @State private var reminderText: String let colors: [Color] = [.red, .orange, .yellow, .green, .mint, .teal, .cyan, .blue, .indigo, .purple, .pink, .brown] init(habitToEdit: Habit? = nil, onSave: @escaping (Habit) -> Void) { self.onSave = onSave if let habit = habitToEdit { _id = State(initialValue: habit.id) _name = State(initialValue: habit.name) _selectedColor = State(initialValue: habit.color) _showReminder = State(initialValue: habit.reminderTime != nil) _reminderTime = State(initialValue: habit.reminderTime ?? Date()) _reminderText = State(initialValue: habit.reminderText ?? "") } else { _id = State(initialValue: UUID()) _name = State(initialValue: "") _selectedColor = State(initialValue: colors.randomElement() ?? .blue) _showReminder = State(initialValue: false) _reminderTime = State(initialValue: Date()) _reminderText = State(initialValue: "") } } var body: some View { NavigationView { Form { Section { TextField("Название привычки", text: $name) } Section(header: Text("Цвет")) { ColorPickerView(selectedColor: $selectedColor, colors: colors) } } .navigationTitle(navigationTitle) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Сохранить") { let habit = Habit( id: id, name: name, reminderText: showReminder ? reminderText : nil, reminderTime: showReminder ? reminderTime : nil ) onSave(habit) dismiss() } .disabled(name.isEmpty) } } } } private var navigationTitle: String { name.isEmpty ? "Новая привычка" : name } } struct ColorPickerView: View { @Binding var selectedColor: Color let colors: [Color] var body: some View { ScrollView(.horizontal, showsIndicators: false) { HStack { ForEach(colors, id: \.self) { color in Circle() .fill(color) .frame(width: 32, height: 32) .overlay( Circle() .stroke(selectedColor == color ? Color.primary : Color.clear, lineWidth: 3) ) .onTapGesture { selectedColor = color } .padding(4) } } } } } extension Date { var startOfWeek: Date { let calendar = Calendar.current let components = calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self) return calendar.date(from: components) ?? self } }