iOS

[iOS] swiftUI 캘린더 직접 구현

pearhyunjin 2024. 6. 6. 13:09

 

swiftUI를 이용해 캘린더 직접 구현하기


 

반려견 산책 기록용 앱개발 프로젝트 진행중 홈화면에 필요한 캘린더 기능을 구현해보았다.

먼저 본격적인 구현에 앞서 캘린더 구현에 중요한 역할을 하는 calendar 구조체에 대해 간단히 학습하고 넘어가고자 한다.

 

calendar 구조체

연도의 시작, 길이 및 구분이 정의되는 계산 시스템에 대한 정보를 요약하는 구조체로 달력에 대한 정보를 제공하고 주어진 달력 단위의 범위를 결정하고 주어진 절대 시간에 단위를 추가하는 것과 같은 달력 계산에 대한 지원을 제공한다. 즉, 날짜를 계산해준다.

 

calendar 메서드

static var current: Calendar

let calendar = Calendar.current  // 사용자 현재 달력 가져오기

// isLeapMonth로 윤달 포함 여부 반환
func dateComponents(Set<Calendar.Component>, from: Date) => DateComponentscalendar.dateComponents([], from: Date())
calendar.dateComponents([.year], from: Date())  // year: 2024 isLeapMonth: false
calendar.dateComponents([.month], from: Date())  // month: 06 isLeapMonth: false
calendar.dateComponents([.day], from: Date())  // day: 11 isLeapMonth: false
calendar.dateComponents([.year, .month, .day], from: Date())  // year: 2024 month: 06 day: 10 isLeapMonth: false

// components에 어떤요소(year, month, day, 비움)가 들어있는지에 따라 달라지는 반환값
func date(from: DateComponents) -> Date?
calendar.date(from: components) // Jan 1, 1 at 12:00  AM 형태로 출력. components에 있는 요소 제외하고는 해당 상태로 초기화.

// 날짜의 한 구성 요소에 대한 값 반환
func component(Calendar.Component, from: Date) -> Int
calendar.component(.year, from: Date())  // 2024
calendar.component(.month, from: Date())  // 06
calendar.component(.day, from: Date())  // 10
calendar.component(.weekday, from: Date())  // 7 (Sun -> 1, Sat -> 7)

// 지정된 시간을 포함하는 큰 달력 요소(월)에서 작은 요소(일)가 취할 수 있는 절대 시간 값의 범위 반환
func range(of: Calendar.Component, in: Calendar.Component, for: Date) -> Range?

// 지정된 날짜에 구성 요소 추가해 계산된 날짜 반환
calendar.dateComponents([.month, .day, .year], from: Date())
func date(byAdding: DateComponents, to: Date, wrappingComponents: Bool) -> Date?
calendar.date(byAdding: DateComponents(month:1, day: -1), to: Date())

 

 

 


 

 

위에서 학습한 내용을 참고하며 캘린더 구현 완료하였습니다.

아래에서 코드의 중요 부분만 가져와 설명하며, 전체 코드는 아래 본인의 깃허브에서 확인 가능합니다.!

 

캘린더를 구성할 View와 Swift File

  • ContentView : 메인이 되는 홈 화면 View 구성 (DateScrollerView 호출, dayOfWeekStack와 calendarGrid 함수 생성해 이용)
  • DateScrollerView : Button, Text 이용해 상단에 날짜와 페이지 바꿈 버튼 만들기
  • dayOfWeekStack : 일요일 ~ 토요일 순서대로 출력
  • calendarGrid : 1일부터 캘린더 그리드에 맞춰 날짜 출력
  • CalendarCellView : 요일에 따른 날짜 색상 변경 등 그캘린더 속 날짜 요소 하나하나의 View 구성 -> 클릭 이벤트 구현
  • MonthStruct : enum 변수 선언
  • CalendarHelper : calendar 메서드 이용해 캘린더 구현 돕는 코드

 

코드 설명

- 홈화면 구성에서 전체적인 캘린더 뼈대 구성.

VStack(spacing: 5) {
	// DateScrollerView (dateHolder 변수로) 가져옴 -> 즉 현재 날짜에 해당하는 형태의 달력 가져옴
    DateScrollerView()
    	.environmentObject(dateHolder)
        .padding()
    // 일주일(요일) 출력
    dayOfWeekStack
    // 캘린더 그리드 내용 출력
    calendarGrid                                
}
.frame(width: 300, height: 400)

 

 

- contentView에서 캘린더 그리드 구성을 위한 변수 선언. CalendarHelper()에서 각 calendar 메서드 이용해 데이터 리턴 받아와 이용.

let daysInMonth = CalendarHelper().daysInMonth(dateHolder.date)
let firstDayOfMonth = CalendarHelper().firstOfMonth(dateHolder.date)
let startingSpaces = CalendarHelper().weekDay(firstDayOfMonth)
let prevMonth = CalendarHelper().minusMonth(dateHolder.date)
let daysInPrevMonth = CalendarHelper().daysInMonth(prevMonth)
let clicked = false

 

 

