Swift

iOS Custom Calendar 구현하기 3

Thor_yeom 2023. 4. 18. 19:45

자 오늘은 본격적으로 Calendar 구조체를 이용해서 만들어보겠습니다.

 

 

그래서 Calendar 구조체가 뭐하는 녀석인데??

공식문서 출처

흠... 뭔가 이해하기 어려운데 쉽게 설명드리면 

날짜를 계산하고 비교해주는 기능

 

 

Calendar의 객체를 생성할때는 두가지 방식으로 사용 가능합니다.

공식문서 출처

 

사용 방법으로는 

공식문서 출처

이렇게 있습니다. 

 

 

 

적용시켜 보겠습니다

 

1) CustomCalendarViewController.swift에

프로퍼티를 만들어줍니다.

    // user의 현재 캘린더
    private let calendar = Calendar.current
    // 입력되는 날짜를 형 변형 시키기위해 DateFormatter() 객체 생성
    private let dateFormatter = DateFormatter()
    // Date() 를 생성해서 현재 시간과 날짜 가져오기
    private var calendarDate = Date()
    // 빈 배열을 만들어주고 만들어진 날짜 day(일) 넣기
    private var days: [String] = []

이렇게 프로퍼티를 만들었으면 

이제 메서드를 추가해줄건데요

 

CustomCalendarViewController 객체 안에서 

만들어주게 되면 깔끔하지 않으니까 

extension을 활용하여 하단으로 빼줍니다.

 

2) extension을 활용하여 여기에 만들어줍니다.

// MARK: - CustomCalendarViewController
extension CustomCalendarViewController {
    
}

 

3) extension 안에 메소드를 추가합니다.

// MARK: - CustomCalendarViewController
extension CustomCalendarViewController {
    
    func configureCalendar() {
        // 메인 타이틀을 어떤 형식으로 보여줄지 형 변형
        dateFormatter.dateFormat = "yyyy년 MM월"
        // dateComponents : 표준 시간대를 사용하여 Calendar의 모든 날짜 구성요소를 반환
        let components = calendar.dateComponents([.year, .month], from: Date())
        // date : 지정된 구성 요소에서 생성된 날짜를 반환
        calendarDate = calendar.date(from: components) ?? Date()
        print("날짜 : \(calendarDate)")
    }
}

 

1. 요일의 시작이 일요일이기 때문에 days배열에 첫번째를 일요일로 해주기 위해

-1을 해준후 반환 메소드 작성

    // component()를 이용하면 1일은 일요일, 7은 토요일로 반환됩니다.
    // days 배열의 0번 인덱스를 일요일로 표시해주기 위하여 -1를 해준후 반환합니다.
    func startDayOftheWeek() -> Int {
        
        return calendar.component(.weekday, from: calendarDate)-1
    }

 

2. 해당 달에 몇일 있는지 반환하는 메소드를 만듭니다.

    // 해당 달에 몇일까지 있는지 확인후 일수를 반환합니다.
    func endDate() -> Int {
        return calendar.range(of: .day, in: .month, for: calendarDate)?.count ?? Int()
    }

 

3. 설정된 calendarDate을 String으로 변환후 MainTitle에 적용합니다.

    // dateFormatter를 이용하여 calendarDate를 설정한것 Maintitle에 적용시킵니다.
    func updateTitle() {
        // calendarDate의 자료형이 Date이기 때문에 자료형 -> String으로 변환
        let date = dateFormatter.string(from: calendarDate)
        titleLabel.text = date
    }

 

4. 이전 배열안에 든것을 삭제해주고 해당 달의 일수를 보여주는 메서드를 작성해줍니다.

    // updatDay()가 불렸을때는 이전 데이터가 필요 없어지고 새로운 해당 달의 일수만 보여주는 메서드
    func updateDay() {
        // 해당 메서드가 불려지만 빈배열로 만듭니다.
        self.days = []
        // 해당 요일의 시작 인덱스를 정해주고
        let startOftheWeek = startDayOftheWeek()
        // 시작 일수와 + 해당 달의 일수를 더해줍니다.
        let totalDay = startOftheWeek + endDate()
    }

 

ex) 2023년 4월 1일이 토요일니까, startOftheweek에는 6이 저장되고, 4월은 30일까지 있으니까

