This commit is contained in:
Vladimir Dubovik 2025-02-06 13:28:57 +03:00
parent 06416138d9
commit 9f717d83df
17 changed files with 389 additions and 147 deletions

View File

@ -27,7 +27,7 @@ struct ContentView: View {
}
.tag(1)
Text("Settings")
SettingsView(vm: vm)
.tabItem {
Image(systemName: "gear")
Text("Настройки")
@ -35,6 +35,13 @@ struct ContentView: View {
.tag(2)
}
.accentColor(Color("blueColor"))
.onAppear {
let group = UserDefaults.standard.string(forKey: "group")
if let nameGroup = group {
vm.group = nameGroup
vm.fetchWeekSchedule(group: nameGroup)
}
}
}
}

View File

@ -22,15 +22,11 @@ struct MainView: View {
isFocusedSearchBar = false
}
}
if (vm.isFirstStartOffApp && vm.isLoading) {
CurrentDateView()
if vm.isLoading {
LoadingView(isLoading: $vm.isLoading)
}
else if (vm.isFirstStartOffApp) {
FirstLaunchScheduleView()
}
else {
CurrentDateView()
ScheduleView(vm: vm, isScrolling: $isScrolling)
}
}
@ -43,6 +39,12 @@ struct MainView: View {
.onTapGesture {
isFocusedSearchBar = false
}
.onAppear {
vm.group = UserDefaults.standard.string(forKey: "group") ?? "notSeted"
if vm.group != "notSeted" {
}
}
}
@ViewBuilder

View File

@ -11,6 +11,8 @@ struct ScheduleView: View {
@ObservedObject var vm: ScheduleViewModel
@FetchRequest(fetchRequest: ClassModel.all()) private var classes //Делаем запрос в CoreData и получаем список сохраненных пар
@State private var selectedClass: ClassModel? = nil
@State private var lastOffset: CGFloat = 0
@State private var scrollTimer: Timer? = nil
@Binding var isScrolling: Bool
var provider = ClassProvider.shared
var body: some View {
@ -73,17 +75,28 @@ struct ScheduleView: View {
.frame(width: UIScreen.main.bounds.width)
.padding(.bottom, 100)
.padding(.top, 30)
.background(GeometryReader { geometry in
Color.clear.preference(key: ViewOffsetKey.self, value: geometry.frame(in: .global).minY)
})
}
.onPreferenceChange(ViewOffsetKey.self) { offset in
if offset > 0 {
if offset != lastOffset {
// Скролл происходит
isScrolling = true
print("Сейчас скролл")
} else {
isScrolling = false
print("Scrolling ended")
// Останавливаем предыдущий таймер
scrollTimer?.invalidate()
// Запускаем новый таймер
scrollTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { _ in
// Скролл остановился
isScrolling = false
}
}
lastOffset = offset
}
.onDisappear {
scrollTimer?.invalidate()
}
.coordinateSpace(name: "scroll")
VStack {
LinearGradient(gradient: Gradient(colors: [Color("background").opacity(0.95), Color.white.opacity(0.1)]), startPoint: .top, endPoint: .bottom)
}

View File

@ -38,7 +38,6 @@ struct SearchBarView: View {
Button {
self.text = ""
self.isFocused = false
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
} label: {
Image(systemName: "xmark.circle.fill")
.padding(.trailing, 20)

View File

@ -8,8 +8,8 @@
import SwiftUI
struct MonthTabView: View {
@State private var currentMonthIndex: Int = 1
@State private var monthSlider: [[Date.MonthWeek]] = []
@State var currentMonthIndex: Int = 1
@State var monthSlider: [[Date.MonthWeek]] = []
@State private var createMonth: Bool = false
@State private var currentWeekIndex: Int = 0
@ObservedObject var vm: ScheduleViewModel
@ -79,53 +79,8 @@ struct MonthTabView: View {
}
}
}
func paginateMonth(_ indexOfWeek: Int = 0) {
let calendar = Calendar.current
if monthSlider.indices.contains(currentMonthIndex) {
if let firstDate = monthSlider[currentMonthIndex].first?.week[0].date,
currentMonthIndex == 0 {
monthSlider.insert(firstDate.createPreviousMonth(), at: 0)
monthSlider.removeLast()
currentMonthIndex = 1
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: -5, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex()
vm.week -= 5
vm.fetchWeekSchedule(isOtherWeek: true)
}
if let lastDate = monthSlider[currentMonthIndex].last?.week[6].date,
currentMonthIndex == (monthSlider.count - 1) {
monthSlider.append(lastDate.createNextMonth())
monthSlider.removeFirst()
currentMonthIndex = monthSlider.count - 2
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: 5, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex()
vm.week += 5
vm.fetchWeekSchedule(isOtherWeek: true)
}
}
}
}
extension MonthTabView {
func updateMonthScreenViewForNewGroup() {
vm.updateSelectedDayIndex()
if monthSlider.isEmpty {
let currentMonth = Date().fetchMonth(vm.selectedDay)
if let firstDate = currentMonth.first?.week[0].date {
monthSlider.append(firstDate.createPreviousMonth())
}
monthSlider.append(currentMonth)
if let lastDate = currentMonth.last?.week[6].date {
monthSlider.append(lastDate.createNextMonth())
}
}
}
}
#Preview {
ContentView()

View File

@ -9,7 +9,7 @@ import SwiftUI
struct WeekTabView: View {
@State private var currentWeekIndex: Int = 1
@State private var weekSlider: [[Date.WeekDay]] = []
@State var weekSlider: [[Date.WeekDay]] = []
@State private var createWeek: Bool = false
@ObservedObject var vm: ScheduleViewModel
var body: some View {
@ -45,25 +45,6 @@ struct WeekTabView: View {
}
}
extension WeekTabView {
func updateWeekScreenViewForNewGroup() {
vm.updateSelectedDayIndex()
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())
}
}
}
}
#Preview {
ContentView()
}

