Skip to content

Commit bef228b

Browse files
Merge pull request #117 from TeamDMU/feat-#116/schedule
[FEAT] 학사일정 화면
2 parents 0eaa763 + dcd5fc7 commit bef228b

8 files changed

Lines changed: 188 additions & 67 deletions

File tree

DMU-iOS/DMU-iOS.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
F91189122B5DF82200492AB1 /* ReactiveMoya in Frameworks */ = {isa = PBXBuildFile; productRef = F91189112B5DF82200492AB1 /* ReactiveMoya */; };
1717
F91189142B5DF82200492AB1 /* RxMoya in Frameworks */ = {isa = PBXBuildFile; productRef = F91189132B5DF82200492AB1 /* RxMoya */; };
1818
F914AD822B47C85400706D48 /* SettingDepartmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F914AD812B47C85400706D48 /* SettingDepartmentView.swift */; };
19+
F915DC1E2C3E1ED6009F2ABA /* ScheduleDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F915DC1D2C3E1ED6009F2ABA /* ScheduleDatePickerView.swift */; };
1920
F92116172B90621000A5507A /* DepartmentNoticeDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = F92116162B90621000A5507A /* DepartmentNoticeDTO.swift */; };
2021
F92116192B90627100A5507A /* DepartmentNoticeRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = F92116182B90627100A5507A /* DepartmentNoticeRepository.swift */; };
2122
F921161B2B90642300A5507A /* DepartmentNoticeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F921161A2B90642300A5507A /* DepartmentNoticeService.swift */; };
@@ -119,6 +120,7 @@
119120
F90D26482B3D9C0100C864C4 /* Onboarding-Step-Two.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Onboarding-Step-Two.swift"; sourceTree = "<group>"; };
120121
F90D264A2B3DA00600C864C4 /* CustomKeyword.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomKeyword.swift; sourceTree = "<group>"; };
121122
F914AD812B47C85400706D48 /* SettingDepartmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingDepartmentView.swift; sourceTree = "<group>"; };
123+
F915DC1D2C3E1ED6009F2ABA /* ScheduleDatePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleDatePickerView.swift; sourceTree = "<group>"; };
122124
F92116162B90621000A5507A /* DepartmentNoticeDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DepartmentNoticeDTO.swift; sourceTree = "<group>"; };
123125
F92116182B90627100A5507A /* DepartmentNoticeRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DepartmentNoticeRepository.swift; sourceTree = "<group>"; };
124126
F921161A2B90642300A5507A /* DepartmentNoticeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DepartmentNoticeService.swift; sourceTree = "<group>"; };
@@ -448,6 +450,7 @@
448450
isa = PBXGroup;
449451
children = (
450452
F9AF3C532B416E5D001779E3 /* ScheduleView.swift */,
453+
F915DC1D2C3E1ED6009F2ABA /* ScheduleDatePickerView.swift */,
451454
);
452455
path = Views;
453456
sourceTree = "<group>";
@@ -958,6 +961,7 @@
958961
F929935E2B63E59F009DC2B6 /* ScheduleService.swift in Sources */,
959962
F9F85E6C2B75C6A1005EFAE2 /* DateFormatExtension.swift in Sources */,
960963
F93816882B561BEF00DA15F9 /* NotificationKeywordEditView.swift in Sources */,
964+
F915DC1E2C3E1ED6009F2ABA /* ScheduleDatePickerView.swift in Sources */,
961965
F9EE42482B3C302600307BA7 /* Department.swift in Sources */,
962966
F9AF3C672B418DFE001779E3 /* NoticeViewModel.swift in Sources */,
963967
F9AF3C572B416EE2001779E3 /* MealView.swift in Sources */,

DMU-iOS/DMU-iOS/Features/Schedule/ViewModels/ScheduleViewModel.swift

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@
88
import Foundation
99

1010
class ScheduleViewModel: ObservableObject {
11-
12-
@Published var currentDate = Date()
11+
12+
@Published var currentDate = Date() {
13+
didSet {
14+
selectedYear = calendar.component(.year, from: currentDate)
15+
selectedMonth = calendar.component(.month, from: currentDate)
16+
}
17+
}
18+
19+
@Published var selectedYear: Int = Calendar.current.component(.year, from: Date())
20+
@Published var selectedMonth: Int = Calendar.current.component(.month, from: Date())
21+
1322
@Published var schedules: [Schedule] = []
1423
@Published var isScheduleLoading = false
1524
@Published var isScheduleLoadingFailed = false
@@ -46,13 +55,12 @@ class ScheduleViewModel: ObservableObject {
4655
}
4756
}
4857

