XLOG

[SwiftUI] 날짜 계산, 월간달력 만들기 본문

Swift/SwiftUI

[SwiftUI] 날짜 계산, 월간달력 만들기

X_PROFIT 2023. 3. 4. 17:25

이전 Posting 에 달력에 활용할 무한스크롤에 대한 아이디어를 적어서 포스팅 한 적이 있다.

그래서 이번엔 그 스크롤에 활용할 달력을 만들어 보려고 한다.

 

그러기 위해선 Swift 에서 날짜를 다루는 것들에 대해 먼저 알아봐야 한다.

 

그래서 Date, Calendar 를 확인할 예정이다.

우선 Date

기본 파라미터 없이 init을 하게 되면 현재 날짜와 시간을 알려준다. 여기서 timeInterval 은 시간차(초 단위) 날짜를 구한다고 알고 있으면 된다.

Calendar 는 달력유닛과 특정 시점으로 정의를 내려준다고 한다. 그리고 날짜 계산 및 비교도 제공을 해준다.

여기서 달력 유닛은 말그대로 달력의 종류인데 일반적으로 양력(그레고리언) 을 사용한다.

이것과 관련된 설정으로 아이폰에서 일반 > 언어 및 지역 > 캘린더 에 가면 선택할 수 있는 옵션이 보이는 것으로 생각하면 된다.

 

사실 Calendar 에 timezone, locale 이 나누어져 있어서 맨 처음 왜 따로 계산하는지 궁금했었다. 하지만 이유는 간단했다. 아무리 Locale 이 같더라도 미국처럼 땅이 크면 도시마다 시차가 발생한다. 그럴때 timezone을 구분해서 사용하게 된다.

 

// "Mar 4, 2023 at 12:42 PM"
let currentDate = Date()

// Calendar 를 사용할 경우 뒤에 current 혹은 identifier를 줘야한다.
// 앞서 설명한 것과 마찬가지로 어떠한 달력 계산 방식을 사용하는지를 설정해줘야 하기 때문이다.
let calendar = Calendar.current

// "Mar 1, 2023 at 12:00 AM"
let startDate = calendar.date(from: Calendar.current.dateComponents([.year, .month], from: currentDate))!

//{lowerBound 1, upperBound 32}
let range = calendar.range(of: .day, in: .month, for: startDate)!

// 한달치의 달력 컴퍼넌트를 가지고 온다.
let rangeMonth = range.compactMap{ day -> Date in
    return calendar.date(byAdding: .day, value: day - 1, to:  startDate)!
}

가장 기본적인 코드들이다. 위의 함수들을 사용하면 나는 해당날짜의 한달치의 날짜 배열을 확인할 수 있다.

하지만 문제는 한달의 시작은 일요일 혹은 월요일 부터 시작하지 않는다는 것이다.

그것을 위해 startDate 를 구해준 것이다.

let calendar = Calendar.current

// "Mar 1, 2023 at 12:00 AM"
let startDate = calendar.date(from: Calendar.current.dateComponents([.year, .month], from: currentDate))!

// 일요일1 ~ 토요일 7
// 4
let startIndex = calendar.component(.weekday, from: startDate)

//weekday: 7 isLeapMonth: false 
let firstday = calendar.dateComponents([.weekday], from: startDate)

// 4
firstday.weekday

이렇게 원하는 날짜(그 달의 시작 날짜)가 무슨 요일인지 확인을 해준다. 

 

let days = ["일", "월", "화", "수", "목", "금", "토"]
VStack {
	// 맨 위 날짜 표시
    HStack(spacing: 0) {
        ForEach(days, id: \.self) { day in
            Text(day)
                .foregroundColor(day == "sun" ? .red : .black)
                .frame(maxWidth: .infinity)
        }
    }
    
    // 칼럼을 이용하여 7개의 칼럼을 가지는 LazyVGrid를 사용
    let columns = Array(repeating: GridItem(.flexible()), count: 7)
    LazyVGrid(columns: columns, spacing: 15) {
    	// 첫째날이 일요일이 아닌경우 빈 칸을 추가해준다
        let firstDay = Calendar.component(.weekday, from: startDate)
        if  firstDay > 1 {
            ForEach(1 ..< firstDay) { i in
                Text("")
            }
        }                
        // 시작 날짜의 요일

        // 그 이후부터 추가
        ForEach(vm.currnetMonthlyDates) { value in
            if value.day > 0 {
                Text("\(value.day)")
            }
        }
    }
}