View File

@ -29,42 +29,4 @@ struct WeekViewForMonth: View {
}
}
}
private func getForegroundColor(day: Date.WeekDay) -> Color {
if isDateInCurrentMonth(day.date) {
return isSameDate(day.date, vm.selectedDay) ? .white : .black
} else {
return isSameDate(day.date, vm.selectedDay) ? .white : Color("greyForDaysInMonthTabView")
}
}
private func getBackgroundColor(day: Date.WeekDay) -> Color {
return isSameDate(day.date, vm.selectedDay) ? Color("blueColor") : Color("background")
}
private func overlay(day: Date.WeekDay) -> some View {
Group {
if day.date.isToday && !isSameDate(day.date, vm.selectedDay) {
RoundedRectangle(cornerRadius: 100)
.stroke(Color("blueColor"), lineWidth: 2)
}
}
}
private func handleTap(day: Date.WeekDay) {
if isSameWeek(day.date, vm.selectedDay) {
print("На одной неделе")
}
else {
var difBetweenWeeks = weeksBetween(startDate: vm.selectedDay, endDate: day.date)
if day.date < vm.selectedDay {
difBetweenWeeks = difBetweenWeeks * -1
}
print(difBetweenWeeks)
vm.week += difBetweenWeeks
vm.fetchWeekSchedule(isOtherWeek: true)
}
vm.selectedDay = day.date
vm.updateSelectedDayIndex()
}
}

View File

@ -73,30 +73,4 @@ struct WeekViewForWeek: View {
}
}
}
func paginateWeek() {
let calendar = Calendar.current
if weekSlider.indices.contains(currentWeekIndex) {
if let firstDate = weekSlider[currentWeekIndex].first?.date,
currentWeekIndex == 0 {
vm.week -= 1
vm.fetchWeekSchedule(isOtherWeek: true)
weekSlider.insert(firstDate.createPrevioustWeek(), at: 0)
weekSlider.removeLast()
currentWeekIndex = 1
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: -1, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex()
}
if let lastDate = weekSlider[currentWeekIndex].last?.date,
currentWeekIndex == (weekSlider.count - 1) {
vm.week += 1
vm.fetchWeekSchedule(isOtherWeek: true)
weekSlider.append(lastDate.createNextWeek())
weekSlider.removeFirst()
currentWeekIndex = weekSlider.count - 2
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: 1, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex()
}
}
}
}

View File

@ -15,4 +15,8 @@ struct MockData {
static let notifications = ["Нет", "За 10 минут", "За 30 миннут", "За 1 час"]
static let onlineOrOffline = ["Оффлайн", "Онлайн"]
static let themes = ["Светлая", "Темная", "Системная"]
static let languages = ["Русский", "Английский", "Китайский", "Испанский"]
}

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "arrowRight.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 6L15 12L9 18" stroke="#8B8B8B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 212 B

View File

