ViewModel was changed and searchbar is working now. You can text your group and find your schedule
This commit is contained in:
178
Schedule ICTIS/Main/MainView.swift
Normal file
178
Schedule ICTIS/Main/MainView.swift
Normal file
@ -0,0 +1,178 @@
|
||||
//
|
||||
// ScheduleView.swift
|
||||
// Schedule ICTIS
|
||||
//
|
||||
// Created by Mironov Egor on 13.11.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MainView: View {
|
||||
@State private var searchText: String = ""
|
||||
@State private var currentDate: Date = .init()
|
||||
@State private var weekSlider: [[Date.WeekDay]] = []
|
||||
@State private var currentWeekIndex: Int = 1
|
||||
@State private var createWeek: Bool = false
|
||||
@State private var isShowingMonthSlider: Bool = false
|
||||
@StateObject var vm = ViewModel()
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
SearchBarView(text: $searchText, vm: vm)
|
||||
HeaderView()
|
||||
ScheduleView(vm: vm)
|
||||
}
|
||||
.background(Color("background"))
|
||||
.onAppear(perform: {
|
||||
if weekSlider.isEmpty {
|
||||
let currentWeek = Date().fetchWeek()
|
||||
|
||||
if let firstDate = currentWeek.first?.date {
|
||||
weekSlider.append(firstDate.createPrevioustWeek())
|
||||
}
|
||||
|
||||
weekSlider.append(currentWeek)
|
||||
|
||||
if let lastDate = currentWeek.last?.date {
|
||||
weekSlider.append(lastDate.createNextWeek())
|
||||
}
|
||||
}
|
||||
vm.updateSelectedDayIndex(currentDate)
|
||||
})
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func HeaderView() -> some View {
|
||||
VStack (alignment: .leading, spacing: 6) {
|
||||
HStack {
|
||||
VStack (alignment: .leading, spacing: 0) {
|
||||
Text(currentDate.format("EEEE"))
|
||||
.font(.system(size: 40, weight: .semibold))
|
||||
.foregroundStyle(.black)
|
||||
HStack (spacing: 5) {
|
||||
Text(currentDate.format("dd"))
|
||||
.font(.system(size: 20, weight: .bold))
|
||||
.foregroundStyle(Color("grayForDate"))
|
||||
Text(currentDate.format("MMMM"))
|
||||
.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()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.top, 8)
|
||||
.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)
|
||||
}
|
||||
}
|
||||
.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
|
||||
}
|
||||
}
|
||||
.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() {
|
||||
if weekSlider.indices.contains(currentWeekIndex) {
|
||||
if let firstDate = weekSlider[currentWeekIndex].first?.date,
|
||||
currentWeekIndex == 0 {
|
||||
weekSlider.insert(firstDate.createPrevioustWeek(), at: 0)
|
||||
weekSlider.removeLast()
|
||||
currentWeekIndex = 1
|
||||
}
|
||||
|
||||
if let lastDate = weekSlider[currentWeekIndex].last?.date,
|
||||
currentWeekIndex == (weekSlider.count - 1) {
|
||||
weekSlider.append(lastDate.createNextWeek())
|
||||
weekSlider.removeFirst()
|
||||
currentWeekIndex = weekSlider.count - 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
MainView()
|
||||
}
|
55
Schedule ICTIS/Main/ScheduleView.swift
Normal file
55
Schedule ICTIS/Main/ScheduleView.swift
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// ScheduleView.swift
|
||||
// Schedule ICTIS
|
||||
//
|
||||
// Created by G412 on 05.12.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ScheduleView: View {
|
||||
@ObservedObject var vm: ViewModel
|
||||
var body: some View {
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
VStack (spacing: 12) {
|
||||
ForEach(vm.classes.indices, id: \.self) { index in
|
||||
if index != 0 && index != 1 && index == vm.selectedIndex {
|
||||
let daySchedule = vm.classes[index] // Это массив строк для дня
|
||||
ForEach(daySchedule.indices.dropFirst(), id: \.self) { lessonIndex in
|
||||
let lesson = daySchedule[lessonIndex] // Это строка с расписанием одной пары
|
||||
if !lesson.isEmpty {
|
||||
HStack(spacing: 6) {
|
||||
VStack {
|
||||
Text(convertTimeString(vm.classes[1][lessonIndex])[0])
|
||||
.font(.system(size: 15, weight: .light))
|
||||
Text(convertTimeString(vm.classes[1][lessonIndex])[1])
|
||||
.font(.system(size: 15, weight: .light))
|
||||
}
|
||||
Rectangle()
|
||||
.frame(width: 2)
|
||||
.frame(maxHeight: 100)
|
||||
Text(lesson)
|
||||
}
|
||||
.background(Color.white)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func convertTimeString(_ input: String) -> [String] {
|
||||
let parts = input.split(separator: "-")
|
||||
if let firstPart = parts.first, let lastPart = parts.last {
|
||||
return [String(firstPart), String(lastPart)]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
MainView()
|
||||
}
|
80
Schedule ICTIS/Main/SearchBarView.swift
Normal file
80
Schedule ICTIS/Main/SearchBarView.swift
Normal file
@ -0,0 +1,80 @@
|
||||
//
|
||||
// SearchBarView.swift
|
||||
// Schedule ICTIS
|
||||
//
|
||||
// Created by Mironov Egor on 13.11.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SearchBarView: View {
|
||||
@Binding var text: String
|
||||
@State private var isEditing = false
|
||||
@ObservedObject var vm: ViewModel
|
||||
|
||||
var body: some View {
|
||||
HStack (spacing: 11) {
|
||||
HStack (spacing: 0) {
|
||||
Image(systemName: "magnifyingglass")
|
||||
.foregroundColor(Color.gray)
|
||||
.padding(.leading, 12)
|
||||
.padding(.trailing, 7)
|
||||
TextField("Поиск группы", text: $text)
|
||||
.disableAutocorrection(true)
|
||||
.onTapGesture {
|
||||
isEditing = true
|
||||
}
|
||||
.onSubmit {
|
||||
isEditing = false
|
||||
if (!text.isEmpty) {
|
||||
vm.fetchWeekSchedule(text)
|
||||
}
|
||||
}
|
||||
.submitLabel(.search)
|
||||
if isEditing {
|
||||
Button {
|
||||
self.text = ""
|
||||
self.isEditing = false
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
} label: {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.padding(.trailing, 20)
|
||||
.offset(x: 10)
|
||||
.foregroundColor(.gray)
|
||||
.background(
|
||||
)
|
||||
}
|
||||
.background(Color.white)
|
||||
}
|
||||
}
|
||||
.frame(height: 40)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.fill(.white)
|
||||
)
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.frame(width: 40, height: 40)
|
||||
.foregroundStyle(Color("blueColor"))
|
||||
.cornerRadius(15)
|
||||
Image(systemName: "plus")
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.scaledToFit()
|
||||
.frame(width: 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 5)
|
||||
.frame(height: 40)
|
||||
.accentColor(.blue)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
MainView()
|
||||
}
|
14
Schedule ICTIS/Main/TabBar/TabBarModel.swift
Normal file
14
Schedule ICTIS/Main/TabBar/TabBarModel.swift
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// Tab.swift
|
||||
// Schedule ICTIS
|
||||
//
|
||||
// Created by G412 on 13.11.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum TabBarModel: String, CaseIterable {
|
||||
case schedule = "house"
|
||||
case tasks = "books.vertical"
|
||||
case settings = "gear"
|
||||
}
|
64
Schedule ICTIS/Main/TabBar/TabBarView.swift
Normal file
64
Schedule ICTIS/Main/TabBar/TabBarView.swift
Normal file
@ -0,0 +1,64 @@
|
||||
//
|
||||
// CustomTabBarView.swift
|
||||
// Schedule ICTIS
|
||||
//
|
||||
// Created by Egor Mironov on 13.11.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TabBarView: View {
|
||||
@Binding var selectedTab: TabBarModel
|
||||
// @NameSpace private var animation
|
||||
var body: some View {
|
||||
VStack {
|
||||
Spacer()
|
||||
HStack(spacing: 15) {
|
||||
content
|
||||
}
|
||||
.animation(.smooth(duration: 0.3, extraBounce: 0), value: selectedTab)
|
||||
.padding(6)
|
||||
.background(.white)
|
||||
.mask(RoundedRectangle(cornerRadius: 24, style: .continuous))
|
||||
.shadow(color: .black.opacity(0.2), radius: 8, x: 4, y: 4)
|
||||
|
||||
// .background(
|
||||
// background
|
||||
// .shadow(.drop(color: .black.opacity(0.08), radius: 5, x: 5, y: 5))
|
||||
// .shadow(.drop(color: .black.opacity(0.08), radius: 5, x: 5, y: -5)),
|
||||
// in: .capsule
|
||||
// )
|
||||
}
|
||||
.ignoresSafeArea(.keyboard, edges: .bottom) // Фиксаци таб-бара, при появлении клавиатуры
|
||||
}
|
||||
|
||||
var content: some View {
|
||||
ForEach(TabBarModel.allCases, id: \.rawValue) { tab in
|
||||
Button {
|
||||
selectedTab = tab
|
||||
} label: {
|
||||
VStack (alignment: .center) {
|
||||
Image(systemName: tab.rawValue)
|
||||
.font(.title3)
|
||||
}
|
||||
.frame(width: 70, height: 28)
|
||||
.foregroundStyle(selectedTab == tab ? Color.white : Color("blueColor"))
|
||||
.padding(.vertical, 7)
|
||||
.padding(.leading, 13)
|
||||
.padding(.trailing, 13)
|
||||
.background {
|
||||
if selectedTab == tab {
|
||||
Capsule()
|
||||
.fill(Color("blueColor"))
|
||||
// .matchedGeometryEffect(id: "ACTIVETAB", in: animation)
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
Reference in New Issue
Block a user