Jiwift

[iOS/Swift] JTAppleCalendar 달력 생성하기 본문

라이브러리/기타

[iOS/Swift] JTAppleCalendar 달력 생성하기

지위프트 2024. 1. 31. 17:32
반응형

 JTAppleCalendar를 사용해서 달력을 생성하고 출력하도록 하겠습니다. JTAppleCalendar를 사용법을 공유하는 한글 게시글이 아예 안 나오는데.. 저만 그런가요?? 아무튼 시작.

 

 JTAppleCalendar는 CollectionView를 기본으로 하는 라이브러리입니다. 일단 모두가 보고 알 수 있게 Storyboard를 사용해서 글을 작성하겠습니다. Code Base로 하실 분들은 참고하여 진행해 주세요.

 

import UIKit
import JTAppleCalendar

class ViewController: UIViewController {

    let df = DateFormatter()

    override func viewDidLoad() {
        super.viewDidLoad()

    }
}
import UIKit
import JTAppleCalendar

class MyDateCell: JTACDayCell {
    override func awakeFromNib() {
        super.awakeFromNib()
        
    }
}

 일단 아무 생각 없이 위 두 코드를 작성해 주세요. 적용하고 싶은 View단에 'JTAppleCalendar'를 import 해주시고 CollectionView에 사용할 Cell 코드도 생성해 줍니다.

 

CollectionView 생성

 UICollectionView를 생성하고 Class는 'JTACMonthView', Module은 'JTAppleCalendar'로 지정합니다. 그리고 월을 표시하기 위해 Label을 하나 생성했습니다. 일단 기본 문구로 '달력 기본 표시'라고 해두었습니다.

 

 

 

 UICollectionViewCell의 Class는 'MyDateCell'로 지정하고 Identifier도 'MyDateCell'로 동일하게 적용하도록 하겠습니다. 

 

UI 추가

 그리고 Cell의 ContentView안에 UIView와 UILabel을 추가해 주세요. View는 선택했을 때 표시되는 기능을 할 것이고 Label은 Date를 표시할 예정입니다. 둘의 Constraint는 상/하/좌/우 모두 ContentView에 붙게 하였습니다. 

 

UIView 설정

  생성한 UIView는 배경색을 아무거나 지정해 주세요. 실험이니 원하는 색으로 하시면 됩니다. 그리고 Hidden을 기본 값으로 적용해 주세요. 

 

 *UIView는 필수는 아니지만 Date를 선택했다는 기능까지는 있어야 그럴듯하니 추가했습니다. 

 

ViewController 연결

 이제는 아까 코드로 생성한 ViewController에 달력 월을 표시하기 위한 Label과 달력을 표시하기 위한 CollectionView를 연결합니다. 예제 코드로는 Label에 현재 표시되고 있는 달이 표시됩니다. 

Cell 연결

 'MyDateCell'에 label과 view를 연결합니다. 저는 'selectedView', 'dateLabel'로 설정했습니다.

 

override func viewDidLoad() {
    super.viewDidLoad()

	// Delegate 설정
    self.calendarView.ibCalendarDataSource = self
    self.calendarView.ibCalendarDelegate = self

	// 월 표시
    self.calendarView.visibleDates() { visibleDates in
        self.setupMonthLabel(date: visibleDates.monthDates.first!.date)
    }
    
    // Date 다중 선택 기능
    self.calendarView.allowsMultipleSelection = true
}

 제일 먼저 기본 설정을 해줍니다. DataSource와 Delegate를 연결하고, didload 시점에 월 표시를 한번 해줍니다. 그리고 일 다중 선택이 가능한 여부를 설정합니다. 일단 true로 하고 어떻게 동작하는지 보면 됩니다. 

 

// Label에 달 표시
func setupMonthLabel(date: Date) {
    self.df.dateFormat = "달력: MMM"
    // 한국 로케일 설정
    self.df.locale = Locale(identifier: "ko_KR")
    self.monthLabel.text = df.string(from: date)
}

