diff --git a/AcaMate.xcodeproj/project.pbxproj b/AcaMate.xcodeproj/project.pbxproj index 565a7a3..136c26e 100644 --- a/AcaMate.xcodeproj/project.pbxproj +++ b/AcaMate.xcodeproj/project.pbxproj @@ -324,7 +324,7 @@ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited) DEV LOCAL"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited) DEV"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; diff --git a/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate b/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate index f7e9f5b..ea19e09 100644 Binary files a/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate and b/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/AcaMate/1. View/10. Common/NavigationView.swift b/AcaMate/1. View/10. Common/NavigationView.swift index 4250e0a..8b6c5e8 100644 --- a/AcaMate/1. View/10. Common/NavigationView.swift +++ b/AcaMate/1. View/10. Common/NavigationView.swift @@ -27,7 +27,7 @@ struct NavigationView: View { case .Login : LoginView(naviState: $naviState) case .Main: - MainView(naviState: $naviState, menuName: .Home) + MainView(naviState: $naviState) } } } diff --git a/AcaMate/1. View/12. Main/121. Home/CalendarBoxView.swift b/AcaMate/1. View/12. Main/121. Home/CalendarBoxView.swift index d78ff5f..dd7fd00 100644 --- a/AcaMate/1. View/12. Main/121. Home/CalendarBoxView.swift +++ b/AcaMate/1. View/12. Main/121. Home/CalendarBoxView.swift @@ -12,26 +12,14 @@ struct CalendarBoxView: View { var body: some View { DashBoardView(image: Image(.Icon.calendar), title: "최근 일정") { - if !summaryCalDataList.isEmpty { + if summaryCalDataList.isEmpty { + EmptyBoxView(title: "최근 일정이 없습니다.") + } else { VStack(spacing: 12) { ForEach(Array(summaryCalDataList.enumerated()), id: \.offset) { index, data in CalCellView(summaryCalData: data) } } - } else { - Text("최근 일정이 없습니다.") - .font(.nps(size: 20)) - .foregroundStyle(Color(.Text.detail)) - .lineLimit(1) - .minimumScaleFactor(0.5) - .truncationMode(.tail) - .frame(maxWidth: .infinity) - .padding([.top,.bottom],12) - .background { - RoundedRectangle(cornerRadius: 4) - .stroke(Color(.Second.normal), lineWidth: 2) - .fill(Color(.Second.light)) - } } } moreAction: { diff --git a/AcaMate/1. View/12. Main/121. Home/DriveBoxView.swift b/AcaMate/1. View/12. Main/121. Home/DriveBoxView.swift new file mode 100644 index 0000000..d5ab51d --- /dev/null +++ b/AcaMate/1. View/12. Main/121. Home/DriveBoxView.swift @@ -0,0 +1,130 @@ +// +// DriveBoxView.swift +// AcaMate +// +// Created by TAnine on 2/12/25. +// + +import SwiftUI + +struct DriveBoxView: View { + @EnvironmentObject var appVM: AppViewModel + + @State var summaryDriveList: [SummaryDrive] + + var body: some View { + DashBoardView(image: Image(.Icon.drive), title: "배차 현황") { + if summaryDriveList.isEmpty { + EmptyBoxView(title: "등록된 차량이 없습니다.") + } else { + VStack(spacing: 12) { + ForEach(Array(summaryDriveList.enumerated()),id: \.offset) { index, drive in + DriveCellView(summaryDrive: $summaryDriveList[index]) + } + } + + } + } moreAction: { + appVM.menuName = .Management + } + } +} + +struct DriveCellView: View { + @EnvironmentObject var appVM: AppViewModel + @StateObject var btnVM = ButtonViewModel() + @Binding var summaryDrive: SummaryDrive + + @State var setNotiBtn = UUID() + + var body: some View { + VStack(spacing:0) { + HStack(spacing: 0) { + Image(summaryDrive.isUsable ? .Icon.driveON : .Icon.driveOFF) + .resizable() + .frame(width: 24, height: 24, alignment: .center) + Spacer(minLength: 1) + + HStack(alignment: .bottom, spacing: 4) { + Text("[\(summaryDrive.title)]") + .font(.nps(size: 14)) + + Text("\(summaryDrive.isIn ? "등원" : "하원")") + .font(.nps(font: .bold, size: 20)) + + Text("차량") + .font(.nps(size: 18)) + } + .foregroundStyle(Color(.Text.detail)) + Spacer(minLength: 1) + SimpleBtnView(vm: btnVM, id: setNotiBtn) + } + HStack(spacing: 0) { + Spacer(minLength: 1) + Text("\(summaryDrive.driver) 기사님") + .font(.nps(size: 12)) + .foregroundStyle(Color(.Text.detail)) + } + HStack(spacing: 0) { + Text("\(summaryDrive.isUsable ? (summaryDrive.isDrive ? "운행": "") : "정지" )") + .font(.nps(font: .bold, size: 20)) + .foregroundStyle(summaryDrive.isUsable ? (summaryDrive.isDrive ? Color(.Other.red): Color(.Text.detail)) : Color(.Text.disabled)) + Spacer(minLength: 1) + } + HStack(alignment: .bottom, spacing: 4) { + Text("출발") + .font(.nps(size: 16)) + + Text("\(summaryDrive.time)") + .font(.nps(font: .bold, size: 20)) + Spacer(minLength: 1) + + Text("\(summaryDrive.number) 번") + .font(.nps(font: .bold, size: 20)) + Text("차량") + .font(.nps(size: 16)) + + + } + .foregroundStyle(Color(.Text.detail)) + + } + .padding(12) + .background { + RoundedRectangle(cornerRadius: 4) + .stroke(summaryDrive.isUsable ? (summaryDrive.isDrive ? Color(.Other.red): Color(.Second.normal)) : Color(.Disable.dark), + lineWidth: 2) + .fill(summaryDrive.isUsable ? (summaryDrive.isDrive ? Color(.Danger.normal): Color(.Second.light)) : Color(.Disable.normal)) + } + .onTapGesture { + // MARK: TO-DO + // 차량 페이지 이동 로직 추가 + printLog(summaryDrive) + } + .onAppear { + btnVM.setImage(for: setNotiBtn, newImage: summaryDrive.isAlarm ? Image(.Icon.notificationON) : Image(.Icon.notificationOFF)) + btnVM.setSize(for: setNotiBtn, newWidth: 24, newHeight: 24) + btnVM.setAction(for: setNotiBtn) { + + if summaryDrive.isUsable { + if summaryDrive.isAlarm{ + appVM.alertData = AlertData(body: "알람이 해제되었습니다.", + button: [ + ButtonType(name: "확인", role: .cancel) { printLog("차량 알림 변경 알럿") } + ]) + } else { + appVM.alertData = AlertData(body: "알람이 설정되었습니다.", + button: [ + ButtonType(name: "확인", role: .cancel) { printLog("차량 알림 변경 알럿") } + ]) + } + appVM.showAlert.toggle() + summaryDrive.isAlarm.toggle() + } + } + } + .onChange(of: summaryDrive.isAlarm) { _, _ in + btnVM.setImage(for: setNotiBtn, newImage: summaryDrive.isAlarm ? Image(.Icon.notificationON) : Image(.Icon.notificationOFF)) + } + } +} diff --git a/AcaMate/1. View/12. Main/121. Home/HomeView.swift b/AcaMate/1. View/12. Main/121. Home/HomeView.swift index 6f7d8a4..4b74c1e 100644 --- a/AcaMate/1. View/12. Main/121. Home/HomeView.swift +++ b/AcaMate/1. View/12. Main/121. Home/HomeView.swift @@ -10,7 +10,6 @@ import SwiftUI struct HomeView: View { @State private var scrollOffset: CGPoint = .zero @State private var topViewState: Bool = false - var body: some View { VStack(spacing: 0) { ZStack { @@ -18,27 +17,40 @@ struct HomeView: View { VStack(spacing: 0) { TopProfileView(userType: .Student) -// .padding(EdgeInsets(top: 0, leading: 0, bottom: 12, trailing: 0))s .padding(.bottom,12) - + Group { AttendanceBoxView() - CalendarBoxView(summaryCalDataList: []) + +// CalendarBoxView(summaryCalDataList: []) CalendarBoxView(summaryCalDataList: [ SummaryCalendar(id: "123", date: "2025-02-28", summary: "요약내용입니다."), SummaryCalendar(id: "123", date: "2025-02-28", summary: "요약내용입니다.")]) - ManagementBoxView(managementList: []) + +// ManagementBoxView(managementList: []) ManagementBoxView(managementList: [ SummaryManagement(id: "01", title: "과목 명1", teacher: "A", ratio: 27, homework: 3), SummaryManagement(id: "02", title: "과목 명2", teacher: "B", ratio: 80, homework: 10), SummaryManagement(id: "03", title: "과목 명3", teacher: "C", ratio: 13, homework: 2), SummaryManagement(id: "04", title: "과목 명4", teacher: "D", ratio: 72, homework: 0), ]) + +// NoticeBoxView(noticeList: []) NoticeBoxView(noticeList: [ SummaryNotice(id: "00", title: "공지사항1", date: "2025-02-11", new: true), SummaryNotice(id: "01", title: "공지사항2", date: "2025-01-11", new: false), SummaryNotice(id: "02", title: "공지사항3", date: "2025-01-01", new: false) ]) + + DriveBoxView(summaryDriveList: [ + SummaryDrive(id: "00", title: "강남행", driver: "A", time: "08:00", number: "1", isUsable: true, isAlarm: true, isDrive: false, isIn: true), + + SummaryDrive(id: "01", title: "강남행", driver: "A", time: "12:00", number: "2", isUsable: true, isAlarm: true, isDrive: true, isIn: true), + + SummaryDrive(id: "02", title: "강남행", driver: "A", time: "18:00", number: "3", isUsable: false, isAlarm: true, isDrive: false, isIn: true), + + SummaryDrive(id: "02", title: "강남행", driver: "A", time: "18:00", number: "3", isUsable: true, isAlarm: true, isDrive: false, isIn: false) + ]) } .background { RoundedRectangle(cornerRadius: 8) @@ -70,3 +82,21 @@ struct HomeView: View { } } +struct EmptyBoxView: View { + let title: String + var body: some View { + Text("\(title)") + .font(.nps(size: 20)) + .foregroundStyle(Color(.Text.detail)) + .lineLimit(1) + .minimumScaleFactor(0.5) + .truncationMode(.tail) + .frame(maxWidth: .infinity) + .padding([.top,.bottom],12) + .background { + RoundedRectangle(cornerRadius: 4) + .stroke(Color(.Second.normal), lineWidth: 2) + .fill(Color(.Second.light)) + } + } +} diff --git a/AcaMate/1. View/12. Main/121. Home/ManagementBoxView.swift b/AcaMate/1. View/12. Main/121. Home/ManagementBoxView.swift index e22ad4c..3110d01 100644 --- a/AcaMate/1. View/12. Main/121. Home/ManagementBoxView.swift +++ b/AcaMate/1. View/12. Main/121. Home/ManagementBoxView.swift @@ -16,7 +16,9 @@ struct ManagementBoxView: View { var body: some View { DashBoardView(image: Image(.Icon.edu), title: "학습 관리") { - if managementList.count >= 1 { + if managementList.isEmpty { + EmptyBoxView(title: "학습하고 있는 강의가 없습니다.") + } else { HStack { SimpleBtnView(vm: btnVM, id: leftBtnID) Spacer(minLength: 1) @@ -25,21 +27,6 @@ struct ManagementBoxView: View { .padding([.bottom],12) MangCellView(summaryMang: $managementList[countNum]) - } else { - Text("학습하고 있는 강의가 없습니다.") - .font(.nps(size: 20)) - .foregroundStyle(Color(.Text.detail)) - .lineLimit(1) - .minimumScaleFactor(0.5) - .truncationMode(.tail) - .frame(maxWidth: .infinity) - .padding([.top,.bottom],12) - .background { - RoundedRectangle(cornerRadius: 4) - .stroke(Color(.Second.normal), lineWidth: 2) - .fill(Color(.Second.light)) - } - } } moreAction: { @@ -93,12 +80,9 @@ struct MangCellView: View { .foregroundStyle(Color(.Text.title)) Spacer(minLength: 1) } - HStack(spacing: 4) { + HStack(spacing: 0) { Spacer(minLength: 1) - Text("\(summaryMang.teacher)") - .font(.nps(size: 12)) - .foregroundStyle(Color(.Text.detail)) - Text("선생님") + Text("\(summaryMang.teacher) 선생님") .font(.nps(size: 12)) .foregroundStyle(Color(.Text.detail)) } diff --git a/AcaMate/1. View/12. Main/121. Home/NoticeBoxView.swift b/AcaMate/1. View/12. Main/121. Home/NoticeBoxView.swift index 90ad031..82a5628 100644 --- a/AcaMate/1. View/12. Main/121. Home/NoticeBoxView.swift +++ b/AcaMate/1. View/12. Main/121. Home/NoticeBoxView.swift @@ -12,14 +12,18 @@ struct NoticeBoxView: View { var body: some View { DashBoardView(image: Image(.Icon.notice), title: "공지사항") { - ForEach(Array(noticeList.enumerated()), id: \.offset) { index, notice in - - if index < 3 { - NotiCellView(summaryNoti: notice) + if noticeList.isEmpty { + EmptyBoxView(title: "공지가 없습니다.") + } else { + VStack(spacing: 12) { + ForEach(Array(noticeList.enumerated()), id: \.offset) { index, notice in + if index < 3 { + NotiCellView(summaryNoti: notice) + } + } } } - // 값없을때도 없다는거 보여주는 창 띄우기 } moreAction: { // MARK: TO-DO @@ -51,8 +55,19 @@ struct NotiCellView: View { .foregroundStyle(Color(.Text.detail)) Text("\(summaryNoti.date)") .font(.nps(size: 12)) + .frame(width: 80) .foregroundStyle(Color(.Text.detail)) } } + .padding(12) + .background { + RoundedRectangle(cornerRadius: 4) + .stroke(Color(.Second.normal), lineWidth: 2) + .fill(Color(.Second.light)) + } + .onTapGesture { + printLog("공지사항 내부 셀 클릭") + // MARK: TO-DO + } } } diff --git a/AcaMate/1. View/12. Main/BottomView.swift b/AcaMate/1. View/12. Main/BottomView.swift index 7b0d855..bc0397d 100644 --- a/AcaMate/1. View/12. Main/BottomView.swift +++ b/AcaMate/1. View/12. Main/BottomView.swift @@ -8,8 +8,8 @@ import SwiftUI struct BottomView: View { + @EnvironmentObject var appVM: AppViewModel @StateObject var btnVM = ButtonViewModel() - @State var pageType: PageType = .Home @State private var homeID = UUID() @State private var managementID = UUID() @@ -17,7 +17,10 @@ struct BottomView: View { @State private var calendarID = UUID() @State private var etcID = UUID() - @Binding var menuName: MenuName + + let btnType: [MenuName] = [.Home, .Management, .Chatting, .Calendar, .Etc] +// var idList: [UUID] = [] + var body: some View { let idList: [UUID] = [homeID,managementID,chattingID,calendarID,etcID] @@ -27,7 +30,6 @@ struct BottomView: View { if index != idList.count-1 { Spacer(minLength: 1) } - } } .padding(EdgeInsets(top: 12, leading: 24, bottom: 0, trailing: 24)) @@ -39,9 +41,9 @@ struct BottomView: View { } .frame(maxWidth: .infinity) .onAppear { - let idList: [UUID] = [homeID,managementID,chattingID,calendarID,etcID] //["홈", "학습 관리", "채팅", "일정", "더보기"] - let btnType: [MenuName] = [.Home, .Management, .Chatting, .Calendar, .Etc] +// idList = [homeID,managementID,chattingID,calendarID,etcID] + let btnImage: [Image] = [Image(.Icon.home),Image(.Icon.management),Image(.Icon.chatting),Image(.Icon.calendar),Image(.Icon.etc)] idList.enumerated().forEach { (index, id) in @@ -50,19 +52,24 @@ struct BottomView: View { btnVM.setText(for: id, newText: btnType[index].rawValue, newFont: .nps(font: .bold, size: 6)) btnVM.setImage(for: id, newImage: btnImage[index]) - - btnVM.setAction(for: id) { + if btnType[index] == appVM.menuName { btnVM.setIsSelected(for: id, newValue: true) - idList.forEach { - if $0 != id { - btnVM.setIsSelected(for: $0, newValue: false) - } - } - menuName = btnType[index] + } + btnVM.setAction(for: id) { + appVM.menuName = btnType[index] } } + } + .onChange(of: appVM.menuName) { old, new in +// let idList: [UUID] = [homeID,managementID,chattingID,calendarID,etcID] + + if let oldID = btnType.firstIndex(of: old) { + btnVM.setIsSelected(for: idList[oldID], newValue: false) + } + if let newID = btnType.firstIndex(of: new) { + btnVM.setIsSelected(for: idList[newID], newValue: true) + } - btnVM.setIsSelected(for: idList[pageType.rawValue], newValue: true) } } diff --git a/AcaMate/1. View/12. Main/MainView.swift b/AcaMate/1. View/12. Main/MainView.swift index ff22129..f23bb73 100644 --- a/AcaMate/1. View/12. Main/MainView.swift +++ b/AcaMate/1. View/12. Main/MainView.swift @@ -9,20 +9,16 @@ import SwiftUI import Combine struct MainView: View { + @EnvironmentObject var appVM: AppViewModel @EnvironmentObject var alertController: AlertController @State var cancellables: Set = [] @Binding var naviState : NaviState - @State var menuName: MenuName - -// @State private var scrollOffset: CGPoint = .zero -// @State private var topViewState: Bool = false - var body: some View { VStack(spacing: 0) { Group { - switch menuName { + switch appVM.menuName { case .Home: HomeView() case .Management: @@ -37,19 +33,11 @@ struct MainView: View { } Spacer(minLength: 1) - BottomView(menuName: $menuName) + BottomView() .frame(maxWidth: .infinity) } .frame(maxWidth: .infinity, maxHeight: .infinity) -// -// .onChange(of: scrollOffset.y) { oldValue, newValue in -// if newValue > 200 && topViewState == false { -// topViewState = true -// } else if newValue < 20 && topViewState == true{ -// topViewState = false -// } -// } - + } } diff --git a/AcaMate/2. Model/Alert.swift b/AcaMate/2. Model/Alert.swift index ed1dc65..c5dcb47 100644 --- a/AcaMate/2. Model/Alert.swift +++ b/AcaMate/2. Model/Alert.swift @@ -93,5 +93,6 @@ struct SetAlertData { ButtonType(name: "확인", role: .cancel, function: nil) ]) } + } diff --git a/AcaMate/2. Model/Drive Data.swift b/AcaMate/2. Model/Drive Data.swift new file mode 100644 index 0000000..3db08de --- /dev/null +++ b/AcaMate/2. Model/Drive Data.swift @@ -0,0 +1,21 @@ +// +// Drive Data.swift +// AcaMate +// +// Created by TAnine on 2/12/25. +// + +import Foundation + +struct SummaryDrive : Hashable { + var id: String + var title: String + var driver: String + var time: String + var number: String + + var isUsable: Bool + var isAlarm: Bool + var isDrive: Bool + var isIn: Bool +} diff --git a/AcaMate/2. Model/Page Type.swift b/AcaMate/2. Model/Page Type.swift deleted file mode 100644 index ce12720..0000000 --- a/AcaMate/2. Model/Page Type.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// Page Type.swift -// AcaMate -// -// Created by TAnine on 2/6/25. -// - -import Foundation - -enum PageType: Int{ - case Home = 0 - case Management - case Chatting - case Calendar - case Etc -} diff --git a/AcaMate/3. ViewModel/AppViewModel.swift b/AcaMate/3. ViewModel/AppViewModel.swift index 0a62992..5398d3a 100644 --- a/AcaMate/3. ViewModel/AppViewModel.swift +++ b/AcaMate/3. ViewModel/AppViewModel.swift @@ -11,6 +11,7 @@ import Combine class AppViewModel: ObservableObject { @Published var isLoading: Bool = false @Published var showAlert: Bool = false + @Published var menuName: MenuName = .Home var alertData: AlertData = .init(body: "") diff --git a/AcaMate/5. Modifier/View.swift b/AcaMate/5. Modifier/View.swift index f7e6df5..4f6cd00 100644 --- a/AcaMate/5. Modifier/View.swift +++ b/AcaMate/5. Modifier/View.swift @@ -71,41 +71,41 @@ struct LoadingModifier: ViewModifier { } } } - -struct PressEffect: ViewModifier { - @State private var isPressed = false - var scale: CGFloat - var opacity: CGFloat - var duration: Double - - func body(content: Content) -> some View { - content - .scaleEffect(isPressed ? scale : 1.0) - .opacity(isPressed ? opacity : 1.0) - .animation(.easeOut(duration: duration), value: isPressed) - .simultaneousGesture( - LongPressGesture(minimumDuration: 0.01) - .onChanged { _ in isPressed = true } - .onEnded { _ in isPressed = false } - ) - } -} -struct PressBackgroundEffect: ViewModifier { - @State private var isPressed = false - var backgroundColor: Color - var duration: Double - - func body(content: Content) -> some View { - content - .background(isPressed ? backgroundColor : Color.clear) - .animation(.easeOut(duration: duration), value: isPressed) - .simultaneousGesture( - LongPressGesture(minimumDuration: 0.01) - .onChanged { _ in isPressed = true } - .onEnded { _ in isPressed = false } - ) - } -} +// +//struct PressEffect: ViewModifier { +// @State private var isPressed = false +// var scale: CGFloat +// var opacity: CGFloat +// var duration: Double +// +// func body(content: Content) -> some View { +// content +// .scaleEffect(isPressed ? scale : 1.0) +// .opacity(isPressed ? opacity : 1.0) +// .animation(.easeOut(duration: duration), value: isPressed) +// .simultaneousGesture( +// LongPressGesture(minimumDuration: 0.01) +// .onChanged { _ in isPressed = true } +// .onEnded { _ in isPressed = false } +// ) +// } +//} +//struct PressBackgroundEffect: ViewModifier { +// @State private var isPressed = false +// var backgroundColor: Color +// var duration: Double +// +// func body(content: Content) -> some View { +// content +// .background(isPressed ? backgroundColor : Color.clear) +// .animation(.easeOut(duration: duration), value: isPressed) +// .simultaneousGesture( +// LongPressGesture(minimumDuration: 0.01) +// .onChanged { _ in isPressed = true } +// .onEnded { _ in isPressed = false } +// ) +// } +//} extension View { @@ -141,13 +141,12 @@ extension View { } } - func pressAnimation(scale: CGFloat = 0.95, opacity: CGFloat = 0.85, duration: Double = 0.1) -> some View { - self.modifier(PressEffect(scale: scale, opacity: opacity, duration: duration)) - - } +// func pressAnimation(scale: CGFloat = 0.95, opacity: CGFloat = 0.85, duration: Double = 0.1) -> some View { +// self.modifier(PressEffect(scale: scale, opacity: opacity, duration: duration)) +// } } extension View { - func pressColorAnimation(backgroundColor: Color = Color.black.opacity(0.1), duration: Double = 0.1) -> some View { - self.modifier(PressBackgroundEffect(backgroundColor: backgroundColor, duration: duration)) - } +// func pressColorAnimation(backgroundColor: Color = Color.black.opacity(0.1), duration: Double = 0.1) -> some View { +// self.modifier(PressBackgroundEffect(backgroundColor: backgroundColor, duration: duration)) +// } }