SwiftUI 에서 기본적으로 제공하는 DatePicker를 사용하지 않고 달력을 구현할 수 있습니다
우선 달력에서는 1월부터 12월까지 모두 다른 일수를 갖고 있습니다. 이런 부분들을 모두 하드코딩해버리면 매우 까다롭기 때문에 SwiftUI 에서 제공하는 dateInterval 메서드를 사용하여 해당 달에 몇일 까지 있는지 구할 수 있습니다.
dateInterval 메서드로 일수 구하기
이번 포스팅을 하는 날짜는 24년 8월이고 이번달에는 31일을 포함하고 있습니다.
이때 func dateInterval(of: Calendar.Component, for: Date) -> DateInterval? 메서드를 사용한다면 이를 쉽게 구할 수 있습니다.
월에 몇일 있는지 구하기
다음은 위에서 언급한 dateInterval 메서드를 사용하여 이번달과 다음달까지의 범위를 Range로 반환합니다.
- 예를들어 현재 날짜가 2024-08-12 이라도 start는 이전달의 마지막 날짜를 의미합니다
- 그렇다면 monthInterval.start 는 2024-07-31
- calendar.date 메서드에서는 현재 2024-07-31 에서 1달 후인 2024-08-31 를 의미하며
- 2024-07-31 ~ 2024-08-31 범위를 반환하게 됩니다.
이때 2024-07-31 이라고 되어 있지만 이는 Calendar의 component로 표기하면 원래 사용하고자한 정확한 값으로 사용 가능합니다
let currentMonth = Date() // 현재 날짜
private func monthDateRange() -> Range<Date> {
let monthInterval = calendar.dateInterval(of: .month, for: currentMonth)! // 현재 날짜의 월
return monthInterval.start..<calendar.date(byAdding: .month, value: 1, to: monthInterval.start)!
}
이제 원하는 달의 일수를 얻었다면 해당 일수들을 포함하는 날짜들을 사용하여 달력을 만들어야 합니다.
그렇게 하기 위해서는 범위를 사용하여 날짜들을 배열에 각각 담아 반환합니다.
- 먼저 날짜를 담을 수 있는 배열을 만들어줍니다
- currentDate에 현재 dateRange 에서 첫날인 lowerBound를 담습니다, dateRange의 첫날은 2024-07-31 입니다
- currentDate가 dateRange 에서 마지막 날인 upperBound를 담습니다, dateRange의 마지막 날은 2024-08-31 입니다.
- while 문을 돌면서 2024-07-31 일부터 2024-08-31 일까지 31일을 모두 배열에 담습니다
- 31일이 담긴 배열을 반환합니다.
private func generateDates(for dateRange: Range<Date>, calendar: Calendar) -> [Date] {
var dates: [Date] = []
var currentDate = dateRange.lowerBound
while currentDate < dateRange.upperBound {
dates.append(currentDate)
currentDate = calendar.date(byAdding: .day, value: 1, to: currentDate)!
}
return dates
}
뷰로 만들기
이제 위에서 만든 메서드들을 사용하여 뷰로 만들어보겠습니다.
- 해당 월의 날짜 범위를 dateRange 에 담습니다.
- dates 에 dateRage 를 사용하여 담은 날짜 배열들을 담습니다
- 이후 LazyVGrid 를 사용하여 뷰로 만들어줍니다.
- LazyVGrid 를 사용하여 메모리 사용량을 줄일 수 있습니다.
var body: some View {
// Get the start and end dates for the calendar view
let dateRange = monthDateRange()
let dates = generateDates(for: dateRange, calendar: calendar)
LazyVGrid(columns: Array(repeating: GridItem(), count: 7)) {
ForEach(dates, id: \.self) { date in
ZStack {
Text("\(calendar.component(.day, from: date))")
.padding(.vertical)
}
}
}
}
달력 탭하기
달력을 탭했을 때 해당 날짜가 탭 되었는지 여부를 다음과 같이 Circle 등을 사용하여 표시할 수 있습니다.
var body: some View {
let dateRange = monthDateRange()
let dates = generateDates(for: dateRange, calendar: calendar)
LazyVGrid(columns: Array(repeating: GridItem(), count: 7)) {
ForEach(dates, id: \.self) { date in
ZStack {
if calendar.isDate(date, equalTo: selectedDate, toGranularity: .day) {
Circle()
.foregroundStyle(Color.blue.opacity(0.2))
}
Text("\(calendar.component(.day, from: date))")
.padding(.vertical)
.onTapGesture {
selectedDate = date
print(date)
}
}
}
}
}
'SwiftUI' 카테고리의 다른 글
Swift - 대소문자 바꾸기 (아스키코드, map) 2가지 방법 (0) | 2024.08.23 |
---|---|
SwiftUI - 버튼, 텍스트 터치 영역 확장시키기 (0) | 2024.08.18 |
SwiftUI - calendar (0) | 2024.08.15 |
SwiftUI - 나만의 SPM 만들어 사용해보기 (0) | 2024.08.08 |
SwiftUI - DragGesture 사용해서 화면 드래그하는법 (0) | 2024.08.07 |