// Cell 다루기
func handleConfiguration(cell: JTACDayCell?, cellState: CellState) {
    guard let cell = cell as? MyDateCell else { return }
    self.handleCellColor(cell: cell, cellState: cellState)
    self.handleCellSelection(cell: cell, cellState: cellState)
}

// Cell 현재 달인 경우 Date 색상
func handleCellColor(cell: MyDateCell,
                     cellState: CellState) {
    if cellState.dateBelongsTo == .thisMonth {
        cell.dateLabel.textColor = .black
    } else {
        cell.dateLabel.textColor = .gray
    }
}

// Cell 선택
func handleCellSelection(cell: MyDateCell,
                         cellState: CellState) {
    cell.selectedView.isHidden = !cellState.isSelected
        switch cellState.selectedPosition() {
        case .left:
            cell.selectedView.layer.cornerRadius = cell.selectedView.frame.height / 2
            cell.selectedView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMinXMinYCorner]
        case .middle:
            cell.selectedView.layer.cornerRadius = cell.selectedView.frame.height / 2
            cell.selectedView.layer.maskedCorners = []
        case .right:
            cell.selectedView.layer.cornerRadius = cell.selectedView.frame.height / 2
            cell.selectedView.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner]
        case .full:
            cell.selectedView.layer.cornerRadius = cell.selectedView.frame.height / 2
            cell.selectedView.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner]
        default: break
	}
}

 동작하는데 필요한 함수입니다. 크게 기능을 하지는 않고, 선택, 색상과 같은 기능들을 합니다. 이건 기본 예제를 그냥 복사했습니다. 

extension ViewController: JTACMonthViewDelegate, JTACMonthViewDataSource {
    func calendar(_ calendar: JTACMonthView, willDisplay cell: JTACDayCell, forItemAt date: Date, cellState: CellState, indexPath: IndexPath) {
        self.handleConfiguration(cell: cell, cellState: cellState)
    }
    
    func calendar(_ calendar: JTACMonthView, cellForItemAt date: Date, cellState: CellState, indexPath: IndexPath) -> JTACDayCell {
        let cell = calendar.dequeueReusableJTAppleCell(withReuseIdentifier: "MyDateCell", for: indexPath) as! MyDateCell
        cell.dateLabel.text = cellState.text
        self.calendar(calendar, willDisplay: cell, forItemAt: date, cellState: cellState, indexPath: indexPath)
        return cell
    }
    
    func calendar(_ calendar: JTACMonthView, didScrollToDateSegmentWith visibleDates: DateSegmentInfo) {
        self.setupMonthLabel(date: visibleDates.monthDates.first!.date)
    }
    
    func calendar(_ calendar: JTACMonthView, didSelectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) {
        self.handleConfiguration(cell: cell, cellState: cellState)
    }
    
    func calendar(_ calendar: JTACMonthView, didDeselectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) {
        self.handleConfiguration(cell: cell, cellState: cellState)
    }
    
    func configureCalendar(_ calendar: JTACMonthView) -> ConfigurationParameters {
        let df = DateFormatter()
        df.dateFormat = "yyyy MM dd"
        
        let startDate = df.date(from: "2017 01 01")!
        let endDate = df.date(from: "2017 12 31")!
        
        let parameter = ConfigurationParameters(startDate: startDate,
                                                endDate: endDate,
                                                numberOfRows: 6,
                                                generateInDates: .forAllMonths,
                                                generateOutDates: .tillEndOfGrid,
                                                firstDayOfWeek: .sunday)
        return parameter
    }
}

 DataSource와 Delegate를 위해서 Extension를 작성합니다. ConfigureCalendar에서 시작날과 끝날을 지정할 수 있습니다. 그 외는 선택 혹은 데이터 표시입니다. 

 

 짜잔 실행시키면 이렇게 평범한 달력이 나오게 됩니다. 이제 여기서 다른 것들을 더 알아가는 글들을 작성해보려고 합니다. 여기까지 하셨으면 직접 코드를 보면서 꾸미시거나 하는 데는 문제가 없을 거라고 생각이 들긴 합니다.

 

반응형