Skip to content

Commit 822f7f0

Browse files
committed
[Feat] TTimePicker 분단위 설정, Binding<Date> 추가
1 parent b0964ac commit 822f7f0

File tree

1 file changed

+76
-33
lines changed

1 file changed

+76
-33
lines changed

TnT/Projects/DesignSystem/Sources/Components/TimePicker/TTimePicker.swift

Lines changed: 76 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,51 @@ import SwiftUI
1010

1111
/// 시간, 분, 오전/오후 세 열을 나란히 배치하는 커스텀 타임 피커
1212
public struct TTimePicker: View {
13-
@State private var selectedHour: Int // 0 ~ 11 (표시 시에는 +1 해서 1~12)
14-
@State private var selectedMinute: Int // 0 ~ 59
15-
@State private var selectedPeriod: Int // 0: AM, 1: PM
13+
/// 선택된 시간이 종합되는 date
14+
@Binding private var date: Date
15+
/// 0 ~ 11 (표시 시에는 +1 해서 1~12)
16+
@State private var selectedHour: Int
17+
/// 분 선택은 내부적으로 분 배열의 인덱스로 저장 (예, minuteStep이 5면 0~11)
18+
@State private var selectedMinuteIndex: Int
19+
/// 0: AM, 1: PM
20+
@State private var selectedPeriod: Int
1621

17-
// 설정값
22+
/// 셀 높이
1823
let rowHeight: CGFloat = 35
24+
/// 피커에 표시되는 셀 개수
1925
let visibleCount: Int = 5
26+
/// 선택되지 않은 시간 텍스트 폰트
2027
let normalFont: Font = Typography.FontStyle.heading4.font
28+
/// 선택된 시간 텍스트 폰트
2129
let selectedFont: Font = Typography.FontStyle.heading3.font
30+
/// 선택되지 않은 시간 텍스트 컬러
2231
let normalColor: Color = .neutral400
32+
/// 선택된 시간 텍스트 컬러
2333
let selectedColor: Color = .neutral900
34+
/// 분 단위 설정
35+
let minuteStep: Int
36+
// 무한 스크롤 사용 여부 - false로 설정하면 유한 스크롤
37+
let infiniteScroll: Bool = true
38+
/// 실제 분 값을 계산
39+
/// ex - minuteStep이 5이면 내부 저장값 7 -> 35분
40+
public var selectedMinute: Int {
41+
selectedMinuteIndex * minuteStep
42+
}
2443

25-
// 무한 스크롤 사용 여부 (여기서 전체 피커에 대해 동일하게 설정할 수 있음)
26-
let infiniteScroll: Bool = true // false로 설정하면 유한 스크롤이 됩니다.
27-
28-
/// 현재 날짜와 시각을 기준으로 기본 선택값을 지정하는 initializer
29-
public init(
30-
selectedHour: Int = {
31-
let calendar = Calendar.current
32-
let hour24 = calendar.component(.hour, from: .now)
33-
// 12시간제로 변환 (0일 경우 12로, 나머지는 그대로)
34-
let hour12 = hour24 % 12 == 0 ? 12 : hour24 % 12
35-
// TTimePicker는 0이 "1시", 11이 "12시"에 해당하므로 -1 처리
36-
return hour12 - 1
37-
}(),
38-
selectedMinute: Int = Calendar.current.component(.minute, from: .now),
39-
selectedPeriod: Int = {
40-
let hour24 = Calendar.current.component(.hour, from: .now)
41-
// 0: AM, 1: PM (오전이면 0, 오후면 1)
42-
return hour24 < 12 ? 0 : 1
43-
}()
44-
) {
45-
_selectedHour = State(initialValue: selectedHour)
46-
_selectedMinute = State(initialValue: selectedMinute)
47-
_selectedPeriod = State(initialValue: selectedPeriod)
48-
}
44+
public init(selectedDate: Binding<Date>, minuteStep: Int = 1) {
45+
_date = selectedDate
46+
47+
let hour24 = Calendar.current.component(.hour, from: selectedDate.wrappedValue)
48+
let hour12 = hour24 % 12 == 0 ? 12 : hour24 % 12
49+
let hour = hour12 - 1
50+
_selectedHour = State(initialValue: hour)
51+
52+
let minute = Calendar.current.component(.minute, from: selectedDate.wrappedValue)
53+
_selectedMinuteIndex = State(initialValue: minute / minuteStep)
54+
55+
_selectedPeriod = State(initialValue: hour24 < 12 ? 0 : 1)
56+
self.minuteStep = minuteStep
57+
}
4958

5059
public var body: some View {
5160
ZStack {
@@ -64,21 +73,33 @@ public struct TTimePicker: View {
6473
normalColor: normalColor,
6574
selectedColor: selectedColor,
6675
infiniteScroll: infiniteScroll,
67-
selected: $selectedHour
76+
selected: Binding(get: {
77+
selectedHour
78+
}, set: {
79+
selectedHour = $0
80+
updateDate()
81+
})
6882
)
6983
Text(":")
7084
.typographyStyle(.heading4, with: .neutral900)
71-
// 분 열: "00" ~ "59"
85+
// 분 열: 생성 시 minuteStep에 따라 아이템 생성 (예, minuteStep 5 → ["00","05",..., "55"])
7286
FlatPickerColumn(
73-
items: (0..<60).map { String(format: "%02d", $0) },
87+
items: stride(from: 0, to: 60, by: minuteStep)
88+
.map { String(format: "%02d", $0) },
7489
rowHeight: rowHeight,
7590
visibleCount: visibleCount,
7691
normalFont: normalFont,
7792
selectedFont: selectedFont,
7893
normalColor: normalColor,
7994
selectedColor: selectedColor,
8095
infiniteScroll: infiniteScroll,
81-
selected: $selectedMinute
96+
selected: Binding(
97+
get: { selectedMinuteIndex },
98+
set: { newValue in
99+
selectedMinuteIndex = newValue
100+
updateDate()
101+
}
102+
)
82103
)
83104
Text(":")
84105
.typographyStyle(.heading4, with: .clear)
@@ -92,9 +113,31 @@ public struct TTimePicker: View {
92113
normalColor: normalColor,
93114
selectedColor: selectedColor,
94115
infiniteScroll: false,
95-
selected: $selectedPeriod
116+
selected: Binding(
117+
get: { selectedPeriod },
118+
set: { newValue in
119+
selectedPeriod = newValue
120+
updateDate()
121+
}
122+
)
96123
)
97124
}
98125
}
99126
}
127+
128+
private func updateDate() {
129+
var calendar: Calendar = Calendar.current
130+
calendar.timeZone = TimeZone(secondsFromGMT: 0) ?? .current // UTC로 설정
131+
var components: DateComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: date)
132+
133+
// 24시간대로 시간 포맷 설정
134+
let hour24: Int = (selectedHour + 1) % 12 + (selectedPeriod == 1 ? 12 : 0)
135+
136+
components.hour = hour24
137+
components.minute = selectedMinute
138+
139+
if let newDate = calendar.date(from: components) {
140+
date = newDate
141+
}
142+
}
100143
}

0 commit comments

Comments
 (0)