totalDay = 36이 됩니다. 

 

 

5. days에 해당 일이 생성될 수 있게 for문을 추가 해줍니다

    func updateDay() {
        // 해당 메서드가 불려지만 빈배열로 만듭니다.
        self.days = []
        // 해당 요일의 시작 인덱스를 정해주고
        let startOftheWeek = startDayOftheWeek()
        // 시작 일수와 + 해당 달의 일수를 더해줍니다.
        let totalDay = startOftheWeek + endDate()
        
        for day in Int()..<totalDay {
            if day < startOftheWeek {
                days.append(String())
                continue
            }
            self.days.append("\(day - startOftheWeek + 1)")
        }
        collectionView.reloadData()
    }

 

앞에 if문이 있는 이유는 

요일에 맞게 빈칸을 만들어주기 위함입니다.

 

2023년 4월 같은 경우에는 토요일이 1이기때문에 startOftheweek은 6이고

days = [" ", " ", " ", " ", " ", " "]

이렇게 if문이 돌려지고 

 

day가 6이 되면 

     

self.days.append("\(day - startOftheWeek + 1)")

여기서 부터 쌓이게 됩니다. 

 

    days = [" ", " ", " ", " ", " ", " ", "1","2","3"...]

 

이렇게 다 쌓였으면

        collectionView.reloadData()

 

collectionView.reloadData()를 불러 화면을 업데이트 합니다.

휘유... 너무 어렵네요 ㅠ

 

6. 이렇게 만들어진 updateDay()와 updateTitle()을 한번에 묶어줄 메서드를 작성합니다.

    // 타이틀과 날짜(일)를 업데이트해주는 메서드입니다. 
    func updateCalendar() {
        updateTitle()
        updateDay()
    }

 

7. 이렇게 작성된 updateCalendar()를 configureCalendar()에서 불러줍니다.

    func configureCalendar() {
        // 메인 타이틀을 어떤 형식으로 보여줄지 형 변형
        dateFormatter.dateFormat = "yyyy년 MM월"
        // dateComponents : 표준 시간대를 사용하여 Calendar의 모든 날짜 구성요소를 반환
        let components = calendar.dateComponents([.year, .month], from: Date())
        // date : 지정된 구성 요소에서 생성된 날짜를 반환
        calendarDate = calendar.date(from: components) ?? Date()
        print("configureCalendar() : \(calendarDate)")
        // 타이틀과 날짜 
        updateCalendar()
    }

 

 

빌드를 해보면 MainTitle이 바뀌는 것을 볼 수 있습니다.

이제 거의 다 왔습니다. 그렇다면 

updateDay()에서 만들어진 배열만

cell에 적용하면 마무리 될 거 같습니다.

 

 

혹시... 빌드가 제대로 안되시는 분들은 

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        configure()
    }
    
    func configure() {
        configureTitleLabel()
        configurePreviousButton()
        configureNextButton()
        configureTodayButton()
        configureStackView()
        configureCollectionView()
        configureCalendar()
    }

모든 메서드를 불러주는 configure() 메서드 안에

configureCalendar()를 불렀는지 확인해봅니다.

 

 

 

8. 이제 배열도 만들어졌겠다. DataSource에 적용해봅시다.

extension CustomCalendarViewController: UICollectionViewDataSource, UICollectionViewDelegate {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // Cell의 갯수를 보여주기 위해 임의로 값을 설정
        return days.count
    }
    
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // cell 구현
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: CustomCalCollectionViewCell.self), for: indexPath) as! CustomCalCollectionViewCell
        // 배열로 만들어진 String들을 dayLabel.text에 하나씩 넣어줍니다.
        cell.dayLabel.text = days[indexPath.item]
        return cell
    }
}

 

빌드를 해보겠습니다.

 

 

완벽합니다

'Swift' 카테고리의 다른 글

iOS Custom Calendar 구현하기 5  (0) 2023.04.20
iOS Cutsom Calendar 구현하기 4  (0) 2023.04.18
iOS Custom Calendar 구현하기2  (0) 2023.04.17
iOS Custom Calender 구현하기  (2) 2023.04.17
어서와 SwiftUI @FetchRequest는 처음이지?  (0) 2023.03.23