This commit is contained in:
Vladimir Dubovik 2025-04-04 11:01:33 +03:00
parent 14c229175c
commit edfe97c6dc
13 changed files with 169 additions and 69 deletions

View File

@ -0,0 +1,32 @@
//
// LoadingView.swift
// Schedule ICTIS
//
// Created by Mironov Egor on 11.12.2024.
//
import SwiftUI
struct ConnectingToNetworkView: View {
@State private var isAnimating = false
var body: some View {
VStack {
Text("Ожидание сети")
.font(.custom("Montserrat-Medium", fixedSize: 18))
Circle()
.trim(from: 0.2, to: 1.0)
.stroke(Color("blueColor"), lineWidth: 3)
.frame(width: 30, height: 30)
.rotationEffect(Angle(degrees: isAnimating ? 360 : 0))
.animation(
Animation.linear(duration: 0.6).repeatForever(autoreverses: false),
value: isAnimating
)
.onAppear { isAnimating = true }
}
}
}
#Preview {
ConnectingToNetworkView()
}

View File

@ -8,35 +8,70 @@
import SwiftUI import SwiftUI
struct ContentView: View { struct ContentView: View {
@State private var selectedTab: Int = 1 @State private var selectedTab: TabBarModel = .schedule
@State private var isTabBarHidden = false
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@ObservedObject var networkMonitor: NetworkMonitor @ObservedObject var networkMonitor: NetworkMonitor
var body: some View { var body: some View {
ZStack (alignment: .bottom) {
TabView(selection: $selectedTab) { TabView(selection: $selectedTab) {
Text("Tasks") Text("Tasks")
.tabItem { .tag(TabBarModel.tasks)
Image(systemName: "books.vertical")
Text("Задания")
}
.tag(0)
MainView(vm: vm, networkMonitor: networkMonitor) MainView(vm: vm, networkMonitor: networkMonitor)
.tabItem { .tag(TabBarModel.schedule)
Image(systemName: "house") .background {
Text("Расписание") if !isTabBarHidden {
HideTabBar {
print("TabBar is hidden")
isTabBarHidden = true
}
}
} }
.tag(1)
SettingsView(vm: vm) SettingsView(vm: vm, networkMonitor: networkMonitor)
.tabItem { .tag(TabBarModel.settings)
Image(systemName: "gear")
Text("Настройки")
} }
.tag(2) TabBarView(selectedTab: $selectedTab)
} }
.accentColor(Color("blueColor"))
.onAppear { .onAppear {
vm.fetchWeekSchedule() vm.fetchWeekSchedule()
} }
} }
} }
struct HideTabBar: UIViewRepresentable {
var result: () -> ()
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: .zero)
view.backgroundColor = .clear
DispatchQueue.main.async {
if let tabController = view.tabController {
tabController.tabBar.isHidden = true
result()
}
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
extension UIView {
var tabController: UITabBarController? {
if let controller = sequence(first: self, next: {
$0.next
}).first(where: { $0 is UITabBarController}) as? UITabBarController {
return controller
}
return nil
}
}
#Preview {
@Previewable @StateObject var vm1 = ScheduleViewModel()
@Previewable @StateObject var vm2 = NetworkMonitor()
ContentView(vm: vm1, networkMonitor: vm2)
}

View File

@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
struct NetworkErrorView: View { struct NetworkErrorView: View {
var message: String
var body: some View { var body: some View {
VStack { VStack {
Spacer() Spacer()
@ -15,17 +16,16 @@ struct NetworkErrorView: View {
Image(systemName: "wifi.slash") Image(systemName: "wifi.slash")
.font(.system(size: 60, weight: .light)) .font(.system(size: 60, weight: .light))
.frame(width: 70, height: 70) .frame(width: 70, height: 70)
Text("Восстановите подключение к интернету чтобы мы могли загрузить расписание") Text(message)
.font(.custom("Montserrat-Medium", fixedSize: 15)) .font(.custom("Montserrat-Medium", fixedSize: 15))
.padding(.top, 5) .padding(.top, 5)
} }
.padding(.horizontal, 30) .padding(.horizontal, 30)
.padding(.top, UIScreen.main.bounds.height/8)
Spacer() Spacer()
} }
} }
} }
#Preview { #Preview {
NetworkErrorView() NetworkErrorView(message: "Восстановите подключение к интернету чтобы мы смогли загрузить расписание")
} }