@ -0,0 +1,68 @@
//
// SelectedGroupView.swift
// Schedule ICTIS
//
// Created by G412 on 30.01.2025.
//
import SwiftUI
struct SelectingGroupView: View {
@Environment(\.dismiss) private var dismiss
@FocusState private var isFocused: Bool
@State private var text: String = ""
@Binding var group: String
var body: some View {
NavigationView {
VStack {
HStack (spacing: 0) {
Image(systemName: "magnifyingglass")
.foregroundColor(Color.gray)
.padding(.leading, 12)
.padding(.trailing, 7)
TextField("Поиск группы", text: $text)
.disableAutocorrection(true)
.focused($isFocused)
.onSubmit {
self.isFocused = false
if (!text.isEmpty) {
UserDefaults.standard.set(text, forKey: "group")
group = text
}
self.text = ""
dismiss()
}
.submitLabel(.done)
if isFocused {
Button {
self.text = ""
self.isFocused = false
} label: {
Image(systemName: "xmark.circle.fill")
.padding(.trailing, 20)
.offset(x: 10)
.foregroundColor(.gray)
.background(
)
}
}
}
.frame(height: 40)
.background(
RoundedRectangle(cornerRadius: 15)
.fill(.white)
)
.padding(.horizontal, 10)
Spacer()
}
.background(Color("background"))
.onTapGesture {
self.isFocused = false
}
}
}
}
#Preview {
SelectingGroupView(group: .constant("КТбо2-6"))
}

View File

@ -0,0 +1,64 @@
//
// SelectedVPKView.swift
// Schedule ICTIS
//
// Created by G412 on 30.01.2025.
//
import SwiftUI
struct SelectingVPKView: View {
@FocusState private var isFocused: Bool
@State private var text: String = ""
var body: some View {
NavigationView {
VStack {
HStack (spacing: 0) {
Image(systemName: "magnifyingglass")
.foregroundColor(Color.gray)
.padding(.leading, 12)
.padding(.trailing, 7)
TextField("Поиск ВПК", text: $text)
.disableAutocorrection(true)
.focused($isFocused)
.onSubmit {
self.isFocused = false
if (!text.isEmpty) {
UserDefaults.standard.set(text, forKey: "group")
}
self.text = ""
}
.submitLabel(.done)
if isFocused {
Button {
self.text = ""
self.isFocused = false
} label: {
Image(systemName: "xmark.circle.fill")
.padding(.trailing, 20)
.offset(x: 10)
.foregroundColor(.gray)
.background(
)
}
}
}
.frame(height: 40)
.background(
RoundedRectangle(cornerRadius: 15)
.fill(.white)
)
.padding(.horizontal, 10)
Spacer()
}
.background(Color("background"))
.onTapGesture {
self.isFocused = false
}
}
}
}
#Preview {
SelectingVPKView()
}

View File

@ -0,0 +1,55 @@
//
// SettingsView.swift
// Schedule ICTIS
//
// Created by G412 on 30.01.2025.
//
import SwiftUI
struct SettingsView: View {
@ObservedObject var vm: ScheduleViewModel
@State private var selectedTheme = "Светлая"
@State private var selectedLanguage = "Русский"
var body: some View {
NavigationView {
VStack {
List {
Section("Общие") {
Picker("Тема", selection: $selectedTheme, content: {
ForEach(MockData.themes, id: \.self) {
Text($0)
}
})
Picker("Язык", selection: $selectedLanguage, content: {
ForEach(MockData.languages, id: \.self) {
Text($0)
}
})
}
Section("Расписание") {
NavigationLink(destination: SelectingGroupView(group: $vm.group)) {
LabeledContent {
Text(vm.group)
} label: {
Text("Избранное расписание")
}
}
NavigationLink(destination: SelectingVPKView()) {
LabeledContent {
} label: {
Text("ВПК")
}
}
}
}
}
.navigationTitle("Настройки")
}
}
}
#Preview {
@Previewable @StateObject var vm = ScheduleViewModel()
SettingsView(vm: vm)
}

View File

