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

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

이렇게 구조를 짰고, 달력 부분은 컬렉션 뷰로 만들었습니다.
didSelectItmeAt 함수가 호출이 아예 안되는 이슈가 있었습니다. 알고보니.. 상단에 있는 자잘한 것들을 WeekBox 뷰로 묶고, 달력을 WeekCalendar 뷰로 묶었는데 그 뷰들에 height 제약을 걸어주지 않아서 생긴 문제였습니다. 높이제약을 잘 걸었는지 항상 확인합시다...
달력과 관련된 일들을 처리하는 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를 선언해주었습니다.
셀에 날짜를 뿌려줄 때 다음과 같이 작성했습니다.
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..으로 표시해주기 위해서입니다.
위의 왼쪽 화살표 아이콘, 오른쪽 화살표 아이콘을 누르면 전주, 다음주로 달력을 이동해야합니다.
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 값을 다르게 주었습니다.
트러블 슈팅 관련해서 저도 뷰 높이 제대로 설정안해줘서 텍스트필드가 입력이 안되던 경험이 있어서...!
진짜 공감되는 글이엇더요🫠
달력도 일일이 커스텀하느라 수고 많으셨겠어요 굳쟙입니다용🥳