main #10
|
@ -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";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited) DEV LOCAL";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
import SwiftUI
|
||||
// MARK: - ACAMATE
|
||||
// APPSTORE_URL : https://apps.apple.com/us/app/%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8%EB%A9%94%EC%9D%B4%ED%8A%B8/id6739448113
|
||||
#if DEV && LOCAL
|
||||
public let API_URL: String = "http://localhost:5144"
|
||||
#elseif DEV
|
||||
//#if DEV && LOCAL
|
||||
//public let API_URL: String = "http://localhost:5144"
|
||||
//#else
|
||||
#if DEV
|
||||
public let API_URL: String = "https://devacamate.ipstein.myds.me"
|
||||
#else
|
||||
public let API_URL: String = "https://acamate.ipstein.myds.me"
|
||||
|
|
|
@ -15,10 +15,18 @@ struct CircleBtnView: View {
|
|||
|
||||
var body: some View {
|
||||
if let state = vm.btnStates[id] {
|
||||
Button{
|
||||
guard let action = state.action else {return}
|
||||
action()
|
||||
} label: {
|
||||
ZStack {
|
||||
if state.isReverse {
|
||||
Circle()
|
||||
.foregroundStyle(state.backColor)
|
||||
.frame(width: state.width, height: state.height)
|
||||
.innerShadow(shape: Circle(), color: Color(.Text.black).opacity(0.75), blur: 8, x: 0, y: 4)
|
||||
} else {
|
||||
Circle()
|
||||
.foregroundStyle(state.backColor)
|
||||
.frame(width: state.width, height: state.height)
|
||||
.shadow(color: Color(.Text.black).opacity(0.75), radius: 8, x: 4, y: 8)
|
||||
}
|
||||
VStack(alignment: .center, spacing: 0) {
|
||||
if let image = state.image {
|
||||
image
|
||||
|
@ -27,31 +35,21 @@ struct CircleBtnView: View {
|
|||
.foregroundStyle(state.foreColor)
|
||||
.frame(width: state.width/2, height: state.height/2)
|
||||
}
|
||||
//
|
||||
if let title = state.title, let font = state.font {
|
||||
Text("\(title)")
|
||||
.font(font)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.truncationMode(.tail)
|
||||
.foregroundStyle(state.textColor)
|
||||
// .padding()
|
||||
}
|
||||
}
|
||||
.background {
|
||||
if state.isReverse {
|
||||
Circle()
|
||||
.accentColor(state.backColor)
|
||||
.frame(width: state.width, height: state.height)
|
||||
.innerShadow(shape: Circle(), color: Color(.Text.black).opacity(0.75), blur: 8, x: 0, y: 4)
|
||||
} else {
|
||||
Circle()
|
||||
.accentColor(state.backColor)
|
||||
.frame(width: state.width, height: state.height)
|
||||
.shadow(color: Color(.Text.black).opacity(0.75), radius: 8, x: 4, y: 8)
|
||||
.foregroundStyle(state.foreColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(width: state.width, height: state.height)
|
||||
.onTapGesture {
|
||||
guard let action = state.action else {return}
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ struct SimpleBtnView: View {
|
|||
.font(font)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.multilineTextAlignment(.center)
|
||||
.truncationMode(.tail)
|
||||
.foregroundStyle(state.textColor)
|
||||
.frame(width: state.width, height: state.height)
|
||||
|
@ -38,7 +39,9 @@ struct SimpleBtnView: View {
|
|||
if let image = state.image {
|
||||
image
|
||||
.resizable()
|
||||
.renderingMode(.template)
|
||||
.frame(width: state.width, height: state.height)
|
||||
.foregroundStyle(state.isUsable ? Color(.Second.normal) : Color(.Normal.normal))
|
||||
}
|
||||
}
|
||||
.disabled(!state.isUsable)
|
|
@ -12,22 +12,23 @@ import Combine
|
|||
/// 이거 위에 다른 뷰들이 위젯 느낌으로 계속 갈아 끼워지는거
|
||||
struct NavigationView: View {
|
||||
@EnvironmentObject var appVM: AppViewModel
|
||||
|
||||
@State private var naviState : NaviState = .init(act: .NONE, path: .Intro)
|
||||
@State private var history: [PathName] = [.Intro]
|
||||
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
switch naviState.path {
|
||||
case .NONE:
|
||||
EmptyView()
|
||||
case .Intro:
|
||||
IntroView(naviState: $naviState)
|
||||
case .Login :
|
||||
LoginView(naviState: $naviState)
|
||||
case .Main:
|
||||
MainView(naviState: $naviState)
|
||||
VStack(spacing: 0) {
|
||||
|
||||
ZStack {
|
||||
switch naviState.path {
|
||||
case .NONE:
|
||||
EmptyView()
|
||||
case .Intro:
|
||||
IntroView(naviState: $naviState)
|
||||
case .Login :
|
||||
LoginView(naviState: $naviState)
|
||||
case .Main:
|
||||
MainView(naviState: $naviState, menuName: .Home)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: naviState) { old, new in
|
||||
|
@ -43,7 +44,6 @@ struct NavigationView: View {
|
|||
case .MOVE:
|
||||
moveHistory(path: new.path)
|
||||
}
|
||||
// LOG
|
||||
printLog("\(old.path) => \(new.path)")
|
||||
showHistory()
|
||||
}
|
||||
|
|
71
AcaMate/1. View/10. Common/OffsetObservableScollView.swift
Normal file
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// OffsetObservableScollView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/10/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
struct OffsetObservableScrollView<Content: View>: View {
|
||||
var axes: Axis.Set = .vertical
|
||||
var showsIndicators: Bool = true
|
||||
|
||||
@Binding var scrollOffset: CGPoint
|
||||
@ViewBuilder var content: (ScrollViewProxy) -> Content
|
||||
|
||||
@Namespace var coordinateSpaceName: Namespace.ID
|
||||
|
||||
init(
|
||||
_ axes: Axis.Set = .vertical,
|
||||
showsIndicators: Bool = true,
|
||||
scrollOffset: Binding<CGPoint>,
|
||||
@ViewBuilder content: @escaping (ScrollViewProxy) -> Content
|
||||
) {
|
||||
self.axes = axes
|
||||
self.showsIndicators = showsIndicators
|
||||
self._scrollOffset = scrollOffset
|
||||
self.content = content
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView(axes, showsIndicators: showsIndicators) {
|
||||
ScrollViewReader { scrollViewProxy in
|
||||
VStack(spacing: 0) {
|
||||
// 맨 위로 스크롤하기 위한 anchor 뷰 (id "TOP")
|
||||
Color.clear
|
||||
.frame(height: 1)
|
||||
.id("TOP")
|
||||
content(scrollViewProxy)
|
||||
.overlay {
|
||||
GeometryReader { geometryProxy in
|
||||
Color.clear
|
||||
.preference(
|
||||
key: ScrollOffsetPreferenceKey.self,
|
||||
value: CGPoint (
|
||||
x: -geometryProxy.frame(in: .named(coordinateSpaceName)).minX,
|
||||
y: -geometryProxy.frame(in: .named(coordinateSpaceName)).minY
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.coordinateSpace(name: coordinateSpaceName)
|
||||
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
|
||||
// printLog("\(scrollOffset) -> \(value)")
|
||||
scrollOffset = value
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ScrollOffsetPreferenceKey: PreferenceKey {
|
||||
static var defaultValue: CGPoint = .zero
|
||||
|
||||
static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) {
|
||||
value = nextValue()
|
||||
}
|
||||
}
|
|
@ -39,6 +39,9 @@ struct IntroView: View {
|
|||
|
||||
.onAppear {
|
||||
printLog("IntroView_onAppear")
|
||||
#if LOCAL
|
||||
naviState.set(act: .RESET, path: .Login)
|
||||
#else
|
||||
subscribeAlertAction()
|
||||
loadVersion()
|
||||
.sink { completion in
|
||||
|
@ -66,7 +69,7 @@ struct IntroView: View {
|
|||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
|
86
AcaMate/1. View/12. Main/121. Home/AttendanceBoxView.swift
Normal file
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// AttendanceView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/7/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AttendanceBoxView: View {
|
||||
@State var monthlyGroup: (Int,Int) = (5,10)//(0,0)
|
||||
@State var dailyGroup: (Int,Int) = (3,4)//(0,0)
|
||||
|
||||
var body: some View {
|
||||
DashBoardView(image: Image(.Icon.attendance),
|
||||
title: "출석")
|
||||
{
|
||||
HStack(spacing: 4) {
|
||||
AttCellView(isDaily: false, valueGroup: $monthlyGroup)
|
||||
Spacer()
|
||||
AttCellView(isDaily: true, valueGroup: $dailyGroup)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
} moreAction: {
|
||||
// MARK: TO-DO
|
||||
// 출석 쪽 더보기 동작 만들기
|
||||
print("123")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AttCellView: View {
|
||||
@StateObject var btnVM = ButtonViewModel()
|
||||
@State private var cellBtnID = UUID()
|
||||
let isDaily: Bool
|
||||
|
||||
@Binding var valueGroup: (Int,Int)
|
||||
@State private var cellText: (name: String, group: String) = ("","")
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
CircleBtnView(vm: btnVM, id: cellBtnID)
|
||||
VStack(alignment: .leading,spacing: 0) {
|
||||
HStack(alignment: .center, spacing: 2) {
|
||||
Text("\(cellText.name)").font(.nps(font: .bold, size: 12))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
Text("출석").font(.nps(size: 12))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
}
|
||||
.padding(.top,4)
|
||||
HStack(alignment: .center, spacing: 2) {
|
||||
Spacer()
|
||||
Text("\(valueGroup.0)").font(.nps(font: .bold, size: 20))
|
||||
.foregroundStyle(((Double(valueGroup.0)/Double(valueGroup.1)) < 0.7) ? Color(.Other.red) : Color(.Other.blue))
|
||||
.frame(width: 28,alignment: .center)
|
||||
Text("/").font(.nps(size: 12))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
Text("\(valueGroup.1)").font(.nps(font: .bold, size: 20))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
.frame(width: 28,alignment: .center)
|
||||
Text("\(cellText.group)")
|
||||
.font(.nps(size: 16))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
}
|
||||
.onAppear {
|
||||
btnVM.setImage(for: cellBtnID,
|
||||
newImage: isDaily ? Image(.Icon.attendanceDaily) : Image(.Icon.attendanceMonthly))
|
||||
btnVM.setSize(for: cellBtnID, newWidth: 48, newHeight: 48)
|
||||
btnVM.setIsReverse(for: cellBtnID, newValue: true)
|
||||
btnVM.setIsSelected(for: cellBtnID, newValue: true)
|
||||
|
||||
if isDaily {
|
||||
cellText = ("일일", "시간")
|
||||
} else {
|
||||
cellText = ("월간", "일")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
AcaMate/1. View/12. Main/121. Home/CalendarBoxView.swift
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// CalendarBoxView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/10/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CalendarBoxView: View {
|
||||
@State var summaryCalDataList: [SummaryCalendar]
|
||||
var body: some View {
|
||||
|
||||
DashBoardView(image: Image(.Icon.calendar), title: "최근 일정") {
|
||||
if !summaryCalDataList.isEmpty {
|
||||
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: {
|
||||
printLog("최근일정의 더보기")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct CalCellView: View {
|
||||
var summaryCalData: SummaryCalendar
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
HStack (spacing: 12) {
|
||||
Image(.Icon.clock)
|
||||
.resizable()
|
||||
.frame(width: 24, height: 24, alignment: .center)
|
||||
Text("\(summaryCalData.date)")
|
||||
.font(.nps(size: 20))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
Spacer(minLength: 1)
|
||||
}
|
||||
HStack(spacing: 0) {
|
||||
Spacer(minLength: 1)
|
||||
Text("\(summaryCalData.summary)")
|
||||
.font(.nps(size: 20))
|
||||
.foregroundStyle(Color(.Text.black))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
|
||||
}
|
||||
.padding(12)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(Color(.Second.normal), lineWidth: 2)
|
||||
.fill(Color(.Second.light))
|
||||
}
|
||||
.onTapGesture {
|
||||
printLog("캘린더 내부 셀 클릭")
|
||||
// MARK: TO-DO
|
||||
// Summary의 날짜 위치로 이동하게 하는걸로
|
||||
// SummaryCalendar' id 를 이용하는걸로
|
||||
// 해당 날짜의 해당 이벤트가 아니라 그냥 해당 날짜로 이동하는게 좋을 것으로 보임
|
||||
}
|
||||
}
|
||||
}
|
||||
|
53
AcaMate/1. View/12. Main/121. Home/DashBoardView.swift
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// DashBoardView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/10/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct DashBoardView<Content: View>: View {
|
||||
@StateObject var btnVM = ButtonViewModel()
|
||||
@State private var attMoreBtnID = UUID()
|
||||
let image: Image
|
||||
let title: String
|
||||
@ViewBuilder let content: Content
|
||||
var moreAction: VOID_TO_VOID?
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 0){
|
||||
image
|
||||
.resizable()
|
||||
.frame(width: 24, height: 24, alignment: .center)
|
||||
.padding([.trailing],4)
|
||||
Text("\(title)")
|
||||
.font(.nps(font: .bold, size: 20))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
Spacer()
|
||||
SimpleBtnView(vm: btnVM, id: attMoreBtnID)
|
||||
}
|
||||
.padding([.bottom],2)
|
||||
|
||||
Rectangle()
|
||||
.frame(maxWidth: .infinity, maxHeight: 2)
|
||||
.foregroundStyle(Color(.Second.normal))
|
||||
.padding([.bottom],12)
|
||||
|
||||
content
|
||||
}
|
||||
.padding(24)
|
||||
.onAppear {
|
||||
btnVM.setSize(for: attMoreBtnID, newWidth: 40, newHeight: 24)
|
||||
btnVM.setText(for: attMoreBtnID, newText: "더보기", newFont: .nps(size: 12))
|
||||
btnVM.setTextColor(for: attMoreBtnID, newColor: .Text.disabled)
|
||||
|
||||
if let action = moreAction {
|
||||
btnVM.setAction(for: attMoreBtnID, newAction: action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
72
AcaMate/1. View/12. Main/121. Home/HomeView.swift
Normal file
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// HomeView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/11/25.
|
||||
//
|
||||
|
||||
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 {
|
||||
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
|
||||
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: [
|
||||
SummaryCalendar(id: "123", date: "2025-02-28", summary: "요약내용입니다."),
|
||||
SummaryCalendar(id: "123", date: "2025-02-28", summary: "요약내용입니다.")])
|
||||
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: [
|
||||
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)
|
||||
])
|
||||
}
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.foregroundStyle(Color(.Other.cell))
|
||||
}
|
||||
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if topViewState {
|
||||
VStack(spacing: 0) {
|
||||
TopView(titleName: "Name")
|
||||
.transition(.move(edge: .top))
|
||||
.animation(.easeInOut, value: scrollOffset)
|
||||
Spacer(minLength: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
173
AcaMate/1. View/12. Main/121. Home/ManagementBoxView.swift
Normal file
|
@ -0,0 +1,173 @@
|
|||
//
|
||||
// ManagementBoxView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/10/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ManagementBoxView: View {
|
||||
@StateObject var btnVM = ButtonViewModel()
|
||||
@State private var leftBtnID = UUID()
|
||||
@State private var rightBtnID = UUID()
|
||||
@State var managementList: [SummaryManagement]
|
||||
@State private var countNum: Int = 0
|
||||
|
||||
var body: some View {
|
||||
DashBoardView(image: Image(.Icon.edu), title: "학습 관리") {
|
||||
if managementList.count >= 1 {
|
||||
HStack {
|
||||
SimpleBtnView(vm: btnVM, id: leftBtnID)
|
||||
Spacer(minLength: 1)
|
||||
SimpleBtnView(vm: btnVM, id: rightBtnID)
|
||||
}
|
||||
.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: {
|
||||
// MARK: TO-DO
|
||||
// 학습관리 전체 페이지 이동 로직 추가
|
||||
printLog("학습 관리의 더보기")
|
||||
}
|
||||
.onAppear {
|
||||
btnVM.setImage(for: leftBtnID, newImage: Image(.Icon.left))
|
||||
btnVM.setSize(for: leftBtnID, newWidth: 24, newHeight: 24)
|
||||
btnVM.setAction(for: leftBtnID) { countNum -= 1 }
|
||||
btnVM.setIsUsable(for: leftBtnID, newValue: false)
|
||||
|
||||
btnVM.setImage(for: rightBtnID, newImage: Image(.Icon.right))
|
||||
btnVM.setSize(for: rightBtnID, newWidth: 24, newHeight: 24)
|
||||
btnVM.setAction(for: rightBtnID) { countNum += 1 }
|
||||
if managementList.count == 0 {
|
||||
btnVM.setIsUsable(for: rightBtnID, newValue: false)
|
||||
}
|
||||
|
||||
}
|
||||
.onChange(of: countNum) { oldValue, newValue in
|
||||
if countNum == 0 {
|
||||
btnVM.setIsUsable(for: leftBtnID, newValue: false)
|
||||
|
||||
} else if 0 < countNum && countNum < managementList.count-1 {
|
||||
btnVM.setIsUsable(for: leftBtnID, newValue: true)
|
||||
btnVM.setIsUsable(for: rightBtnID, newValue: true)
|
||||
}
|
||||
else {
|
||||
btnVM.setIsUsable(for: rightBtnID, newValue: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MangCellView: View {
|
||||
@StateObject var btnVM = ButtonViewModel()
|
||||
@Binding var summaryMang: SummaryManagement
|
||||
@State var ratioBtn = UUID()
|
||||
@State var flagBtn = UUID()
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
VStack(spacing: 8) {
|
||||
HStack(spacing: 12) {
|
||||
Image(.Icon.management)
|
||||
.resizable()
|
||||
.frame(width: 24, height: 24, alignment: .center)
|
||||
Text("\(summaryMang.title)")
|
||||
.font(.nps(size: 20))
|
||||
.foregroundStyle(Color(.Text.title))
|
||||
Spacer(minLength: 1)
|
||||
}
|
||||
HStack(spacing: 4) {
|
||||
Spacer(minLength: 1)
|
||||
Text("\(summaryMang.teacher)")
|
||||
.font(.nps(size: 12))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
Text("선생님")
|
||||
.font(.nps(size: 12))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// 상단 내용은 여까지
|
||||
HStack(spacing: 6) {
|
||||
HStack(spacing: 10) {
|
||||
CircleBtnView(vm: btnVM, id: ratioBtn)
|
||||
VStack(spacing: 10) {
|
||||
HStack(spacing: 4) {
|
||||
Text("학습")
|
||||
.font(.nps(size: 16))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
Text("진도")
|
||||
.font(.nps(font: .bold, size: 16))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
Spacer(minLength: 1)
|
||||
}
|
||||
HStack(spacing: 4) {
|
||||
Spacer(minLength: 1)
|
||||
Text("\(summaryMang.ratio)")
|
||||
.font(.nps(font: .bold, size: 24))
|
||||
.foregroundStyle(summaryMang.ratio > 70 ? Color(.Other.blue):Color(.Other.red))
|
||||
Text("%")
|
||||
.font(.nps(font: .bold, size: 20))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
|
||||
}
|
||||
}
|
||||
VStack(spacing: 10) {
|
||||
HStack(spacing: 4) {
|
||||
Text("숙제")
|
||||
.font(.nps(font: .bold, size: 16))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
Text("개수")
|
||||
.font(.nps(size: 16))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
|
||||
Spacer(minLength: 1)
|
||||
}
|
||||
HStack(spacing: 4) {
|
||||
Spacer(minLength: 1)
|
||||
Text("\(summaryMang.homework)")
|
||||
.font(.nps(font: .bold, size: 24))
|
||||
.foregroundStyle(Color(.Second.normal))
|
||||
Text("개")
|
||||
.font(.nps(font: .bold, size: 20))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
.padding(12)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(Color(.Second.normal), lineWidth: 2)
|
||||
.fill(Color(.Second.light))
|
||||
}
|
||||
.onTapGesture {
|
||||
// MARK: TO-DO
|
||||
// 해당 과목의 학습관리 페이지 이동 로직 추가
|
||||
printLog(summaryMang)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
58
AcaMate/1. View/12. Main/121. Home/NoticeBoxView.swift
Normal file
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// NoticeBoxView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/11/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct NoticeBoxView: View {
|
||||
@State var noticeList: [SummaryNotice]
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 값없을때도 없다는거 보여주는 창 띄우기
|
||||
|
||||
} moreAction: {
|
||||
// MARK: TO-DO
|
||||
// 공지사항 전체 페이지 이동 로직 추가
|
||||
printLog("공지사항의 더보기")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NotiCellView: View {
|
||||
var summaryNoti: SummaryNotice
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
HStack(spacing: 12) {
|
||||
Image(summaryNoti.new ? .Icon.noticeNew : .Icon.noticeOld)
|
||||
.resizable()
|
||||
.frame(width: 24, height: 24, alignment: .center)
|
||||
Text("\(summaryNoti.title)")
|
||||
.font(.nps(size: 20))
|
||||
.foregroundStyle(Color(.Text.title))
|
||||
Spacer(minLength: 1)
|
||||
}
|
||||
HStack(spacing: 4) {
|
||||
Spacer(minLength: 1)
|
||||
Text("날짜 :")
|
||||
.font(.nps(size: 12))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
Text("\(summaryNoti.date)")
|
||||
.font(.nps(size: 12))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
136
AcaMate/1. View/12. Main/121. Home/TopProfileView.swift
Normal file
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// TopProfileView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/5/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TopProfileView: View {
|
||||
@StateObject var btnVM = ButtonViewModel()
|
||||
|
||||
var userType: UserType
|
||||
|
||||
// MARK: TO-DO
|
||||
// 여기서 이름 떙겨오는것도 고민을 해야 함
|
||||
var childrenList: [String] = ["name1", "name2", "name3"]
|
||||
|
||||
var academyName: String = "Academy' NAME"
|
||||
var myName: String = "Name"
|
||||
|
||||
@State private var typeName: String = "유형"
|
||||
@State private var ParentsSelectID: UUID?
|
||||
|
||||
@State private var shopID = UUID()
|
||||
@State private var notifyID = UUID()
|
||||
@State private var childIDList: [UUID] = []
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0){
|
||||
VStack(alignment: .center, spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
SimpleBtnView(vm: btnVM, id: shopID)
|
||||
Spacer(minLength: 1)
|
||||
SimpleBtnView(vm: btnVM, id: notifyID)
|
||||
} /// 최상단 버튼 Stack
|
||||
ZStack{
|
||||
Circle()
|
||||
.stroke(Color(.Second.normal) ,lineWidth: 4)
|
||||
.frame(width: 200, height: 200)
|
||||
|
||||
Text("\(typeName)")
|
||||
.font(.nps(font: .bold, size: 48))
|
||||
.foregroundStyle(Color(.Second.normal))
|
||||
|
||||
|
||||
} /// 중앙 원형 Stack
|
||||
.padding([.top, .bottom], 40)
|
||||
VStack(alignment: .center, spacing: 8) {
|
||||
Text("\(self.academyName)")
|
||||
.frame(alignment: .center)
|
||||
.font(.nps(font: .bold, size: 36))
|
||||
.foregroundStyle(Color(.Text.title))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.truncationMode(.tail)
|
||||
Text("\(self.myName)")
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(alignment: .center)
|
||||
.font(.nps(size: 18))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
} /// 위쪽 VStack
|
||||
.padding(EdgeInsets(top: 24, leading: 24, bottom: 12, trailing: 24))
|
||||
|
||||
// MARK: TO-DO
|
||||
// 여기에 가로스크롤 넣어야 할거 같음
|
||||
if userType == .Parent {
|
||||
HStack(spacing: 0) {
|
||||
ForEach(Array(childIDList.enumerated()),id: \.offset){ index, id in
|
||||
CircleBtnView(vm: btnVM, id: id)
|
||||
if index != childIDList.count-1 {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
} /// 아래쪽 HStack
|
||||
.padding([.leading, .trailing, .bottom], 24)
|
||||
}
|
||||
}
|
||||
.fullDrawView(.Other.cell)
|
||||
|
||||
|
||||
.onAppear {
|
||||
let topBtnIDList = [shopID,notifyID]
|
||||
let iconList = [Image(.Icon.market), Image(.Icon.notificationSET)]
|
||||
|
||||
topBtnIDList.enumerated().forEach { (index, id) in
|
||||
btnVM.setImage(for: topBtnIDList[index], newImage: iconList[index])
|
||||
btnVM.setSize(for: topBtnIDList[index], newWidth: 40, newHeight: 40)
|
||||
}
|
||||
// MARK: TO-DO
|
||||
// 마켓 버튼과 알림 버튼 동작 로직 구현하기
|
||||
|
||||
|
||||
switch self.userType {
|
||||
case .Student:
|
||||
typeName = "학생"
|
||||
case .Parent:
|
||||
typeName = "학부모"
|
||||
for _ in 0..<childrenList.count {
|
||||
childIDList.append(UUID())
|
||||
}
|
||||
ParentsSelectID = childIDList[0]
|
||||
btnVM.setIsSelected(for: childIDList[0], newValue: true)
|
||||
|
||||
childIDList.enumerated().forEach { (index, id) in
|
||||
btnVM.setImage(for: id, newImage: Image(.Icon.face))
|
||||
btnVM.setSize(for: id, newWidth: 64, newHeight: 64)
|
||||
btnVM.setText(for: id, newText: "\(childrenList[index])", newFont: .nps(size: 12))
|
||||
// MARK: TO-DO
|
||||
// 좀더 자세한 동작 로직 구현 필요
|
||||
btnVM.setAction(for: id) {
|
||||
if let selectID = ParentsSelectID {
|
||||
btnVM.setIsSelected(for: selectID, newValue: false)
|
||||
}
|
||||
btnVM.setIsSelected(for: id, newValue: true)
|
||||
ParentsSelectID = id
|
||||
}
|
||||
}
|
||||
|
||||
case .Teacher:
|
||||
typeName = "선생님"
|
||||
case .Admin:
|
||||
typeName = "관리자"
|
||||
case .Employee:
|
||||
typeName = "직원"
|
||||
case .ETC:
|
||||
typeName = "방문객"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
//
|
||||
// TopProfileView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/5/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TopProfileView: View {
|
||||
var userType: UserType = .ETC
|
||||
var childrenList: [String] = ["name1", "name2", "name3"]
|
||||
var academyName: String = "Academy' NAME"
|
||||
var myName: String = "Name"
|
||||
|
||||
@State var childNum: Int = 0
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0){
|
||||
VStack(alignment: .center, spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
// SimpleBtnView(image: Image(.Icon.market), title: nil, font: nil,
|
||||
// width: 40, height: 40)
|
||||
// .doAction {
|
||||
// printLog("TEST")
|
||||
// }
|
||||
Spacer(minLength: 1)
|
||||
// SimpleBtnView(image: Image(.Icon.notificationSET), title: nil, font: nil,
|
||||
// width: 40, height: 40)
|
||||
// .doAction {
|
||||
// printLog("TEST")
|
||||
// }
|
||||
} /// 최상단 버튼 Stack
|
||||
ZStack{
|
||||
Circle()
|
||||
.stroke(Color(.Second.normal) ,lineWidth: 4)
|
||||
.frame(width: 200, height: 200)
|
||||
switch self.userType {
|
||||
case .Student:
|
||||
// MARK: TO-DO
|
||||
// 학생일때 표시하는거 어떻게 할 건지 좀 더 고민을 해보자
|
||||
Image(.Icon.face)
|
||||
.resizable()
|
||||
.frame(width: 150, height: 150)
|
||||
case .Parent:
|
||||
Text("학부모")
|
||||
.font(.nps(font: .bold, size: 48))
|
||||
.foregroundStyle(Color(.Second.normal))
|
||||
case .Teacher:
|
||||
Text("선생님")
|
||||
.font(.nps(font: .bold, size: 48))
|
||||
.foregroundStyle(Color(.Second.normal))
|
||||
case .Admin:
|
||||
Text("관리자")
|
||||
.font(.nps(font: .bold, size: 48))
|
||||
.foregroundStyle(Color(.Second.normal))
|
||||
case .Employee:
|
||||
Text("직원")
|
||||
.font(.nps(font: .bold, size: 48))
|
||||
.foregroundStyle(Color(.Second.normal))
|
||||
case .ETC:
|
||||
Text("방문객")
|
||||
.font(.nps(font: .bold, size: 48))
|
||||
.foregroundStyle(Color(.Second.normal))
|
||||
}
|
||||
} /// 중앙 원형 Stack
|
||||
.padding([.top, .bottom], 40)
|
||||
VStack(alignment: .center, spacing: 8) {
|
||||
Text("\(self.academyName)")
|
||||
.frame(alignment: .center)
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.nps(font: .bold, size: 36))
|
||||
Text("\(self.myName)")
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(alignment: .center)
|
||||
.font(.nps(size: 18))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
}
|
||||
} /// 위쪽 VStack
|
||||
.padding(EdgeInsets(top: 24, leading: 24, bottom: 12, trailing: 24))
|
||||
|
||||
HStack(spacing: 0) {
|
||||
// ForEach(Array(childrenList.enumerated()),id: \.offset){ index, name in
|
||||
// CircleBtnView(title: "\(name)", image: Image(.Icon.face),
|
||||
// // MARK: TO-DO
|
||||
// /// 이 바인딩 이거 잘 작동 되는지 후에 확인 할것
|
||||
// isSelected: Binding(
|
||||
// get: { self.childNum == index },
|
||||
// set: { _ in self.childNum = index }
|
||||
// ), isReverse: false) {
|
||||
// self.childNum = index
|
||||
// }
|
||||
// if index != childrenList.count-1 {
|
||||
// Spacer()
|
||||
// }
|
||||
// }
|
||||
} /// 아래쪽 HStack
|
||||
.padding([.leading, .trailing], 24)
|
||||
}
|
||||
.fullDrawView(.Other.cell)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
TopProfileView()
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// ManagementView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/11/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ManagementView: View {
|
||||
var body: some View {
|
||||
Text("학습 관리")
|
||||
}
|
||||
}
|
14
AcaMate/1. View/12. Main/123. Chatting/ChattingView.swift
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// ChattingView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/11/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ChattingView: View {
|
||||
var body: some View {
|
||||
Text("채팅")
|
||||
}
|
||||
}
|
14
AcaMate/1. View/12. Main/124. Calendar/CalendarView.swift
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// CalendarView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/11/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CalendarView: View {
|
||||
var body: some View {
|
||||
Text("일정")
|
||||
}
|
||||
}
|
15
AcaMate/1. View/12. Main/125. Etc/EtcView.swift
Normal file
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// EtcView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/11/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct EtcView: View {
|
||||
var body: some View {
|
||||
Text("더보기")
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ struct BottomView: View {
|
|||
@State private var calendarID = UUID()
|
||||
@State private var etcID = UUID()
|
||||
|
||||
@Binding var menuName: MenuName
|
||||
var body: some View {
|
||||
let idList: [UUID] = [homeID,managementID,chattingID,calendarID,etcID]
|
||||
|
||||
|
@ -29,11 +30,7 @@ struct BottomView: View {
|
|||
|
||||
}
|
||||
}
|
||||
// MARK: TO-DO
|
||||
// 이거 패딩 제대로 안먹는거 이유 찾기
|
||||
.padding([.top],12)
|
||||
.padding([.horizontal],24)
|
||||
// .padding(EdgeInsets(top: 12, leading: 24, bottom: 0, trailing: 24))
|
||||
.padding(EdgeInsets(top: 12, leading: 24, bottom: 0, trailing: 24))
|
||||
|
||||
.background {
|
||||
Rectangle()
|
||||
|
@ -43,13 +40,14 @@ struct BottomView: View {
|
|||
.frame(maxWidth: .infinity)
|
||||
.onAppear {
|
||||
let idList: [UUID] = [homeID,managementID,chattingID,calendarID,etcID]
|
||||
let btnText: [String] = ["홈", "학습 관리", "채팅", "일정", "더보기"]
|
||||
//["홈", "학습 관리", "채팅", "일정", "더보기"]
|
||||
let btnType: [MenuName] = [.Home, .Management, .Chatting, .Calendar, .Etc]
|
||||
let btnImage: [Image] = [Image(.Icon.home),Image(.Icon.management),Image(.Icon.chatting),Image(.Icon.calendar),Image(.Icon.etc)]
|
||||
|
||||
idList.enumerated().forEach { (index, id) in
|
||||
btnVM.btnStates[id] = ButtonState()
|
||||
btnVM.setSize(for: id, newWidth: 48, newHeight: 48)
|
||||
btnVM.setText(for: id, newText: btnText[index],
|
||||
btnVM.setSize(for: id, newWidth: 52, newHeight: 52)
|
||||
btnVM.setText(for: id, newText: btnType[index].rawValue,
|
||||
newFont: .nps(font: .bold, size: 6))
|
||||
btnVM.setImage(for: id, newImage: btnImage[index])
|
||||
|
||||
|
@ -60,6 +58,7 @@ struct BottomView: View {
|
|||
btnVM.setIsSelected(for: $0, newValue: false)
|
||||
}
|
||||
}
|
||||
menuName = btnType[index]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,22 +13,44 @@ struct MainView: View {
|
|||
@State var cancellables: Set<AnyCancellable> = []
|
||||
@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) {
|
||||
TopView(titleName: "Name")
|
||||
Group {
|
||||
switch menuName {
|
||||
case .Home:
|
||||
HomeView()
|
||||
case .Management:
|
||||
ManagementView()
|
||||
case .Chatting:
|
||||
ChattingView()
|
||||
case .Calendar:
|
||||
CalendarView()
|
||||
case .Etc:
|
||||
EtcView()
|
||||
}
|
||||
}
|
||||
Spacer(minLength: 1)
|
||||
|
||||
Spacer()
|
||||
|
||||
BottomView()
|
||||
BottomView(menuName: $menuName)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
// .backgroundStyle(Color(.Normal.dark))
|
||||
// .safeAreaInset(edge: .bottom, spacing: 0) {
|
||||
// Color(.Normal.dark)
|
||||
//// .frame(height: 0)
|
||||
// }
|
||||
}
|
||||
// .fullDrawView(Color(.Normal.dark))
|
||||
.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
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,13 +11,12 @@ struct TopView: View {
|
|||
@StateObject var btnVM = ButtonViewModel()
|
||||
|
||||
@State var titleName: String = ""
|
||||
@State var changeLogo: Bool = false
|
||||
@State var tailLogo: Bool = false
|
||||
|
||||
@State private var leftBtnID = UUID()
|
||||
@State private var rightBtnID = UUID()
|
||||
|
||||
var myType: UserType = .Teacher
|
||||
//MARK: - 변경 값
|
||||
var myType: UserType = .Student
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .center, spacing: 0) {
|
||||
|
@ -36,6 +35,7 @@ struct TopView: View {
|
|||
.foregroundStyle(Color(.Text.detail))
|
||||
.font(.nps(font: .bold, size: 20))
|
||||
Spacer()
|
||||
|
||||
SimpleBtnView(vm: btnVM, id: rightBtnID)
|
||||
.padding(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 24))
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ struct ButtonState {
|
|||
var textColor: Color = .Text.detail
|
||||
var isUsable: Bool = true
|
||||
|
||||
|
||||
|
||||
// -- CircleBtn 전용 -- //
|
||||
|
||||
var isSelected: Bool = false
|
||||
|
|
14
AcaMate/2. Model/Calendar Data.swift
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// SimpleCalendar.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/10/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SummaryCalendar {
|
||||
var id: String
|
||||
var date: String
|
||||
var summary: String
|
||||
}
|
16
AcaMate/2. Model/Management Data.swift
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// ManagementData.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/10/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SummaryManagement {
|
||||
var id: String
|
||||
var title: String
|
||||
var teacher: String
|
||||
var ratio: Int
|
||||
var homework: Int
|
||||
}
|
|
@ -13,6 +13,8 @@ struct NaviState: Equatable {
|
|||
var act: NaviAction
|
||||
var path: PathName
|
||||
|
||||
|
||||
|
||||
static func == (lhs: NaviState, rhs: NaviState) -> Bool {
|
||||
return lhs.act == rhs.act && lhs.path == rhs.path
|
||||
}
|
||||
|
@ -21,6 +23,7 @@ struct NaviState: Equatable {
|
|||
self.act = act
|
||||
self.path = path
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum NaviAction: Hashable {
|
||||
|
@ -45,3 +48,11 @@ enum PathName: Hashable {
|
|||
|
||||
case NONE
|
||||
}
|
||||
|
||||
enum MenuName: String, Hashable {
|
||||
case Home = "홈"
|
||||
case Management = "학습 관리"
|
||||
case Chatting = "채팅"
|
||||
case Calendar = "일정"
|
||||
case Etc = "더보기"
|
||||
}
|
||||
|
|
15
AcaMate/2. Model/Notice Data.swift
Normal file
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// Notice Data.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 2/11/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SummaryNotice {
|
||||
var id: String
|
||||
var title: String
|
||||
var date: String
|
||||
var new: Bool
|
||||
}
|
|
@ -10,7 +10,6 @@ import Combine
|
|||
|
||||
class AppViewModel: ObservableObject {
|
||||
@Published var isLoading: Bool = false
|
||||
|
||||
@Published var showAlert: Bool = false
|
||||
|
||||
var alertData: AlertData = .init(body: "")
|
||||
|
|
|
@ -60,6 +60,13 @@ class ButtonViewModel: ObservableObject {
|
|||
btnStates[id] = state
|
||||
objectWillChange.send()
|
||||
}
|
||||
//
|
||||
// func setDisable(for id: UUID, _ newValue: Bool) {
|
||||
// var state = btnStates[id] ?? ButtonState()
|
||||
// state.isUsable = newValue
|
||||
// btnStates[id] = state
|
||||
// objectWillChange.send()
|
||||
// }
|
||||
|
||||
// -- CircleBtn 전용 -- //
|
||||
|
||||
|
@ -74,7 +81,11 @@ class ButtonViewModel: ObservableObject {
|
|||
func setIsReverse(for id: UUID, newValue: Bool){
|
||||
var state = btnStates[id] ?? ButtonState()
|
||||
state.isReverse = newValue
|
||||
state.backColor = newValue ? Color(.Second.light) : Color(.Normal.normal)
|
||||
if state.isReverse {
|
||||
state.backColor = Color(.Second.light)
|
||||
} else {
|
||||
state.backColor = Color(.Normal.normal)
|
||||
}
|
||||
btnStates[id] = state
|
||||
objectWillChange.send()
|
||||
}
|
||||
|
|
|
@ -72,6 +72,42 @@ 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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension View {
|
||||
/// View에 전체적으로 색 입히기
|
||||
func fullDrawView(_ backColor: Color) -> some View {
|
||||
|
@ -104,16 +140,14 @@ extension View {
|
|||
.mask(shape.fill(LinearGradient(gradient: Gradient(colors: [.black, .clear]), startPoint: .topLeading, endPoint: .bottomTrailing)))
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func switchButtonStyle(_ animate: Bool) -> some View {
|
||||
if animate {
|
||||
self.buttonStyle(DefaultButtonStyle())
|
||||
} else {
|
||||
self.buttonStyle(PlainButtonStyle())
|
||||
// .allowsHitTesting(false)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
//extension BUtton
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.114",
|
||||
"green" : "0.114",
|
||||
"red" : "0.114"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Attendance.imageset/Attendance.png
vendored
Normal file
After Width: | Height: | Size: 741 B |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Attendance.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Attendance.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 779 B |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Attendance_Daily.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Attendance_Daily.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 630 B |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Attendance_Monthly.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Attendance_Monthly.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Clock.imageset/Clock.png
vendored
Normal file
After Width: | Height: | Size: 1.8 KiB |
15
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Clock.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Clock.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Drive OFF.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Drive OFF.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Drive OFF.imageset/Drive OFF.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Drive ON.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Drive ON.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Drive ON.imageset/Drive ON.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Drive.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Drive.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Drive.imageset/Drive.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
15
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Edu.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Edu.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Edu.imageset/Edu.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Flag.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Flag.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Flag.imageset/Flag.png
vendored
Normal file
After Width: | Height: | Size: 463 B |
15
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Left.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Left.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Left.imageset/Left.png
vendored
Normal file
After Width: | Height: | Size: 505 B |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Notice.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Notice.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Notice.imageset/Notice.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Notice_New.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Notice_New.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Notice_New.imageset/Notice_New.png
vendored
Normal file
After Width: | Height: | Size: 618 B |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Notice_Old.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Notice_Old.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Notice_Old.imageset/Notice_Old.png
vendored
Normal file
After Width: | Height: | Size: 579 B |
12
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Ratio.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Ratio.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Ratio.imageset/Ratio.png
vendored
Normal file
After Width: | Height: | Size: 1.8 KiB |
15
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Right.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Right.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Right.imageset/Right.png
vendored
Normal file
After Width: | Height: | Size: 470 B |
BIN
AcaMate/6. Resources/Images/Icon/Attendance.png
Normal file
After Width: | Height: | Size: 741 B |
BIN
AcaMate/6. Resources/Images/Icon/Attendance_Daily.png
Normal file
After Width: | Height: | Size: 779 B |
BIN
AcaMate/6. Resources/Images/Icon/Attendance_Monthly.png
Normal file
After Width: | Height: | Size: 630 B |
Before Width: | Height: | Size: 636 B After Width: | Height: | Size: 636 B |
Before Width: | Height: | Size: 543 B After Width: | Height: | Size: 543 B |
BIN
AcaMate/6. Resources/Images/Icon/Clock.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
AcaMate/6. Resources/Images/Icon/Drive OFF.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
AcaMate/6. Resources/Images/Icon/Drive ON.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
AcaMate/6. Resources/Images/Icon/Drive.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
AcaMate/6. Resources/Images/Icon/Edu.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 635 B After Width: | Height: | Size: 635 B |
BIN
AcaMate/6. Resources/Images/Icon/Flag.png
Normal file
After Width: | Height: | Size: 463 B |
Before Width: | Height: | Size: 604 B After Width: | Height: | Size: 604 B |
BIN
AcaMate/6. Resources/Images/Icon/Left.png
Normal file
After Width: | Height: | Size: 505 B |
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 577 B |
BIN
AcaMate/6. Resources/Images/Icon/Notice.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
AcaMate/6. Resources/Images/Icon/Notice_New.png
Normal file
After Width: | Height: | Size: 618 B |
BIN
AcaMate/6. Resources/Images/Icon/Notice_Old.png
Normal file
After Width: | Height: | Size: 579 B |
BIN
AcaMate/6. Resources/Images/Icon/Ratio.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
AcaMate/6. Resources/Images/Icon/Right.png
Normal file
After Width: | Height: | Size: 470 B |
10
README.md
|
@ -11,3 +11,13 @@
|
|||
2. kakao Login
|
||||
3. naver Map
|
||||
4. firebase
|
||||
|
||||
## Git Commit message
|
||||
1. [📝]: DOCS -> 문서 추가, 문서 수정 등
|
||||
2. [✨]: FEAT -> 새로운 기능의 추가
|
||||
3. [🔥]: FIRE -> 코드나 문서 등의 삭제
|
||||
4. [🐛]: FIX BUG -> 버그 수정
|
||||
5. [👷]: STYLE -> 직접적인 코드의 수정은 없는 경우 (빈줄 처리, 주석 등)
|
||||
6. [♻️]: REFACTOR -> 코드 리팩토링
|
||||
7. [✅]: TEST -> 테스트 코드 추가
|
||||
8. [📁]: CHORE: 빌드 업무, 패키지 매니저 수정 등 (.gitignore 수정 등)
|
||||
|
|