-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathWeekView.swift
More file actions
133 lines (114 loc) · 4.3 KB
/
WeekView.swift
File metadata and controls
133 lines (114 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//
// WeekView.swift
// Presentation
//
// Created by 최정인 on 7/20/25.
//
import SnapKit
import UIKit
protocol WeekViewDelegate: AnyObject {
func weekView(_ sender: WeekView, didSelectDate date: Date)
}
final class WeekView: UIView {
private enum Layout {
static let dateStackViewSpacing: CGFloat = 21
static let horizontalMargin: CGFloat = 20
static let dateStackViewHeight: CGFloat = 72
static let dateViewHeight: CGFloat = 55
}
private let dateStackView = UIStackView()
private var dateViews: [Date: DateView] = [:]
private let calendar = Calendar.current
private var selectedDate: Date
private var allCompletedDates: [Date] = []
weak var delegate: WeekViewDelegate?
init(date: Date = Date()) {
self.selectedDate = date
super.init(frame: .zero)
configureAttribute()
configureLayout()
updateWeekDateViews(date: date)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configureAttribute() {
dateStackView.axis = .horizontal
dateStackView.spacing = Layout.dateStackViewSpacing
dateStackView.alignment = .center
dateStackView.distribution = .equalSpacing
}
private func configureLayout() {
backgroundColor = BitnagilColor.gray99
addSubview(dateStackView)
dateStackView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.leading.equalToSuperview().offset(Layout.horizontalMargin)
make.trailing.equalToSuperview().inset(Layout.horizontalMargin)
make.height.equalTo(Layout.dateStackViewHeight)
}
}
// 현재 주의 첫째 날을 계산해줍니다.
private func calculateWeekStartDate(for date: Date) -> Date {
let weekday = calendar.component(.weekday, from: date)
let daysFromMonday = (weekday == 1) ? 6 : weekday - 2
return calendar.date(byAdding: .day, value: -daysFromMonday, to: date) ?? date
}
// 현재 주에 맞춰 DateView들 업데이트합니다.
func updateWeekDateViews(date: Date) {
dateViews.values.forEach {
$0.removeFromSuperview()
}
dateViews.removeAll()
let weekStartDate = calculateWeekStartDate(for: date)
let isSelectedDay = calendar.isDate(selectedDate, equalTo: date, toGranularity: .day)
if !isSelectedDay {
selectedDate = weekStartDate
}
for i in 0..<7 {
guard let date = calendar.date(byAdding: .day, value: i, to: weekStartDate)
else { continue }
let isSelected = calendar.isDate(date, inSameDayAs: selectedDate)
let isToday = calendar.isDate(date, inSameDayAs: Date())
let dateView = DateView(
date: date,
isSelected: isSelected,
isToday: isToday)
let isAllCompleted = allCompletedDates.contains(date)
if isAllCompleted {
dateView.updateAllCompleted(isCompleted: true)
}
dateView.didTapDateButton = { [weak self] date in
self?.selectDate(date: date)
}
dateViews[date] = dateView
dateStackView.addArrangedSubview(dateView)
dateView.snp.makeConstraints { make in
make.height.equalTo(Layout.dateViewHeight)
}
}
}
func updateAllCompletedState(allCompletedDates: [Date]) {
self.allCompletedDates = allCompletedDates
for dateView in dateViews {
if allCompletedDates.contains(dateView.key) {
dateView.value.updateAllCompleted(isCompleted: true)
} else {
dateView.value.updateAllCompleted(isCompleted: false)
}
}
}
// 날짜를 선택합니다.
private func selectDate(date: Date) {
selectedDate = date
updateSelectState()
delegate?.weekView(self, didSelectDate: date)
}
// 선택한 날짜의 dateView를 업데이트합니다.
private func updateSelectState() {
for (date, dateView) in dateViews {
let isSelected = calendar.isDate(date, inSameDayAs: selectedDate)
dateView.updateSelectState(isSelected: isSelected)
}
}
}