View File

@ -2,24 +2,28 @@
// LoadingView.swift // LoadingView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by Mironov Egor on 11.12.2024. // Created by Mironov Egor on 04.04.2025.
// //
import SwiftUI import SwiftUI
struct LoadingView: View { struct LoadingView: View {
@Binding var isLoading: Bool @State private var isAnimating = false
var body: some View { var body: some View {
ZStack { Circle()
Color("background") .trim(from: 0.2, to: 1.0)
.ignoresSafeArea() .stroke(Color("blueColor"), lineWidth: 3)
ProgressView() .frame(width: 30, height: 30)
.progressViewStyle(CircularProgressViewStyle(tint: .secondary)) .rotationEffect(Angle(degrees: isAnimating ? 360 : 0))
.scaleEffect(1) .animation(
} Animation.linear(duration: 0.6).repeatForever(autoreverses: false),
value: isAnimating
)
.onAppear { isAnimating = true }
} }
} }
#Preview { #Preview {
LoadingView(isLoading: .constant(true)) LoadingView()
} }

View File

@ -48,7 +48,7 @@ struct ScheduleView: View {
private var onlineContent: some View { private var onlineContent: some View {
Group { Group {
if vm.errorInNetwork == .timeout { if vm.errorInNetwork == .timeout {
NetworkErrorView() NetworkErrorView(message: "Проверьте подключение к интернету")
} else if vm.isLoading { } else if vm.isLoading {
LoadingScheduleView() LoadingScheduleView()
} else if vm.errorInNetwork != .invalidResponse { } else if vm.errorInNetwork != .invalidResponse {
@ -114,7 +114,8 @@ struct ScheduleView: View {
} else { } else {
let filteredSubjects = subjects.filter { $0.day == Int16(vm.selectedIndex) } let filteredSubjects = subjects.filter { $0.day == Int16(vm.selectedIndex) }
if (filteredSubjects.isEmpty || vm.week != 0) && !hasClassesToShow { if (filteredSubjects.isEmpty || vm.week != 0) && !hasClassesToShow {
NetworkErrorView() ConnectingToNetworkView()
.padding(.top, 100)
} else { } else {
ForEach(filteredSubjects, id: \.self) { subject in ForEach(filteredSubjects, id: \.self) { subject in
if (vm.showOnlyChoosenGroup == "Все" || subject.group == vm.showOnlyChoosenGroup) && vm.week == 0 { if (vm.showOnlyChoosenGroup == "Все" || subject.group == vm.showOnlyChoosenGroup) && vm.week == 0 {

View File

@ -8,7 +8,7 @@
import SwiftUI import SwiftUI
enum TabBarModel: String, CaseIterable { enum TabBarModel: String, CaseIterable {
case schedule = "house"
case tasks = "books.vertical" case tasks = "books.vertical"
case schedule = "house"
case settings = "gear" case settings = "gear"
} }

View File

@ -9,6 +9,7 @@ import SwiftUI
struct FavGroupsView: View { struct FavGroupsView: View {
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@ObservedObject var networkMonitor: NetworkMonitor
var firstFavGroup = (UserDefaults.standard.string(forKey: "group") ?? "") var firstFavGroup = (UserDefaults.standard.string(forKey: "group") ?? "")
var secondFavGroup = (UserDefaults.standard.string(forKey: "group2") ?? "") var secondFavGroup = (UserDefaults.standard.string(forKey: "group2") ?? "")
var thirdFavGroup = (UserDefaults.standard.string(forKey: "group3") ?? "") var thirdFavGroup = (UserDefaults.standard.string(forKey: "group3") ?? "")
@ -74,7 +75,7 @@ struct FavGroupsView: View {
HStack { HStack {
Spacer() Spacer()
if firstFavGroup == "" || secondFavGroup == "" || thirdFavGroup == "" { if firstFavGroup == "" || secondFavGroup == "" || thirdFavGroup == "" {
NavigationLink(destination: SelectingGroupView(vm: vm, firstFavGroup: firstFavGroup, secondFavGroup: secondFavGroup, thirdFavGroup: thirdFavGroup)) { NavigationLink(destination: SelectingGroupView(vm: vm, networkMonitor: networkMonitor, firstFavGroup: firstFavGroup, secondFavGroup: secondFavGroup, thirdFavGroup: thirdFavGroup)) {
HStack { HStack {
Image(systemName: "plus") Image(systemName: "plus")
.foregroundColor(.white) .foregroundColor(.white)
@ -87,7 +88,7 @@ struct FavGroupsView: View {
} }
} }
} }
.padding(.bottom, 50) .padding(.bottom, 90)
} }
.background(Color("background")) .background(Color("background"))
} }
@ -95,5 +96,6 @@ struct FavGroupsView: View {
#Preview { #Preview {
@Previewable @StateObject var vm = ScheduleViewModel() @Previewable @StateObject var vm = ScheduleViewModel()
FavGroupsView(vm: vm) @Previewable @StateObject var vm2 = NetworkMonitor()
FavGroupsView(vm: vm, networkMonitor: vm2)
} }

View File

@ -2,13 +2,14 @@
// FavGroupsView.swift // FavGroupsView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by G412 on 05.03.2025. // Created by Egor Mironov on 05.03.2025.
// //
import SwiftUI import SwiftUI
struct FavVPKView: View { struct FavVPKView: View {
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@ObservedObject var networkMonitor: NetworkMonitor
var firstFavVPK = (UserDefaults.standard.string(forKey: "vpk1") ?? "") var firstFavVPK = (UserDefaults.standard.string(forKey: "vpk1") ?? "")
var secondFavVPK = (UserDefaults.standard.string(forKey: "vpk2") ?? "") var secondFavVPK = (UserDefaults.standard.string(forKey: "vpk2") ?? "")
var thirdFavVPK = (UserDefaults.standard.string(forKey: "vpk3") ?? "") var thirdFavVPK = (UserDefaults.standard.string(forKey: "vpk3") ?? "")
@ -74,7 +75,7 @@ struct FavVPKView: View {
HStack { HStack {
Spacer() Spacer()
if firstFavVPK == "" || secondFavVPK == "" || thirdFavVPK == "" { if firstFavVPK == "" || secondFavVPK == "" || thirdFavVPK == "" {
NavigationLink(destination: SelectingVPKView(vm: vm, firstFavVPK: firstFavVPK, secondFavVPK: secondFavVPK, thirdFavVPK: thirdFavVPK)) { NavigationLink(destination: SelectingVPKView(vm: vm, networkMonitor: networkMonitor, firstFavVPK: firstFavVPK, secondFavVPK: secondFavVPK, thirdFavVPK: thirdFavVPK)) {
HStack { HStack {
Image(systemName: "plus") Image(systemName: "plus")
.foregroundColor(.white) .foregroundColor(.white)
@ -87,7 +88,7 @@ struct FavVPKView: View {
} }
} }
} }
.padding(.bottom, 50) .padding(.bottom, 90)
} }
.background(Color("background")) .background(Color("background"))
} }
@ -95,5 +96,6 @@ struct FavVPKView: View {
#Preview { #Preview {
@Previewable @StateObject var vm = ScheduleViewModel() @Previewable @StateObject var vm = ScheduleViewModel()
FavVPKView(vm: vm) @Previewable @StateObject var vm2 = NetworkMonitor()
FavVPKView(vm: vm, networkMonitor: vm2)
} }

View File

@ -9,9 +9,10 @@ import SwiftUI
struct ScheduleGroupSettings: View { struct ScheduleGroupSettings: View {
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@ObservedObject var networkMonitor: NetworkMonitor
var body: some View { var body: some View {
VStack { VStack {
NavigationLink(destination: FavGroupsView(vm: vm)) { NavigationLink(destination: FavGroupsView(vm: vm, networkMonitor: networkMonitor)) {
HStack { HStack {
Text("Избранное расписание") Text("Избранное расписание")
.font(.custom("Montserrat-Medium", fixedSize: 17)) .font(.custom("Montserrat-Medium", fixedSize: 17))
@ -27,7 +28,7 @@ struct ScheduleGroupSettings: View {
.foregroundColor(Color("customGray1")) .foregroundColor(Color("customGray1"))
.frame(height: 1) .frame(height: 1)
.padding(.horizontal) .padding(.horizontal)
NavigationLink(destination: FavVPKView(vm: vm)) { NavigationLink(destination: FavVPKView(vm: vm, networkMonitor: networkMonitor)) {
HStack { HStack {
Text("ВПК") Text("ВПК")
.font(.custom("Montserrat-Medium", fixedSize: 17)) .font(.custom("Montserrat-Medium", fixedSize: 17))

View File

@ -12,6 +12,7 @@ struct SelectingGroupView: View {
@FocusState private var isFocused: Bool @FocusState private var isFocused: Bool
@State private var text: String = "" @State private var text: String = ""
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@ObservedObject var networkMonitor: NetworkMonitor
@State private var isLoading = false @State private var isLoading = false
@State private var searchTask: DispatchWorkItem? @State private var searchTask: DispatchWorkItem?
@StateObject private var serchGroupsVM = SearchGroupsViewModel() @StateObject private var serchGroupsVM = SearchGroupsViewModel()
@ -93,9 +94,9 @@ struct SelectingGroupView: View {
) )
Spacer() Spacer()
if isLoading { if isLoading {
LoadingView(isLoading: $isLoading) LoadingView()
} Spacer()
//if isFocused { } else if networkMonitor.isConnected {
ScrollView(.vertical, showsIndicators: true) { ScrollView(.vertical, showsIndicators: true) {
ForEach(serchGroupsVM.groups) { item in ForEach(serchGroupsVM.groups) { item in
if item.name.starts(with: "КТ") { //Отображаем только группы(без аудиторий и преподавателей) if item.name.starts(with: "КТ") { //Отображаем только группы(без аудиторий и преподавателей)
@ -134,7 +135,9 @@ struct SelectingGroupView: View {
} }
} }
} }
//} } else {
NetworkErrorView(message: "Восстановите подключение к интернету чтобы мы смогли загрузить список групп")
}
} }
.padding(.horizontal, 10) .padding(.horizontal, 10)
.background(Color("background")) .background(Color("background"))
@ -146,5 +149,6 @@ struct SelectingGroupView: View {
#Preview { #Preview {
@Previewable @StateObject var vm = ScheduleViewModel() @Previewable @StateObject var vm = ScheduleViewModel()
SelectingGroupView(vm: vm, firstFavGroup: "", secondFavGroup: "", thirdFavGroup: "") @Previewable @StateObject var vm2 = NetworkMonitor()
SelectingGroupView(vm: vm, networkMonitor: vm2, firstFavGroup: "", secondFavGroup: "", thirdFavGroup: "")
} }

View File

@ -12,6 +12,7 @@ struct SelectingVPKView: View {
@FocusState private var isFocused: Bool @FocusState private var isFocused: Bool
@State private var text: String = "" @State private var text: String = ""
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@ObservedObject var networkMonitor: NetworkMonitor
@State private var isLoading = false @State private var isLoading = false
@State private var searchTask: DispatchWorkItem? @State private var searchTask: DispatchWorkItem?
@StateObject private var serchGroupsVM = SearchGroupsViewModel() @StateObject private var serchGroupsVM = SearchGroupsViewModel()
@ -94,9 +95,13 @@ struct SelectingVPKView: View {
) )
Spacer() Spacer()
if isLoading { if isLoading {
LoadingView(isLoading: $isLoading) LoadingView()
} Spacer()
} else if networkMonitor.isConnected {
ListOfGroupsView(vm: vm, serchGroupsVM: serchGroupsVM, firstFavVPK: firstFavVPK, secondFavVPK: secondFavVPK, thirdFavVPK: thirdFavVPK) ListOfGroupsView(vm: vm, serchGroupsVM: serchGroupsVM, firstFavVPK: firstFavVPK, secondFavVPK: secondFavVPK, thirdFavVPK: thirdFavVPK)
} else {
ConnectingToNetworkView()
}
} }
.padding(.horizontal, 10) .padding(.horizontal, 10)
.background(Color("background")) .background(Color("background"))
@ -108,5 +113,6 @@ struct SelectingVPKView: View {
#Preview { #Preview {
@Previewable @StateObject var vm = ScheduleViewModel() @Previewable @StateObject var vm = ScheduleViewModel()
SelectingVPKView(vm: vm, firstFavVPK: "", secondFavVPK: "", thirdFavVPK: "") @Previewable @StateObject var vm2 = NetworkMonitor()
SelectingVPKView(vm: vm, networkMonitor: vm2, firstFavVPK: "", secondFavVPK: "", thirdFavVPK: "")
} }

View File

@ -9,6 +9,7 @@ import SwiftUI
struct SettingsView: View { struct SettingsView: View {
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@ObservedObject var networkMonitor: NetworkMonitor
@State private var selectedTheme = "Светлая" @State private var selectedTheme = "Светлая"
@State private var selectedLanguage = "Русский" @State private var selectedLanguage = "Русский"
var body: some View { var body: some View {
@ -28,7 +29,7 @@ struct SettingsView: View {
.font(.custom("Montserrat-Medium", fixedSize: 18)) .font(.custom("Montserrat-Medium", fixedSize: 18))
.foregroundColor(Color("customGray3")) .foregroundColor(Color("customGray3"))
.padding(.horizontal) .padding(.horizontal)
ScheduleGroupSettings(vm: vm) ScheduleGroupSettings(vm: vm, networkMonitor: networkMonitor)
} }
.padding(.top, 20) .padding(.top, 20)
} }
@ -42,5 +43,6 @@ struct SettingsView: View {
#Preview { #Preview {
@Previewable @StateObject var vm = ScheduleViewModel() @Previewable @StateObject var vm = ScheduleViewModel()
SettingsView(vm: vm) @Previewable @StateObject var vm2 = NetworkMonitor()
SettingsView(vm: vm, networkMonitor: vm2)
} }

View File

@ -2,14 +2,14 @@
// CustomTabBarView.swift // CustomTabBarView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by Egor Mironov on 13.11.2024. // Created by Mironov Egor on 13.11.2024.
// //
import SwiftUI import SwiftUI
struct TabBarView: View { struct TabBarView: View {
@Binding var selectedTab: TabBarModel @Binding var selectedTab: TabBarModel
// @NameSpace private var animation @Namespace private var animation
var body: some View { var body: some View {
VStack { VStack {
Spacer() Spacer()
@ -20,14 +20,14 @@ struct TabBarView: View {
.padding(6) .padding(6)
.background(.white) .background(.white)
.mask(RoundedRectangle(cornerRadius: 24, style: .continuous)) .mask(RoundedRectangle(cornerRadius: 24, style: .continuous))
.shadow(color: .black.opacity(0.2), radius: 8, x: 4, y: 4) .shadow(color: .black.opacity(0.4), radius: 20, x: 8, y: 8)
// .background( //.background(
// background // background
// .shadow(.drop(color: .black.opacity(0.08), radius: 5, x: 5, y: 5)) // .shadow(.drop(color: Color.black.opacity(0.08), radius: 5, x: 5, y: 5))
// .shadow(.drop(color: .black.opacity(0.08), radius: 5, x: 5, y: -5)), // .shadow(.drop(color: Color.black.opacity(0.08), radius: 5, x: 5, y: -5)),
// in: .capsule // in: .capsule
// ) //)
} }
.ignoresSafeArea(.keyboard, edges: .bottom) // Фиксаци таб-бара, при появлении клавиатуры .ignoresSafeArea(.keyboard, edges: .bottom) // Фиксаци таб-бара, при появлении клавиатуры
} }
@ -40,6 +40,7 @@ struct TabBarView: View {
VStack (alignment: .center) { VStack (alignment: .center) {
Image(systemName: tab.rawValue) Image(systemName: tab.rawValue)
.font(.title3) .font(.title3)
.fontWeight(.regular)
} }
.frame(width: 70, height: 28) .frame(width: 70, height: 28)
.foregroundStyle(selectedTab == tab ? Color.white : Color("blueColor")) .foregroundStyle(selectedTab == tab ? Color.white : Color("blueColor"))
@ -49,8 +50,18 @@ struct TabBarView: View {
.background { .background {
if selectedTab == tab { if selectedTab == tab {
Capsule() Capsule()
.fill(Color("blueColor")) .fill(
// .matchedGeometryEffect(id: "ACTIVETAB", in: animation) LinearGradient(
gradient: Gradient(stops: [
.init(color: Color("blueColor").opacity(0.9), location: 0.0),
.init(color: Color("blueColor").opacity(0.9), location: 0.5),
.init(color: Color("blueColor").opacity(1.0), location: 1.0)
]),
startPoint: .top,
endPoint: .bottom
)
)
.matchedGeometryEffect(id: "ACTIVETAB", in: animation)
} }
} }
} }