diff --git a/Schedule ICTIS/Assets.xcassets/greyForDaysInMonthTabView.colorset/Contents.json b/Schedule ICTIS/Assets.xcassets/greyForDaysInMonthTabView.colorset/Contents.json new file mode 100644 index 0000000..bfc2a11 --- /dev/null +++ b/Schedule ICTIS/Assets.xcassets/greyForDaysInMonthTabView.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x99", + "green" : "0x99", + "red" : "0x99" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x99", + "green" : "0x99", + "red" : "0x99" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Schedule ICTIS/Helpers/Date+Extensions.swift b/Schedule ICTIS/Helpers/Date+Extensions.swift index e25fe44..c4256ca 100644 --- a/Schedule ICTIS/Helpers/Date+Extensions.swift +++ b/Schedule ICTIS/Helpers/Date+Extensions.swift @@ -25,13 +25,7 @@ extension Date { var isToday: Bool { return Calendar.current.isDateInToday(self) } - - private func isSameDate(_ date1: Date?, _ date2: Date?) -> Bool { - guard let date1 = date1, let date2 = date2 else { return false } - let calendar = Calendar.current - return calendar.isDate(date1, inSameDayAs: date2) - } - + func fetchWeek(_ date: Date = .init()) -> [WeekDay] { let calendar = Calendar.current let startOfDate = calendar.startOfDay(for: date) @@ -56,6 +50,31 @@ extension Date { return week } + func fetchMonth(_ date: Date = .init()) -> [MonthWeek] { + let calendar = Calendar.current + let startOfDate = calendar.startOfDay(for: date) + + let weekForDate = calendar.dateInterval(of: .weekOfMonth, for: startOfDate) + + guard let startOfWeek = weekForDate?.start else { + return [] + } + + var month: [MonthWeek] = [] + + for weekIndex in 0..<5 { + var week: [WeekDay] = [] + for dayIndex in 0..<7 { + if let weekDay = calendar.date(byAdding: .day, value: (weekIndex * 7 + dayIndex), to: startOfWeek) { + week.append(WeekDay(date: weekDay)) + } + } + month.append(MonthWeek(week: week)) + } + + return month + } + func createNextWeek() -> [WeekDay] { let calendar = Calendar.current let startOfLastDate = calendar.startOfDay(for: self) @@ -78,4 +97,9 @@ extension Date { var id: UUID = .init() var date: Date } + + struct MonthWeek: Identifiable { + var id: UUID = .init() + var week: [WeekDay] + } } diff --git a/Schedule ICTIS/Helpers/View+Extensions.swift b/Schedule ICTIS/Helpers/View+Extensions.swift index d1ad692..07bd8e9 100644 --- a/Schedule ICTIS/Helpers/View+Extensions.swift +++ b/Schedule ICTIS/Helpers/View+Extensions.swift @@ -11,4 +11,17 @@ extension View { func isSameDate(_ date1: Date, _ date2: Date) -> Bool { return Calendar.current.isDate(date1, inSameDayAs: date2) } + + func isDateInCurrentMonth(_ date: Date) -> Bool { + let calendar = Calendar.current + let currentDate = Date() + + let currentMonth = calendar.component(.month, from: currentDate) + let currentYear = calendar.component(.year, from: currentDate) + + let dateMonth = calendar.component(.month, from: date) + let dateYear = calendar.component(.year, from: date) + + return currentMonth == dateMonth && currentYear == dateYear + } } diff --git a/Schedule ICTIS/Main/FirstLaunchScheduleView.swift b/Schedule ICTIS/Main/FirstLaunchScheduleView.swift index b3d591c..92e3d77 100644 --- a/Schedule ICTIS/Main/FirstLaunchScheduleView.swift +++ b/Schedule ICTIS/Main/FirstLaunchScheduleView.swift @@ -9,15 +9,7 @@ import SwiftUI struct FirstLaunchScheduleView: View { var body: some View { - VStack (alignment: .center) { - Spacer() - HStack { - Image(systemName: "pencil") - .font(.title) - Text("Введите свою группу") - .font(.system(size: 20, weight: .bold, design: .default)) - } - .foregroundColor(Color("blueColor")) + VStack () { Spacer() } } diff --git a/Schedule ICTIS/Main/MainView.swift b/Schedule ICTIS/Main/MainView.swift index 2c4abb9..e44e294 100644 --- a/Schedule ICTIS/Main/MainView.swift +++ b/Schedule ICTIS/Main/MainView.swift @@ -16,7 +16,7 @@ struct MainView: View { @State private var isShowingMonthSlider: Bool = false @State private var isFirstAppearence = true @ObservedObject var vm: ViewModel - + var body: some View { VStack { SearchBarView(text: $searchText, vm: vm) @@ -39,13 +39,13 @@ struct MainView: View { vm.updateSelectedDayIndex(currentDate) if weekSlider.isEmpty { let currentWeek = Date().fetchWeek(vm.selectedDay) - + if let firstDate = currentWeek.first?.date { weekSlider.append(firstDate.createPrevioustWeek()) } - + weekSlider.append(currentWeek) - + if let lastDate = currentWeek.last?.date { weekSlider.append(lastDate.createNextWeek()) } @@ -69,14 +69,20 @@ struct MainView: View { .font(.system(size: 20, weight: .bold)) .foregroundStyle(Color("grayForDate")) Spacer() - HStack (spacing: 2) { - Text(isShowingMonthSlider ? "Свернуть" : "Развернуть") - .font(.system(size: 15, weight: .light)) - .foregroundStyle(Color.blue) - Image(isShowingMonthSlider ? "arrowup" : "arrowdown") - } - .onTapGesture { - isShowingMonthSlider.toggle() + Button(action: { + withAnimation(.easeInOut(duration: 0.5)) { + isShowingMonthSlider.toggle() + } + }) { + HStack(spacing: 2) { + Text(isShowingMonthSlider ? "Свернуть" : "Развернуть") + .font(.system(size: 16, weight: .light)) + .foregroundStyle(Color.blue) + Image(isShowingMonthSlider ? "arrowup" : "arrowdown") + .resizable() + .scaledToFit() + .frame(width: 15, height: 15) // Установите размер изображения + } } } } @@ -84,126 +90,19 @@ struct MainView: View { .padding(.leading, 5) Spacer() } - - TabView(selection: $currentWeekIndex) { - ForEach(weekSlider.indices, id: \.self) { index in - let week = weekSlider[index] - WeekView(week) - .padding(.horizontal, 15) - .tag(index) - } + if (!isShowingMonthSlider) { + WeekTabView(currentWeekIndex: $currentWeekIndex, weekSlider: $weekSlider, currentDate: $currentDate, vm: vm) + .transition(.opacity) } - .padding(.horizontal, -15) - .tabViewStyle(.page(indexDisplayMode: .never)) - .frame(height: 90) - } - .onChange(of: currentWeekIndex, initial: false) { oldValue, newValue in - if newValue == 0 || newValue == (weekSlider.count - 1) { - createWeek = true + else { + MonthTabView(vm: vm) + .transition(.opacity) } } .padding(.horizontal) - } - - @ViewBuilder - func WeekView(_ week: [Date.WeekDay]) -> some View { - HStack (spacing: 10) { - ForEach(week) { day in - VStack (spacing: 1) { - Text(day.date.format("E")) - .font(.system(size: 15, weight: .semibold)) - .foregroundColor(day.date.format("E") == "Вс" ? Color(.red) : isSameDate(day.date, currentDate) ? Color("customGray1") : Color("customGray3")) - .padding(.top, 13) - .foregroundColor(.gray) - Text(day.date.format("dd")) - .font(.system(size: 15, weight: .bold)) - .foregroundStyle(isSameDate(day.date, currentDate) ? .white : .black) - .padding(.bottom, 13) - } - .frame(width: 43, height: 55, alignment: .center) - .background( content: { - Group { - if isSameDate(day.date, currentDate) { - Color("blueColor") - } - else { - Color(.white) - } - if isSameDate(day.date, currentDate) { - Color("blueColor") - } - } - } - ) - .overlay ( - Group { - if day.date.isToday && !isSameDate(day.date, currentDate) { - RoundedRectangle(cornerRadius: 15) - .stroke(Color("blueColor"), lineWidth: 2) - } - } - ) - .cornerRadius(15) - .onTapGesture { - currentDate = day.date - vm.updateSelectedDayIndex(currentDate) - } - } - } - .background { - GeometryReader { - let minX = $0.frame(in: .global).minX - - Color.clear - .preference(key: OffsetKey.self, value: minX) - .onPreferenceChange(OffsetKey.self) { value in - if value.rounded() == 15 && createWeek { - paginateWeek() - - createWeek = false - } - } - } - } - } - - func paginateWeek() { - let calendar = Calendar.current - if weekSlider.indices.contains(currentWeekIndex) { - if let firstDate = weekSlider[currentWeekIndex].first?.date, - currentWeekIndex == 0 { - switch (vm.numOfGroup) { - case "": - vm.week -= 1 - default: - vm.fetchWeekSchedule("new week", -1) - } - weekSlider.insert(firstDate.createPrevioustWeek(), at: 0) - weekSlider.removeLast() - currentWeekIndex = 1 - vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: -1, to: vm.selectedDay) ?? Date.init() - currentDate = vm.selectedDay - } - - if let lastDate = weekSlider[currentWeekIndex].last?.date, - currentWeekIndex == (weekSlider.count - 1) { - switch (vm.numOfGroup) { - case "": - vm.week += 1 - default: - vm.fetchWeekSchedule("new week", 1) - } - weekSlider.append(lastDate.createNextWeek()) - weekSlider.removeFirst() - currentWeekIndex = weekSlider.count - 2 - vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: 1, to: vm.selectedDay) ?? Date.init() - currentDate = vm.selectedDay - print(currentDate) - } - } + .animation(.easeInOut(duration: 0.25), value: isShowingMonthSlider) } } - #Preview { ContentView() } diff --git a/Schedule ICTIS/Main/ScheduleView.swift b/Schedule ICTIS/Main/ScheduleView.swift index 3bcac4c..212ae75 100644 --- a/Schedule ICTIS/Main/ScheduleView.swift +++ b/Schedule ICTIS/Main/ScheduleView.swift @@ -55,10 +55,12 @@ struct ScheduleView: View { .padding(.top, 30) } VStack { - Rectangle() - .frame(width: UIScreen.main.bounds.width, height: 25) - .foregroundColor(Color("background").opacity(0.9)) + LinearGradient(gradient: Gradient(colors: [Color("background").opacity(0.9), Color("background").opacity(0.89)]), startPoint: .top, endPoint: .bottom) +// Rectangle() +// .frame(width: UIScreen.main.bounds.width, height: 25) +// .foregroundColor(Color("background").opacity(0.9)) } + .frame(width: UIScreen.main.bounds.width, height: 15) } } diff --git a/Schedule ICTIS/Main/SearchBarView.swift b/Schedule ICTIS/Main/SearchBarView.swift index c52576d..abe37e9 100644 --- a/Schedule ICTIS/Main/SearchBarView.swift +++ b/Schedule ICTIS/Main/SearchBarView.swift @@ -53,7 +53,7 @@ struct SearchBarView: View { RoundedRectangle(cornerRadius: 10) .fill(.white) ) - if (!vm.isFirstStartOffApp && !vm.isShowingAlertForIncorrectGroup) { + if (!vm.isFirstStartOffApp) { Button { } label: { ZStack { diff --git a/Schedule ICTIS/Main/TabViews/MonthTabView.swift b/Schedule ICTIS/Main/TabViews/MonthTabView.swift new file mode 100644 index 0000000..539d24d --- /dev/null +++ b/Schedule ICTIS/Main/TabViews/MonthTabView.swift @@ -0,0 +1,112 @@ +// +// MonthTabView.swift +// Schedule ICTIS +// +// Created by G412 on 10.12.2024. +// + +import SwiftUI + +struct MonthTabView: View { + @State private var currentMonthIndex: Int = 1 + @State private var monthSlider: [[Date.MonthWeek]] = [] + @State private var createMonth: Bool = false + @State private var currentDate: Date = .init() + @ObservedObject var vm: ViewModel + var body: some View { + VStack { + HStack (spacing: 34) { + ForEach(MockData.daysOfWeek.indices, id: \.self) { index in + Text(MockData.daysOfWeek[index]) + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(MockData.daysOfWeek[index] == "Вс" ? Color(.red) : Color("customGray2")) + .padding(.top, 13) + .foregroundColor(.gray) + } + } + .padding(.top, 14) + //.background(Color.red) + TabView(selection: $currentMonthIndex) { + ForEach(monthSlider.indices, id: \.self) { index in + let month = monthSlider[index] + MonthView(month) + .tag(index) + } + } + .padding(.top, -25) + .padding(.bottom, -10) + .padding(.horizontal, -15) + .tabViewStyle(.page(indexDisplayMode: .never)) + //.background(Color.green) + } + .onChange(of: currentMonthIndex, initial: false) { oldValue, newValue in + if newValue == 0 || newValue == (monthSlider.count - 1) { + createMonth = true + } + } + .onAppear(perform: { + currentDate = vm.selectedDay + vm.updateSelectedDayIndex(currentDate) + if monthSlider.isEmpty { + let currentMonth = Date().fetchMonth(vm.selectedDay) + + monthSlider.append(currentMonth) + } + }) + } + + @ViewBuilder + func MonthView(_ month: [Date.MonthWeek]) -> some View { + VStack { + ForEach(month.indices, id: \.self) { index in + let week = month[index].week + WeekView(week) + } + } + } + + @ViewBuilder + func WeekView(_ week: [Date.WeekDay]) -> some View { + HStack (spacing: 23) { + ForEach(week) { day in + VStack { + Text(day.date.format("dd")) + .font(.system(size: 15, weight: .bold)) + .foregroundStyle(isDateInCurrentMonth(day.date) ? isSameDate(day.date, currentDate) ? Color.white : Color.black: isSameDate(day.date, currentDate) ? Color.white : Color("greyForDaysInMonthTabView")) + } + .frame(width: 30, height: 30, alignment: .center) + .background( content: { + Group { + if isSameDate(day.date, currentDate) { + Color("blueColor") + } + else { + Color("background") + } + if isSameDate(day.date, currentDate) { + Color("blueColor") + } + } + } + ) + .overlay ( + Group { + if day.date.isToday && !isSameDate(day.date, currentDate) { + RoundedRectangle(cornerRadius: 100) + .stroke(Color("blueColor"), lineWidth: 2) + } + } + ) + .cornerRadius(15) + .onTapGesture { + currentDate = day.date + vm.updateSelectedDayIndex(currentDate) + } + } + } + } +} + +#Preview { + ContentView() +} diff --git a/Schedule ICTIS/Main/TabViews/WeekTabView.swift b/Schedule ICTIS/Main/TabViews/WeekTabView.swift new file mode 100644 index 0000000..37a634b --- /dev/null +++ b/Schedule ICTIS/Main/TabViews/WeekTabView.swift @@ -0,0 +1,138 @@ +// +// WeekTabView.swift +// Schedule ICTIS +// +// Created by G412 on 10.12.2024. +// + +import SwiftUI + +struct WeekTabView: View { + @Binding var currentWeekIndex: Int + @Binding var weekSlider: [[Date.WeekDay]] + @Binding var currentDate: Date + @State private var createWeek: Bool = false + @ObservedObject var vm: ViewModel + var body: some View { + HStack { + TabView(selection: $currentWeekIndex) { + ForEach(weekSlider.indices, id: \.self) { index in + let week = weekSlider[index] + WeekView(week) + .padding(.horizontal, 15) + .tag(index) + } + } + .padding(.horizontal, -15) + .tabViewStyle(.page(indexDisplayMode: .never)) + .frame(height: 90) + } + .onChange(of: currentWeekIndex, initial: false) { oldValue, newValue in + if newValue == 0 || newValue == (weekSlider.count - 1) { + createWeek = true + } + } + } + + @ViewBuilder + func WeekView(_ week: [Date.WeekDay]) -> some View { + HStack (spacing: 10) { + ForEach(week) { day in + VStack (spacing: 1) { + Text(day.date.format("E")) + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(day.date.format("E") == "Вс" ? Color(.red) : isSameDate(day.date, currentDate) ? Color("customGray1") : Color("customGray3")) + .padding(.top, 13) + .foregroundColor(.gray) + Text(day.date.format("dd")) + .font(.system(size: 15, weight: .bold)) + .foregroundStyle(isSameDate(day.date, currentDate) ? .white : .black) + .padding(.bottom, 13) + } + .frame(width: 43, height: 55, alignment: .center) + .background( content: { + Group { + if isSameDate(day.date, currentDate) { + Color("blueColor") + } + else { + Color(.white) + } + if isSameDate(day.date, currentDate) { + Color("blueColor") + } + } + } + ) + .overlay ( + Group { + if day.date.isToday && !isSameDate(day.date, currentDate) { + RoundedRectangle(cornerRadius: 15) + .stroke(Color("blueColor"), lineWidth: 2) + } + } + ) + .cornerRadius(15) + .onTapGesture { + currentDate = day.date + vm.updateSelectedDayIndex(currentDate) + } + } + } + .background { + GeometryReader { + let minX = $0.frame(in: .global).minX + + Color.clear + .preference(key: OffsetKey.self, value: minX) + .onPreferenceChange(OffsetKey.self) { value in + if value.rounded() == 15 && createWeek { + paginateWeek() + + createWeek = false + } + } + } + } + } + + func paginateWeek() { + let calendar = Calendar.current + if weekSlider.indices.contains(currentWeekIndex) { + if let firstDate = weekSlider[currentWeekIndex].first?.date, + currentWeekIndex == 0 { + switch (vm.numOfGroup) { + case "": + vm.week -= 1 + default: + vm.fetchWeekSchedule("new week", -1) + } + weekSlider.insert(firstDate.createPrevioustWeek(), at: 0) + weekSlider.removeLast() + currentWeekIndex = 1 + vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: -1, to: vm.selectedDay) ?? Date.init() + currentDate = vm.selectedDay + } + + if let lastDate = weekSlider[currentWeekIndex].last?.date, + currentWeekIndex == (weekSlider.count - 1) { + switch (vm.numOfGroup) { + case "": + vm.week += 1 + default: + vm.fetchWeekSchedule("new week", 1) + } + weekSlider.append(lastDate.createNextWeek()) + weekSlider.removeFirst() + currentWeekIndex = weekSlider.count - 2 + vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: 1, to: vm.selectedDay) ?? Date.init() + currentDate = vm.selectedDay + print(currentDate) + } + } + } +} + +#Preview { + ContentView() +} diff --git a/Schedule ICTIS/MockData.swift b/Schedule ICTIS/MockData.swift index 76785cd..7dd0c08 100644 --- a/Schedule ICTIS/MockData.swift +++ b/Schedule ICTIS/MockData.swift @@ -9,6 +9,7 @@ import Foundation struct MockData { + static let daysOfWeek = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"] static let onlineClasses: [String] = [ "пр.Академический курс иностранного языка Янкаускас Е. С. LMS", "пр.Академический курс иностранного языка Янкаускас Е. С. LMS-3",