@ -160,3 +160,137 @@ extension View {
}
}
}
extension WeekTabView {
func updateWeekScreenViewForNewGroup() {
vm.updateSelectedDayIndex()
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())
}
}
}
}
extension WeekViewForWeek {
func paginateWeek() {
let calendar = Calendar.current
if weekSlider.indices.contains(currentWeekIndex) {
if let firstDate = weekSlider[currentWeekIndex].first?.date,
currentWeekIndex == 0 {
vm.week -= 1
vm.fetchWeekSchedule(isOtherWeek: true)
weekSlider.insert(firstDate.createPrevioustWeek(), at: 0)
weekSlider.removeLast()
currentWeekIndex = 1
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: -1, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex()
}
if let lastDate = weekSlider[currentWeekIndex].last?.date,
currentWeekIndex == (weekSlider.count - 1) {
vm.week += 1
vm.fetchWeekSchedule(isOtherWeek: true)
weekSlider.append(lastDate.createNextWeek())
weekSlider.removeFirst()
currentWeekIndex = weekSlider.count - 2
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: 1, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex()
}
}
}
}
extension WeekViewForMonth {
func getForegroundColor(day: Date.WeekDay) -> Color {
if isDateInCurrentMonth(day.date) {
return isSameDate(day.date, vm.selectedDay) ? .white : .black
} else {
return isSameDate(day.date, vm.selectedDay) ? .white : Color("greyForDaysInMonthTabView")
}
}
func getBackgroundColor(day: Date.WeekDay) -> Color {
return isSameDate(day.date, vm.selectedDay) ? Color("blueColor") : Color("background")
}
func overlay(day: Date.WeekDay) -> some View {
Group {
if day.date.isToday && !isSameDate(day.date, vm.selectedDay) {
RoundedRectangle(cornerRadius: 100)
.stroke(Color("blueColor"), lineWidth: 2)
}
}
}
func handleTap(day: Date.WeekDay) {
if isSameWeek(day.date, vm.selectedDay) {
print("На одной неделе")
}
else {
var difBetweenWeeks = weeksBetween(startDate: vm.selectedDay, endDate: day.date)
if day.date < vm.selectedDay {
difBetweenWeeks = difBetweenWeeks * -1
}
print(difBetweenWeeks)
vm.week += difBetweenWeeks
vm.fetchWeekSchedule(isOtherWeek: true)
}
vm.selectedDay = day.date
vm.updateSelectedDayIndex()
}
}
extension MonthTabView {
func updateMonthScreenViewForNewGroup() {
vm.updateSelectedDayIndex()
if monthSlider.isEmpty {
let currentMonth = Date().fetchMonth(vm.selectedDay)
if let firstDate = currentMonth.first?.week[0].date {
monthSlider.append(firstDate.createPreviousMonth())
}
monthSlider.append(currentMonth)
if let lastDate = currentMonth.last?.week[6].date {
monthSlider.append(lastDate.createNextMonth())
}
}
}
func paginateMonth(_ indexOfWeek: Int = 0) {
let calendar = Calendar.current
if monthSlider.indices.contains(currentMonthIndex) {
if let firstDate = monthSlider[currentMonthIndex].first?.week[0].date,
currentMonthIndex == 0 {
monthSlider.insert(firstDate.createPreviousMonth(), at: 0)
monthSlider.removeLast()
currentMonthIndex = 1
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: -5, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex()
vm.week -= 5
vm.fetchWeekSchedule(isOtherWeek: true)
}
if let lastDate = monthSlider[currentMonthIndex].last?.week[6].date,
currentMonthIndex == (monthSlider.count - 1) {
monthSlider.append(lastDate.createNextMonth())
monthSlider.removeFirst()
currentMonthIndex = monthSlider.count - 2
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: 5, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex()
vm.week += 5
vm.fetchWeekSchedule(isOtherWeek: true)
}
}
}
}

View File

@ -36,11 +36,11 @@ final class ScheduleViewModel: ObservableObject {
Task {
do {
var schedule: Schedule
// В этот if мы заходим только если пользователь перелистывает недели и нам известы номер группы(в html формате) и номер неделе, которая показывается пользователю
// В этот if мы заходим только если пользователь перелистывает недели и нам известы номер группы(в html формате) и номер недели, которая показывается пользователю
if (isOtherWeek || !isFirstStartOffApp) && (group == "default") {
schedule = try await NetworkManager.shared.getScheduleForOtherWeek(self.week, self.numOfGroup)
}
// В else мы заходим в том случае, если не знаем номера недели, которую нужно отобразить и номер группы(в html формате)
// В else мы заходим в том случае, если не знаем номер недели, которую нужно отобразить и номер группы(в html формате)
else {
schedule = try await NetworkManager.shared.getSchedule(group)
if (!self.isFirstStartOffApp) {