- 캘린더 상단에 각각 이전/다음 달로 넘어가는 버튼과 현재 년도/월을 표시하는 텍스트 구성.

// 이전 달 버튼
Button(action: previousMonth) {
	Image(systemName: "arrow.left")
    	.imageScale(.large)
        .font(Font.title.weight(.bold))
}
            
// 여백
Spacer()
            
// 년도,월 표시
// CalendarHelper에서 monthYearString 함수에 date 변수 값 넣어 연산하고 폰트와 동작 등 지정
Text(CalendarHelper().monthYearString(dateHolder.date))
	.font(.title)
	.bold()
	.animation(.none)
	.frame(maxWidth: .infinity)
            
// 여백
Spacer()
            
// 다음 달 버튼
Button(action: nextMonth) {
	Image(systemName: "arrow.right")
		.imageScale(.large)
		.font(Font.title.weight(.bold))
}

 

 

- 캘린더 구성을 위함 함수들 (한 file에 들어있는 내용은 아니다. 임의로 모아서 설명)

// 이전 달 버튼 클릭 이벤트
func previousMonth() {
	dateHolder.date = CalendarHelper().minusMonth(dateHolder.date)
}
    
// 다음 달 버튼 클릭 이벤트
func nextMonth() {
	dateHolder.date = CalendarHelper().plusMonth(dateHolder.date)
}

// 날짜 String 표시 함수 (date 변수로 받음)
func monthYearString(_ date: Date) -> String {
	dateFormatter.dateFormat = "LLL yyyy"
	return dateFormatter.string(from: date)
}
    
// 입력된 변수(date)에서 +1 해주고 리턴 (date 변수로 받음)
func plusMonth(_ date: Date) -> Date {
	return calendar.date(byAdding: .month, value: 1, to: date)!
}
    
// 입력된 변수(date)에서 -1 해주고 리턴 (date 변수로 받음)
func minusMonth(_ date: Date) -> Date {
	return calendar.date(byAdding: .month, value: -1, to: date)!
}
    
// 입력받은 변수(date)를 이용해 해당 월의 날짜 수를 구한다. (1월 : 31일, 2월 : 28일, ...)
func daysInMonth(_ date: Date) -> Int {
	let range = calendar.range(of: .day, in: .month, for: date)!
	return range.count
}
    
// 입력받은 변수(date)를 통해 해당 월의 일자를 넣어준다. (1월 : 1~31일의 정수 리턴, 2월 : 1~28일, ...)
func dayOfMonth(_ date: Date) -> Int {
	let components = calendar.dateComponents([.day], from: date)
	return components.day!
}
    
// 입력받은 변수(date)를 통해 해당 월의 첫 날짜를 구한다.
func firstOfMonth(_ date: Date) -> Date {
	let components = calendar.dateComponents([.year, .month], from: date)
	return calendar.date(from: components)!
}
    
// 입력받은 변수(date)를 통해 일주일 중 어느 요일인지를 구한다..? (위의 함수를 이용해 구한 날짜를 통해 요일을 구하기..)
func weekDay(_ date: Date) -> Int {
	let components = calendar.dateComponents([.weekday], from: date)
	return components.weekday! - 1
}

 

 

https://github.com/pearhyunjin/DogWalkDiary.git

 

GitHub - pearhyunjin/DogWalkDiary

Contribute to pearhyunjin/DogWalkDiary development by creating an account on GitHub.

github.com

 

 

 

 

 

 

📌 'Code With Cal'님의 강의와 깃허브를 참고해 진행했다. (아래 출처 기록)


* https://www.youtube.com/watch?v=jBvkFKhnYLI&t=430s

 

* https://developer.apple.com/documentation/foundation/calendar

 

Calendar | Apple Developer Documentation

A definition of the relationships between calendar units and absolute points in time, providing features for calculation and comparison of dates.

developer.apple.com

 

* https://boreum.tistory.com/63

 

[iOS] Calendar 구조체로 달력 구현하기 (2) - 달력 뷰 구성하기

달력 뷰 미리 보기 Calendar 구조체도 알아봤으니 이제 달력 구현을 위하여 뷰를 구성해 보겠습니다. 위의 사진과 같은 심플한 달력을 만들 겁니다. 각각 뭐하는 친구들인지 설명도 적어드릴게요!

boreum.tistory.com

 

* https://green1229.tistory.com/362

 

SwiftUI로 캘린더 직접 구현하기

안녕하세요. 그린입니다🍏 이번 포스팅에서는 오랜만에 SwiftUI로 뚝닥뚝닥 해보는 시간입니다🙋🏻 뭘 뚝닥뚝닥 해볼지 고민하다가 그냥 밑도 끝도 없이 캘린더를 간단하게 직접 만들어보고

green1229.tistory.com