49-
// MARK: 학사일정 월 이동
50-
func changeMonth(by increment: Int) {
51-
guard let newDate = calendar.date(byAdding: .month, value: increment, to: currentDate) else { return }
52-
53-
currentDate = newDate
54-
55-
loadScheduleData()
58+
// MARK: 데이트피커를 통해 년월 선택
59+
func selectYearMonth() {
60+
if let newDate = Calendar.current.date(from: DateComponents(year: selectedYear, month: selectedMonth)) {
61+
currentDate = newDate
62+
loadScheduleData()
63+
}
5664
}
5765

5866
// MARK: 현재 년월
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// ScheduleDatePickerView.swift
3+
// DMU-iOS
4+
//
5+
// Created by 이예빈 on 7/10/24.
6+
//
7+
8+
import SwiftUI
9+
10+
struct ScheduleDatePickerView: View {
11+
12+
@StateObject var viewModel: ScheduleViewModel
13+
@Binding var isPresented: Bool
14+
15+
@State private var tempSelectedYear: Int = 0
16+
@State private var tempSelectedMonth: Int = 0
17+
18+
var body: some View {
19+
VStack {
20+
HStack {
21+
Picker("년도", selection: $tempSelectedYear) {
22+
Text("2024년").tag(2024)
23+
Text("2025년").tag(2025)
24+
}
25+
.pickerStyle(WheelPickerStyle())
26+
27+
Picker("", selection: $tempSelectedMonth) {
28+
ForEach(1..<13) { month in
29+
Text("\(month)").tag(month)
30+
}
31+
}
32+
.pickerStyle(WheelPickerStyle())
33+
}
34+
35+
CustomButton(title: "완료", action: {
36+
viewModel.selectedYear = tempSelectedYear
37+
viewModel.selectedMonth = tempSelectedMonth
38+
viewModel.selectYearMonth()
39+
isPresented = false
40+
}, isEnabled: true)
41+
}
42+
.onAppear {
43+
tempSelectedYear = viewModel.selectedYear
44+
tempSelectedMonth = viewModel.selectedMonth
45+
}
46+
}
47+
}
48+
49+
#Preview {
50+
ScheduleDatePickerView(viewModel: ScheduleViewModel(), isPresented: .constant(true))
51+
}

DMU-iOS/DMU-iOS/Features/Schedule/Views/ScheduleView.swift

