반응형
🔥 목표는 위 캘린더를 구현하는것!
- 저는 Snapkit으로 구현하였습니다.
1. FSCalendar 뷰 생성 및 delegate, datasource 선언
private var calendar = FSCalendar()
//viewDidLoad()
calendar.delegate = self
calendar.dataSource = self
2. 달력 기본값 세팅
calendar.scope = .month //월 표시
calendar.locale = Locale(identifier: "ko_KR") //요일을 한글로 표시하기 위함
3. 헤더 - 기본 설정
calendar.appearance.headerDateFormat = "YYYY년 MM월" //헤더 포맷
calendar.appearance.headerTitleColor = .black //헤더 색 설정
calendar.appearance.headerTitleFont = .pretendardMedium16 //헤더 폰트
calendar.appearance.headerTitleAlignment = .center //헤더 정렬
calendar.headerHeight = 50 //헤더 높이
기본헤더를 사용하면 디자인처럼 헤더와 캘린더 사이의 간격을 디테일하게 조정하기에 어려움이 있고,
이전달/다음달을 넘기는 기능을 커스텀하기에 어려움이 있어
헤더와 이전 달/다음달 버튼을 커스텀했습니다.
3. 헤더 - 커스텀
- 기본 헤더 없애기
calendar.headerHeight = 0
- 레이아웃
//이전 달 버튼
private var prevMonthButton = UIButton().then{ view in
view.setBackgroundImage(Asset.icArrow2Left.image, for: .normal)
view.snp.makeConstraints{ make in
make.width.equalTo(7)
make.height.equalTo(14)
}
view.contentMode = .scaleAspectFit
}
//다음 달 버튼
private var nextMonthButton = UIButton().then{ view in
view.setBackgroundImage(Asset.icArrow2Right.image, for: .normal)
view.snp.makeConstraints{ make in
make.width.equalTo(7)
make.height.equalTo(14)
}
}
// 가운데 년 월 헤더
private var calendarHeaderTitle = UILabel().then { view in
view.font = .pretendardMedium16
view.textColor = .black
view.text = DateUtil.shared.formattedString(for: Date(), format: DateFormat.yyyyMKR)
//위 formattedString은 오늘 날짜(Date())를 년월일로 바꿔주는 함수로서, DateUtil로 따로 묶어두었습니다.
/*
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy년 M월"
return dateFormatter.string(from: Date())
*/
}
- 이전/다음달 이동 버튼
private var currentPage: Date?
private func scrollCalendar(isPrev: Bool) { //클릭 시 이전 달 / 다음달 띄우기 위한 메소드
let cal = Calendar.current
var dateComponents = DateComponents()
dateComponents.month = isPrev ? -1 : 1
self.currentPage = cal.date(byAdding: dateComponents, to: self.currentPage ?? Date())
self.calendar.setCurrentPage(self.currentPage!, animated: true)
self.calendarHeaderTitle.text = DateUtil.shared.formattedString(for: self.currentPage!, format: DateFormat.yyyyMKR)
}
prevMonthButton.rx.tap //이전달 이동
.subscribe({_ in
self.scrollCurrentPage(isPrev: true)
})
.disposed(by: disposeBag)
nextMonthButton.rx.tap //다음달 이동
.subscribe({ _ in
self.scrollCurrentPage(isPrev: false)
})
.disposed(by: disposeBag)
4. 캘린더 커스텀
calendar.appearance.todayColor = .blue //오늘 날짜 배경색
calendar.appearance.weekdayTextColor = .black //일~토 제목 타이틀 색
calendar.weekdayHeight = 34 //일~토 제목 높이
calendar.appearance.eventDefaultColor = .blue //이벤트 컬러
extension ViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { //달력
// 날짜 글씨 색 지정
func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? {
let day = Calendar.current.component(.weekday, from: date) - 1
if dateUtil.formattedString(for: dateUtil.now, format: .yyMMdd) != dateUtil.formattedString(for: date, format: .yyMMdd) { //'오늘'이 아닐 경우
if Calendar.current.shortWeekdaySymbols[day] == "일" {
return .red
} else if Calendar.current.shortWeekdaySymbols[day] == "토" {
return .blue
} else {
return .black
}
}
else{ //오늘은 'white' 로 표시
return .white
}
}
//이벤트가 있는 날에 점을 몇개 표시할 것인지?
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int { //이벤트가 있을 시, 점을 몇개 표시할건지
// 사전에 Date() 객체가 담길 datesWithEvent 배열을 선언해주었고, 이벤트가 있는 날짜들을 배열에 넣어두었습니다.
if self.datesWithEvent.contains(date){ //만약 캘린더의 특정 날짜가 배열의 날짜를 포함한다면 표시 1개
return 1
}
else{
return 0
}
}
//이벤트가 있는 날의 dot 표시 크기 조정
func calendar(_ calendar: FSCalendar, willDisplay cell: FSCalendarCell, for date: Date, at monthPosition: FSCalendarMonthPosition) {
let eventScaleFactor: CGFloat = 1.5
cell.eventIndicator.transform = CGAffineTransform(scaleX: eventScaleFactor, y: eventScaleFactor)
}
}
4번까지의 결과물은 다음과 같습니다.
5. 오늘 이전의 날짜 흐리게 만들기
extension ViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { //달력
// 날짜 색 지정
func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? {
let day = Calendar.current.component(.weekday, from: date) - 1
if dateUtil.formattedString(for: dateUtil.now, format: .yyMMdd) != dateUtil.formattedString(for: date, format: .yyMMdd) { //'오늘'이 아닐 경우
if date < dateUtil.now { //이전 날짜일 경우 alpha값 추가
if Calendar.current.shortWeekdaySymbols[day] == "일" {
return .salmon.withAlphaComponent(0.4)
} else if Calendar.current.shortWeekdaySymbols[day] == "토" {
return .cornFlower.withAlphaComponent(0.4)
} else {
return .black.withAlphaComponent(0.4)
}
}
else if date > dateUtil.now{ //이후 날짜일 경우
if Calendar.current.shortWeekdaySymbols[day] == "일" {
return .salmon
} else if Calendar.current.shortWeekdaySymbols[day] == "토" {
return .cornFlower
} else {
return .black
}
}
else{
return .black
}
}
else{ //오늘일 경우 흰 글자
return .white
}
}
반응형
'🍎 iOS > iOS 심화 & 응용' 카테고리의 다른 글
[iOS] 자체 private repository 라이브러리 Cocoapods 배포하기 (0) | 2024.12.05 |
---|---|
[iOS] Xcode Cloud를 활용하여 iOS CI/CD 환경 구축하기 (0) | 2024.03.31 |
[iOS/Swift] URLSession vs Alamofire (0) | 2023.07.20 |
[iOS/Swift] Tableview/Collectionview 페이징 (Pagination) (0) | 2022.02.06 |