Custom Calendar 만들기 #2

Zeto·2022년 6월 8일
0

Swift_UIKit

목록 보기
5/12

이번에는 이전에 생성한 Calendar에 추가 로직을 구현하여 1년 치의 수직 스크롤 달력의 형태로 수정해보고자 한다.

Custom Calendar #2

😚 First Step.

먼저 기존에 dateFormatter의 역할을 딱 한 달치의 월과 일을 만들어주는 역할만 하도록 로직을 분리하고자 한다.

struct CalendarDateFormatter {
    
    private let dateFormatter = DateFormatter()
    private var calendarComponent: Calendar?
    
    init() {
        self.configureDateFormatter()
    }
    
    mutating func setCalendarComponent(calendar: Calendar) {
        self.calendarComponent = calendar
    }
    
    func getYearMonthText(calendarDate: Date) -> String {
        let yearMonthText = self.dateFormatter.string(from: calendarDate)
        
        return yearMonthText
    }
    
    func updateCurrentMonthDays(calendarDate: Date) -> [String] {
        var days = [String]()
        
        let startDayOfWeek = self.getStartingDayOfWeek(date: calendarDate)
        let totalDaysOfMonth = startDayOfWeek + self.getEndDateOfMonth(date: calendarDate)
        
        for day in 0..<totalDaysOfMonth {
            if day < startDayOfWeek {
                days.append("")
            } else {
                days.append("\(day - startDayOfWeek + 1)")
            }
        }
        
        return days
    }
}

private extension CalendarDateFormatter {
    
    func getStartingDayOfWeek(date: Date) -> Int {
        guard let calendar = calendarComponent else { return 0 }
        return calendar.component(.weekday, from: date) - 1
    }
    
    func getEndDateOfMonth(date: Date) -> Int {
        guard let calendar = calendarComponent else { return 0 }
        return calendar.range(of: .day, in: .month, for: date)?.count ?? 0
    }
    
    func configureDateFormatter() {
        self.dateFormatter.dateFormat = "yyyy년 MM월"
    }
}

기존과 달리 데이터를 관리하는 역할을 빼주었기 때문에 구조체로 타입을 변경해주었을 뿐더러, String 타입으로 date를 포맷해주는 역할을 하는 DateFormatter만 직접 생성 및 소유하게 두었다.
이와 함께 날짜 생성에 필요한 component는 상위 객체로부터 전달받도록 구현하였고, 이외에 기존 메서드들은 매개변수로 필요 데이터를 받아와서 적절한 데이터로 변환하여 리턴하는 형태로 수정해주었다.

😚 Second Step.

다음으로는 formatter 구조체를 활용하여 date 값들을 변환하고 관리해줄 수 있는 상위 객체인 CalendarManager를 생성해줄 차례이다.

final class CalendarManager {
    
    private let calendar = Calendar.current
    private var nowCalendarDate = Date()
    private var calendarFormatter = CalendarDateFormatter()
    private(set) var yearMonths = [Date]()
    private(set) var monthDays = [[String]]()
    
    init() {
        self.setCalendar()
    }
    
    func getMonthsToString(section: Int) -> String {
        guard yearMonths.count > section else { return "" }
        
        let monthString = self.calendarFormatter.getYearMonthText(calendarDate: yearMonths[section])
        return monthString
    }
    
}

private extension CalendarManager {
    
    func setCalendar() {
        let components = self.calendar.dateComponents([.year, .month], from: Date())
        self.nowCalendarDate = self.calendar.date(from: components) ?? Date()
        self.calendarFormatter.setCalendarComponent(calendar: self.calendar)
        self.setYearMonth()
        self.setMonthDays()
    }
    
    func setYearMonth() {
        self.yearMonths.append(self.nowCalendarDate)
        
        for plusNum in 1...11 {
            let month = self.calendar.date(byAdding: .month, value: plusNum, to: nowCalendarDate)
            self.yearMonths.append(month ?? Date())
        }
    }
    
    func setMonthDays() {
        self.yearMonths.forEach {
            let days = self.calendarFormatter.updateCurrentMonthDays(calendarDate: $0)
            self.monthDays.append(days)
        }
    }
}

기존 dateFormatter가 소유하고 있던 Date, Calendar 타입의 데이터와 함께 1년 치의 월과 일을 소유하는 프로퍼티를 소유하는 형태이다.

설정된 components와 date를 활용하여 1년 간의 월과 일의 데이터를 dateFormatter를 통해 받아오고 이를 배열에 할당하여 관리해주도록 내부 메서드를 작성해주었다.
이와 함께 이후 CollectionHeader의 Label에 넣어줄 월을 String 타입으로 넣어줄 메서드 또한 작성해준다.

😚 Third Step.

마지막으로는 ViewController에 생성한 dateFormatter를 dateManager로 교체하고 이에 맞춰서 모든 로직들을 적절하게 수정해주는 작업이 필요하다.

다만 해당 작업은 따로 로직을 기입하지 않을 예정인데 전체적인 코드가 담겨있는 Git 주소를 올리니 혹시 궁금할 경우에는 해당 주소에서 확인해주세요.

https://github.com/wnsxor1993/CustomCalendar

profile
중2병도 iOS가 하고싶어

0개의 댓글