Lines changed: 74 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,108 +11,100 @@ struct ScheduleView: View {
1111

1212
@StateObject var viewModel: ScheduleViewModel
1313

14+
@State private var isDatePickerPresented = false
15+
1416
var body: some View {
1517
ZStack{
1618
VStack {
17-
ScheduleTitle
19+
ScheduleTitleView()
1820

19-
ScheduleMonthNavigationBarView
21+
ScheduleMonthNavigationBarView(currentYearMonth: viewModel.currentYearMonth, isDatePickerPresented: $isDatePickerPresented)
2022

21-
SchedulesListView
23+
SchedulesListView(schedules: viewModel.schedules, loadScheduleData: viewModel.loadScheduleData)
2224
}
2325

2426
VStack {
2527
if viewModel.isScheduleLoadingFailed {
26-
VStack(alignment: .center) {
27-
Text("학사일정을 불러오지 못했어요")
28-
.font(.SemiBold20)
29-
.foregroundColor(Color.Gray600)
30-
.environment(\.sizeCategory, .large)
31-
.padding(.bottom, 12)
32-
Text("네트워크 상태를 확인한 후,\n새로고침 버튼을 눌러 페이지를 불러올 수 있어요.")
33-
.font(.Medium16)
34-
.foregroundColor(Color.Gray400)
35-
.environment(\.sizeCategory, .large)
36-
.padding(.bottom, 28)
37-
CustomButton(title: "새로고침", action: {
38-
viewModel.loadScheduleData()
39-
}, isEnabled: true)
40-
}
41-
.multilineTextAlignment(.center)
28+
ScheduleLoadingFailedView(loadScheduleData: viewModel.loadScheduleData)
4229
} else if viewModel.isScheduleLoading {
4330
LoadingView(lottieFileName: "DMforU_Loading_GIF")
4431
.frame(width: 100, height: 100)
4532
}
4633
}
4734
}
4835
.onAppear(perform: viewModel.loadScheduleData)
36+
.sheet(isPresented: $isDatePickerPresented) {
37+
ScheduleDatePickerView(viewModel: viewModel, isPresented: $isDatePickerPresented)
38+
.presentationDetents([
39+
.fraction(0.48)
40+
])
41+
.presentationDragIndicator(.hidden)
42+
}
4943
}
50-
51-
// MARK: 학사일정 화면 타이틀 뷰
52-
private var ScheduleTitle: some View {
44+
}
45+
46+
struct ScheduleTitleView: View {
47+
var body: some View {
5348
Text("학사일정")
5449
.font(.SemiBold20)
5550
.frame(maxWidth: .infinity)
5651
.foregroundColor(Color.Gray500)
5752
.environment(\.sizeCategory, .large)
5853
.padding()
5954
}
55+
}
56+
57+
struct ScheduleMonthNavigationBarView: View {
58+
let currentYearMonth: String
6059

61-
// MARK: 학사일정 화면 네비게이션바 뷰
62-
private var ScheduleMonthNavigationBarView: some View {
63-
HStack {
64-
if viewModel.currentYearMonth != "2023년 1월" {
65-
monthChangeButton(direction: -1, systemName: "chevron.left")
66-
} else {
67-
Spacer()
68-
}
69-
70-
Spacer().frame(minWidth: 100)
71-
72-
Text(viewModel.currentYearMonth)
60+
@Binding var isDatePickerPresented: Bool
61+
62+
var body: some View {
63+
HStack(spacing: 4) {
64+
Text(currentYearMonth)
7365
.font(.Medium16)
7466
.foregroundColor(Color.Gray500)
7567
.environment(\.sizeCategory, .large)
7668
.lineLimit(1)
7769

78-
Spacer().frame(minWidth: 100)
79-
80-
if viewModel.currentYearMonth != "2025년 2월" {
81-
monthChangeButton(direction: 1, systemName: "chevron.right")
82-
} else {
83-
Spacer()
84-
}
70+
Image(isDatePickerPresented ? "polygon-up" : "polygon-down")
71+
.resizable()
72+
.aspectRatio(contentMode: .fit)
73+
.frame(width: 16, height: 14)
8574
}
8675
.padding(.horizontal, 24)
87-
}
88-
89-
// MARK: 학사일정 화면 월 이동 버튼
90-
private func monthChangeButton(direction: Int, systemName: String) -> some View {
91-
Button(action: {
92-
viewModel.changeMonth(by: direction)
93-
}) {
94-
Image(systemName: systemName)
95-
.resizable()
96-
.frame(width: 12, height: 20)
97-
.foregroundColor(Color.blue300)
76+
.onTapGesture {
77+
isDatePickerPresented.toggle()
9878
}
9979
}
80+
}
81+
82+
struct SchedulesListView: View {
83+
let schedules: [Schedule]
84+
let loadScheduleData: () -> Void
10085

101-
// MARK: 학사일정 화면 일정 리스트 뷰
102-
private var SchedulesListView: some View {
86+
var body: some View {
10387
ScrollView {
10488
VStack(spacing: 0) {
105-
ForEach(viewModel.schedules) { schedule in
89+
ForEach(schedules) { schedule in
10690
ScheduleSingleView(for: schedule)
10791
}
10892
}
10993
}
11094
.refreshable {
111-
viewModel.loadScheduleData()
95+
loadScheduleData()
11296
}
11397
}
98+
}
99+
100+
struct ScheduleSingleView: View {
101+
let schedule: Schedule
102+
103+
init(for schedule: Schedule) {
104+
self.schedule = schedule
105+
}
114106

115-
private func ScheduleSingleView(for schedule: Schedule) -> some View {
107+
var body: some View {
116108
VStack {
117109
HStack {
118110
Text(schedule.scheduleDisplay)
@@ -126,7 +118,7 @@ struct ScheduleView: View {
126118
Text(schedule.detail)
127119
.font(.SemiBold14)
128120
.foregroundColor(Color.Gray500)
129-
.multilineTextAlignment(.center)
121+
.multilineTextAlignment(.trailing)
130122
.lineLimit(nil)
131123
.environment(\.sizeCategory, .large)
132124
}
@@ -141,6 +133,30 @@ struct ScheduleView: View {
141133
}
142134
}
143135

136+
struct ScheduleLoadingFailedView: View {
137+
let loadScheduleData: () -> Void
138+
139+
var body: some View {
140+
VStack(alignment: .center) {
141+
Text("학사일정을 불러오지 못했어요")
142+
.font(.SemiBold20)
143+
.foregroundColor(Color.Gray600)
144+
.environment(\.sizeCategory, .large)
145+
.padding(.bottom, 12)
146+
Text("네트워크 상태를 확인한 후,\n새로고침 버튼을 눌러 페이지를 불러올 수 있어요.")
147+
.font(.Medium16)
148+
.foregroundColor(Color.Gray400)
149+
.environment(\.sizeCategory, .large)
150+
.padding(.bottom, 28)
151+
CustomButton(title: "새로고침", action: {
152+
loadScheduleData()
153+
}, isEnabled: true)
154+
}
155+
.multilineTextAlignment(.center)
156+
}
157+
}
158+
159+
144160
#Preview {
145161
ScheduleView(viewModel: ScheduleViewModel())
146162
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "polygon-down.png",
5+
"idiom" : "universal",
6+
"scale" : "1x"
7+
},
8+
{
9+
"idiom" : "universal",
10+
"scale" : "2x"
11+
},
12+
{
13+
"idiom" : "universal",
14+
"scale" : "3x"
15+
}
16+
],
17+
"info" : {
18+
"author" : "xcode",
19+
"version" : 1
20+
}
21+
}
4.64 KB
Loading
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "polygon-up.png",
5+
"idiom" : "universal",
6+
"scale" : "1x"
7+
},
8+
{
9+
"idiom" : "universal",
10+
"scale" : "2x"
11+
},
12+
{
13+
"idiom" : "universal",
14+
"scale" : "3x"
15+
}
16+
],
17+
"info" : {
18+
"author" : "xcode",
19+
"version" : 1
20+
}
21+
}
4.6 KB
Loading

0 commit comments

Comments
 (0)