From 632ab8342fd8b9f7c12aa7e584dac40b9e036131 Mon Sep 17 00:00:00 2001 From: rwrun Date: Tue, 30 May 2023 14:33:40 +0500 Subject: [PATCH] add calendar --- smartClass.xcodeproj/project.pbxproj | 16 ++ .../Blue.colorset/Contents.json | 38 ++++ .../Gray.colorset/Contents.json | 38 ++++ .../Red.colorset/Contents.json | 38 ++++ smartClass/CalendarView.swift | 178 ++++++++++++++++++ smartClass/CalendarViewModel.swift | 87 +++++++++ smartClass/ContentView.swift | 9 +- smartClass/Extension/Color.ext.swift | 17 ++ smartClass/Extension/Date.ext.swift | 58 ++++++ smartClass/Extension/View.ext.swift | 58 ++++++ 10 files changed, 529 insertions(+), 8 deletions(-) create mode 100644 smartClass/Assets.xcassets/Blue.colorset/Contents.json create mode 100644 smartClass/Assets.xcassets/Gray.colorset/Contents.json create mode 100644 smartClass/Assets.xcassets/Red.colorset/Contents.json create mode 100644 smartClass/CalendarView.swift create mode 100644 smartClass/CalendarViewModel.swift create mode 100644 smartClass/Extension/Color.ext.swift create mode 100644 smartClass/Extension/Date.ext.swift diff --git a/smartClass.xcodeproj/project.pbxproj b/smartClass.xcodeproj/project.pbxproj index 16ead9d..0c7bc10 100644 --- a/smartClass.xcodeproj/project.pbxproj +++ b/smartClass.xcodeproj/project.pbxproj @@ -15,6 +15,10 @@ A8700E052A1DDC2A00D60253 /* FontManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8700E042A1DDC2A00D60253 /* FontManager.swift */; }; A8700E082A1DDC6100D60253 /* CustomFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8700E072A1DDC6100D60253 /* CustomFont.swift */; }; A8700E0B2A1DDCAD00D60253 /* View.ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8700E0A2A1DDCAD00D60253 /* View.ext.swift */; }; + A8700E272A24B71500D60253 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8700E262A24B71500D60253 /* CalendarView.swift */; }; + A8700E292A24B96500D60253 /* Date.ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8700E282A24B96500D60253 /* Date.ext.swift */; }; + A8700E2B2A24BA2100D60253 /* CalendarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8700E2A2A24BA2100D60253 /* CalendarViewModel.swift */; }; + A8700E2D2A24BBA900D60253 /* Color.ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8700E2C2A24BBA900D60253 /* Color.ext.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -28,6 +32,10 @@ A8700E042A1DDC2A00D60253 /* FontManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontManager.swift; sourceTree = ""; }; A8700E072A1DDC6100D60253 /* CustomFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFont.swift; sourceTree = ""; }; A8700E0A2A1DDCAD00D60253 /* View.ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.ext.swift; sourceTree = ""; }; + A8700E262A24B71500D60253 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; + A8700E282A24B96500D60253 /* Date.ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.ext.swift; sourceTree = ""; }; + A8700E2A2A24BA2100D60253 /* CalendarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewModel.swift; sourceTree = ""; }; + A8700E2C2A24BBA900D60253 /* Color.ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.ext.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,7 +73,9 @@ A8700E032A1DDBFC00D60253 /* Info.plist */, A8700E002A1DDB6B00D60253 /* Fonts */, A8700DF22A1DDAD500D60253 /* smartClassApp.swift */, + A8700E2A2A24BA2100D60253 /* CalendarViewModel.swift */, A8700DF42A1DDAD500D60253 /* ContentView.swift */, + A8700E262A24B71500D60253 /* CalendarView.swift */, A8700DF62A1DDAD600D60253 /* Assets.xcassets */, A8700DF82A1DDAD600D60253 /* Preview Content */, ); @@ -101,6 +111,8 @@ isa = PBXGroup; children = ( A8700E0A2A1DDCAD00D60253 /* View.ext.swift */, + A8700E282A24B96500D60253 /* Date.ext.swift */, + A8700E2C2A24BBA900D60253 /* Color.ext.swift */, ); path = Extension; sourceTree = ""; @@ -176,10 +188,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A8700E2B2A24BA2100D60253 /* CalendarViewModel.swift in Sources */, A8700DF52A1DDAD500D60253 /* ContentView.swift in Sources */, A8700E082A1DDC6100D60253 /* CustomFont.swift in Sources */, + A8700E272A24B71500D60253 /* CalendarView.swift in Sources */, A8700E052A1DDC2A00D60253 /* FontManager.swift in Sources */, + A8700E292A24B96500D60253 /* Date.ext.swift in Sources */, A8700DF32A1DDAD500D60253 /* smartClassApp.swift in Sources */, + A8700E2D2A24BBA900D60253 /* Color.ext.swift in Sources */, A8700E0B2A1DDCAD00D60253 /* View.ext.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/smartClass/Assets.xcassets/Blue.colorset/Contents.json b/smartClass/Assets.xcassets/Blue.colorset/Contents.json new file mode 100644 index 0000000..096abf2 --- /dev/null +++ b/smartClass/Assets.xcassets/Blue.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.945", + "green" : "0.431", + "red" : "0.149" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.945", + "green" : "0.431", + "red" : "0.149" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/smartClass/Assets.xcassets/Gray.colorset/Contents.json b/smartClass/Assets.xcassets/Gray.colorset/Contents.json new file mode 100644 index 0000000..357827d --- /dev/null +++ b/smartClass/Assets.xcassets/Gray.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.957", + "green" : "0.957", + "red" : "0.957" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.957", + "green" : "0.957", + "red" : "0.957" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/smartClass/Assets.xcassets/Red.colorset/Contents.json b/smartClass/Assets.xcassets/Red.colorset/Contents.json new file mode 100644 index 0000000..9edde74 --- /dev/null +++ b/smartClass/Assets.xcassets/Red.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.290", + "green" : "0.290", + "red" : "0.996" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.290", + "green" : "0.290", + "red" : "0.996" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/smartClass/CalendarView.swift b/smartClass/CalendarView.swift new file mode 100644 index 0000000..2322045 --- /dev/null +++ b/smartClass/CalendarView.swift @@ -0,0 +1,178 @@ +// +// CalendarView.swift +// smartClass +// +// Created by Ислам Батыргереев on 29.05.2023. +// + +import SwiftUI + +struct CalendarView: View { + let daysName = ["пн","вт","ср","чт","пт","сб","вс"] + @ObservedObject var calendarViewModel = CalendarViewModel() + @State var curentMonth: Int = 0 + @State var currentDate = Date() + let calendarGrid = Array(repeating: GridItem(.flexible()), count: 7) + @State var today = Date() + + var body: some View { + + VStack(alignment: .leading){ + Text("Календарь") + .customFont(font: FontManager.main) + .padding(.leading, 20) + .foregroundColor(.gray) + VStack{ + + VStack(spacing: 30){ + VStack(spacing: 24){ + HStack{ + //left + HStack{ + Button { + curentMonth -= 1 + } label: { + Image(systemName: "chevron.left") + .foregroundColor(.black) + } + + } + Spacer() + VStack{ + Text(extraDate(currentDate: currentDate)[1]) + .customFont(font: FontManager.Nunito.bold, size: 14) + Text(extraDate(currentDate: currentDate)[0]) + .customFont(font: FontManager.Nunito.bold, size: 20) + .foregroundColor(.mBlue) + } + Spacer() + //right + HStack{ + Button { + curentMonth += 1 + } label: { + Image(systemName: "chevron.right") + .foregroundColor(.black) + } + + } + } + .padding(.horizontal, 20) + + HStack(spacing: 0){ + ForEach(daysName, id: \.self) { day in + Text(day) + .frame(maxWidth: .infinity) + .customFont(font: FontManager.Nunito.black) + } + } + } + + LazyVGrid(columns: calendarGrid, spacing: 10) { + ForEach(extractDate(curentMonth: curentMonth)) { value in + CardView(value: value) + .environmentObject(calendarViewModel) + .overlay { + if sameDate(date1: value.date, date2: today), value.day != -1 { + ZStack{ + Circle() + .stroke(Color.mBlue, lineWidth: 4) + .frame(width: 35, height: 35) + } + .zIndex(0) + } + } + .onTapGesture { + today = value.date + currentDate = value.date + // + } + } + } + .onChange(of: curentMonth) { newValue in + currentDate = getCurrenMonth(curentMonth: curentMonth) + today = Date() + } + } + + } + .padding(.vertical, 20) + .padding(.horizontal, 10) + .background(Color.mGray) + .cornerRadius(20) + + } + .padding() + + + } +} + + +struct CardView: View{ + @ObservedObject var calendarViewModel = CalendarViewModel() + var value: DateValue + + var body: some View{ + VStack{ + if value.day != -1 { + + if let selectDay = calendarViewModel.availableDays.first(where: { day in + return sameDate(date1: day.date, date2: value.date) + }){ + ZStack{ + Circle() + .frame(width: 35, height: 35) + .clipShape(Circle()) + .foregroundColor(Color.mBlue) + + Text("\(value.day)") + .foregroundColor(.white) + .overlay { + ZStack{ + Circle() + .frame(width: 20, height: 20) + .foregroundColor(Color.mRed) + Text("\(selectDay.times.count)") + .customFont(font: FontManager.main, size: 14) + .foregroundColor(.white) + } + .offset(x: -15, y: -15) + } + } + } else { + Text("\(value.day)") + .frame(width: 35, height: 35) + .clipShape(Circle()) + } + } + } + } +} + + +struct CalendarView_Previews: PreviewProvider { + static var previews: some View { + CalendarView() + } +} + + +struct DateValue: Identifiable{ + var id = UUID().uuidString + var day: Int + var date: Date + var isTime: Bool = false + var lessons: [Lesson] = [] +} + +struct Lesson: Identifiable, Equatable{ + var id = UUID().uuidString + var date: Date + var times: [Times] +} + +struct Times: Identifiable, Equatable{ + var id = UUID().uuidString + var time: String +} diff --git a/smartClass/CalendarViewModel.swift b/smartClass/CalendarViewModel.swift new file mode 100644 index 0000000..039982f --- /dev/null +++ b/smartClass/CalendarViewModel.swift @@ -0,0 +1,87 @@ +// +// CalendarViewModel.swift +// smartClass +// +// Created by Ислам Батыргереев on 29.05.2023. +// + +import Foundation + +class CalendarViewModel: ObservableObject{ + @Published var availableDays = [Lesson]() + + init(){ + self.availableDays = getDateForMonth(moth: 0) + } + + + func addDayTime(day: Date, time: Times){ + if let toDay = checkToday(days: availableDays, completion: { date in + return sameDate(date1: day, date2: date) + }) { + if let index = availableDays.firstIndex(of: toDay){ + availableDays[index].times.append(time) + } + } else { + let newDate = Lesson(date: day, times: [time]) + availableDays.append(newDate) + } + + } + + func getDateForMonth(moth: Int) -> [Lesson]{ + return [ + Lesson(date: getSumpleDate(1), times: [ + Times(time: "18:00"), + Times(time: "19:00"), + Times(time: "20:00"), + Times(time: "21:00") + ]), + Lesson(date: getSumpleDate(3), times: [ + Times(time: "18:00"), + Times(time: "19:00"), + Times(time: "21:00") + ]), + Lesson(date: getSumpleDate(4), times: [ + Times(time: "18:00"), + Times(time: "19:00"), + Times(time: "21:00") + ]), + Lesson(date: getSumpleDate(6), times: [ + Times(time: "17:00"), + Times(time: "19:00"), + Times(time: "21:00") + ]), + Lesson(date: getSumpleDate(10), times: [ + Times(time: "17:00"), + Times(time: "19:00") + ]) + ] + } + + + private func checkToday(days: [Lesson], completion: (Date) -> (Bool)) -> Lesson?{ + + var resultDates: Lesson? + for day in days { + if completion(day.date) { + resultDates = day + } + } + return resultDates + } + + + private func sameDate(date1: Date, date2: Date) -> Bool{ + let calendar = Calendar.current + + return calendar.isDate(date1, equalTo: date2, toGranularity: .day) + //return calendar.isDate(date1, inSameDayAs: date2) + } +} + +func getSumpleDate(_ offset: Int) -> Date{ + let calendar = Calendar.current + let date = calendar.date(byAdding: .day, value: offset, to: Date()) + return date ?? Date() +} diff --git a/smartClass/ContentView.swift b/smartClass/ContentView.swift index ce69487..de4383d 100644 --- a/smartClass/ContentView.swift +++ b/smartClass/ContentView.swift @@ -9,14 +9,7 @@ import SwiftUI struct ContentView: View { var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundColor(.accentColor) - Text("Hello, world!") - .customFont(font: FontManager.Nunito.black, size: 20) - } - .padding() + CalendarView() } } diff --git a/smartClass/Extension/Color.ext.swift b/smartClass/Extension/Color.ext.swift new file mode 100644 index 0000000..a3a0f42 --- /dev/null +++ b/smartClass/Extension/Color.ext.swift @@ -0,0 +1,17 @@ +// +// Color.ext.swift +// smartClass +// +// Created by Ислам Батыргереев on 29.05.2023. +// + +import SwiftUI + +extension Color{ + + static var mBlue = Color("Blue") + static var mRed = Color("Red") + static var mGray = Color("Gray") + + +} diff --git a/smartClass/Extension/Date.ext.swift b/smartClass/Extension/Date.ext.swift new file mode 100644 index 0000000..0f2f23e --- /dev/null +++ b/smartClass/Extension/Date.ext.swift @@ -0,0 +1,58 @@ +// +// Date.ext.swift +// smartClass +// +// Created by Ислам Батыргереев on 29.05.2023. +// + +import Foundation + +extension Date{ + + func formatDate() -> String{ + //dd.MM.yy + let formatter = DateFormatter() + formatter.dateFormat = "dd MMM yyyy" + + return formatter.string(from: self) + } + + func formatTime() -> String{ + //dd.MM.yy + let formatter = DateFormatter() + formatter.dateFormat = "HH:mm" + + return formatter.string(from: self) + } + + func getAllDates() -> [Date]{ + let calendar = Calendar.current + + let startDate = calendar.date(from: Calendar.current.dateComponents([.year, .month], from: self))! + + let range = calendar.range(of: .day, in: .month, for: startDate)! + + //range.removeLast() + + return range.compactMap { day -> Date in + return calendar.date(byAdding: .day, value: day - 1, to: startDate)! + } + + } + + + func startOfMonth() -> Date { + return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: self))! + } + + + func endOfMonth() -> Date { + return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())! + } + + + func getCurentDateStart()->Date{ + let date = Calendar.current.date(bySettingHour: 10, minute: 0, second: 0, of: self)! + return date + } +} diff --git a/smartClass/Extension/View.ext.swift b/smartClass/Extension/View.ext.swift index a993893..ac4bd62 100644 --- a/smartClass/Extension/View.ext.swift +++ b/smartClass/Extension/View.ext.swift @@ -11,5 +11,63 @@ extension View{ func customFont(font: String, size: CGFloat = 18) -> some View { modifier(CustomFont(font: font, size: size)) } + + + func sameDate(date1: Date, date2: Date) -> Bool{ + let calendar = Calendar.current + + return calendar.isDate(date1, equalTo: date2, toGranularity: .day) + //return calendar.isDate(date1, inSameDayAs: date2) + } + + func extraDate(currentDate: Date) -> [String]{ + let formatter = DateFormatter() + formatter.dateFormat = "MMMM YYYY" + + let data = formatter.string(from: currentDate) + + return data.components(separatedBy: " ") + } + + + func getCurrenMonth(curentMonth: Int) -> Date{ + + let calendar = Calendar.current + + guard let curentMonth = calendar.date(byAdding: .month, value: curentMonth, to: Date()) else { return Date() } + + return curentMonth + + } + + func extractDate(curentMonth: Int) -> [DateValue]{ + let calendar = Calendar.current + + + let curentMonth = getCurrenMonth(curentMonth: curentMonth) + + + var days = curentMonth.getAllDates().compactMap { date -> DateValue in + let day = calendar.component(.day, from: date) + + return DateValue(day: day, date: date.getCurentDateStart()) + } + + let firstDay = calendar.component(.weekday, from: curentMonth.startOfMonth()) + //let firstDay = calendar.component(.weekday, from: days.first?.date ?? Date()) + + if firstDay == 1 { + for _ in 0..<6{ + days.insert(DateValue(day: -1, date: Date()), at: 0) + } + } else { + for _ in 1..