[iOS/UIkit] 주간 캘린더 구현

2na·2025년 5월 19일

Swift 🍎

목록 보기
5/9

이번 합동세미나에서 저희 팀이 맡은 서비스는 투두메이트였습니다.
그 중 저는 캘린더를 가져오게 되었는데요..
캘린더 UI 작업이 얼추 완료되었는데, 정리 겸 회고 겸 아티클 작성겸... 여튼 작성해보려고합니다.

1. UICollectionView로 캘린더 만들기

이렇게 생긴 캘린더를 만들건데요.


이렇게 구조를 짰고, 달력 부분은 컬렉션 뷰로 만들었습니다.

☄️ 여기서 생긴 트러블 슈팅..

didSelectItmeAt 함수가 호출이 아예 안되는 이슈가 있었습니다. 알고보니.. 상단에 있는 자잘한 것들을 WeekBox 뷰로 묶고, 달력을 WeekCalendar 뷰로 묶었는데 그 뷰들에 height 제약을 걸어주지 않아서 생긴 문제였습니다. 높이제약을 잘 걸었는지 항상 확인합시다...

2. Calendar Class 만들기

달력과 관련된 일들을 처리하는 Calendar Formatter Class를 만들었습니다. 이곳에서 관련된 함수들을 만들어, 날짜를 받아오는 일들은 전부 이 클래스에서 처리했습니다.

private let calendar: Calendar = {
            var calendar = Calendar(identifier: .gregorian)
            calendar.firstWeekday = 2
            return calendar
}()

투두메이트는 월요일부터 시작하기 때문에 firsrtWeekday 속성을 수정했습니다.

 private let calendarFormatter = DateFormatter()
 private let monthFormatter = DateFormatter()
 private let dateFormatter = DateFormatter()
 private var nowCalendarDate = Date()
 private var dates = [String]()
    
init() {
        calendarFormatter.dateFormat = "yyyy-MM-dd"
        monthFormatter.dateFormat = "yyyy년 MM월"
        monthFormatter.locale = Locale(identifier: "ko_KR")
        dateFormatter.dateFormat = "d"
        dateFormatter.locale = Locale(identifier: "ko_KR")
 }

각자 용도에 맞는 formatter를 선언해주었습니다.

  • monthFormatter : 월 텍스트를 받아올 때 사용합니다.
  • calendarFormatter : 현재 뷰에 보여지는 한 주간의 날짜를 yyyy-mm-dd 형식의 배열로 보여줄 때 사용합니다.
  • dateFormatter : 오늘 날짜에 관련된 일들을 처리할 때 사용합니다.

3. 셀에 날짜 뿌려주기

셀에 날짜를 뿌려줄 때 다음과 같이 작성했습니다.

private func setDate(_ offset: Int) {
        weekDates = formatter.getWeekDateStringsOfWeek(offset)
        selectedIndex = formatter.getTodayDay()
        selectedDate = weekDates[selectedIndex]
        weekCollectionView.reloadData()
}
    
extension WeekCalendar: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: WeekCollectionViewCell.identifier, for: indexPath) as? WeekCollectionViewCell else {
            return WeekCollectionViewCell()
        }
        
        let date = weekDates[indexPath.item]
        let isSelected = selectedIndex == indexPath.item
        cell.dataBind(date: date, isSelected: isSelected, index: indexPath.item)
        return cell
    }
    ...
}

yyyy-mm-dd 형식으로 현재 주를 배열로 리턴해주는 함수 getWeekDateStringsOfWeek를 호출하여 weekDates 배열에 담아주었습니다.

extension WeekCollectionViewCell {
    func dataBind(date: String, isSelected: Bool, index: Int) {
        let slicedDate = Int(date.suffix(2)) ?? 1
        dayLabel.text = "\(slicedDate)"
        
        if isSelected {
            dayLabel.backgroundColor = .black
            dayLabel.layer.cornerRadius = 10
            dayLabel.textColor = .white
        } else {
           changeDayLabelColor(index)
       }
    }
}

데이터 바인딩 할때, dd 만 가져올 수 있도록 추출해주었습니다. Int 형식으로 변환한 이유는 01, 02, 03.. 인 날짜일 때 1, 2, 3..으로 표시해주기 위해서입니다.

4. 주간 달력 이동하기

위의 왼쪽 화살표 아이콘, 오른쪽 화살표 아이콘을 누르면 전주, 다음주로 달력을 이동해야합니다.

func getWeekDateStringsOfWeek(_ offset: Int) -> [String] {
        guard let targetDate = calendar.date(byAdding: .weekOfYear, value: offset, to: nowCalendarDate),
              let weekInterval = calendar.dateInterval(of: .weekOfYear, for: targetDate) else {
            return []
        }

        var weekDates: [String] = []
        for i in 0..<7 {
            if let date = calendar.date(byAdding: .day, value: i, to: weekInterval.start) {
                weekDates.append(calendarFormatter.string(from: date))
            }
        }
        print("이번주는요 ", weekDates)
        return weekDates
    }

offset을 주어서, 현재 날짜에서 얼마나 이동해야하는지를 체크하여 그에 따른 날짜를 리턴합니다.

extension WeekCalendar: WeekBoxMoveButtonDelegate {
    func didTapPreMoveButton() {
        print("전주 보여주세요")
        offset -= 1
        setDate(offset)
    }
    
    func didTapRightMoveButton() {
        print("다음주 보여주세요")
        offset += 1
        setDate(offset)
    }
}

weekbox 뷰 안의 아이콘을 눌렀을 때, delegate로 처리하여 각각 Offset 값을 다르게 주었습니다.

관련 PR

profile
Studying Frontend 👩🏻‍💻

1개의 댓글

comment-user-thumbnail
2025년 5월 20일

트러블 슈팅 관련해서 저도 뷰 높이 제대로 설정안해줘서 텍스트필드가 입력이 안되던 경험이 있어서...!
진짜 공감되는 글이엇더요🫠
달력도 일일이 커스텀하느라 수고 많으셨겠어요 굳쟙입니다용🥳

답글 달기