[iOS/Swift] UICalendarView 사용해 보기

Nakyung Lee·2023년 7월 5일
0

iOS

목록 보기
2/14
post-thumbnail

졸업 프로젝트에서 달력에 사용자가 그날 결과로 얻은 감정들을 이모지로 표시해 주는 기능을 추가하기로 했다.

처음에는 캘린더 라이브러리를 사용하려다가 자꾸 오류가 나서 UICalendarView 를 사용하여 구현하기로 했다.

여러 번의 삽질 끝에 기능 구현에 성공했다.

😗 그럼 바로 시작해 보자!


1. UIKit import하기

import UIKit

2. UICalendarView 생성

// 달력 선언
lazy var calendarView: UICalendarView = {
    let view = UICalendarView()
    view.translatesAutoresizingMaskIntoConstraints = false
    
    // 달력 커스텀을 위해 설정해 주어야 하는 속성
    view.wantsDateDecorations = true
        
    return view
}()

override func viewDidLoad() {
    super.viewDidLoad()
    
    applyConstraints()
}

fileprivate func applyConstraints() {
    view.addSubview(calendarView)
        
    let calendarViewConstraints = [
            calendarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            calendarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            calendarView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor )
    ]
        
    NSLayoutConstraint.activate(calendarViewConstraints)
}

3. UICalendarView에 권한 부여 & 특정 날짜에 이모지 표시하기

UICustomView는 Date형을 사용해 주어야 하기 때문에 문자열 -> Date형 형변환 함수 를 만들어 주었다.

📍 형변환 함수

// 문자열 -> Date 형변환 함수
func getStringToDate(strDate: String) -> Date {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd"
    dateFormatter.timeZone = NSTimeZone(name: "Ko_KR") as TimeZone?
        
    return dateFormatter.date(from: strDate)!
}

📍 전체 코드

import UIKit

class StatViewController: UIViewController {
    
    let day1 = "2023-07-02"
    let day2 = "2023-07-03"
    let day3 = "2023-07-04"
    
    let cal = Calendar.current
    
    // 더미데이터
    lazy var days = [ getStringToDate(strDate: day1) : "기쁨/사랑",  getStringToDate(strDate: day2) : "이별/슬픔", getStringToDate(strDate: day3) : "우울" ]
    
    // 달력 선언
    lazy var calendarView: UICalendarView = {
        let view = UICalendarView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.wantsDateDecorations = true
        
        return view
    }()
    
    var selectedDate: DateComponents? = nil
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        calendarView.delegate = self
        
        applyConstraints()
        setCalendar()
        reloadDateView(date: Date())
    }
    
    fileprivate func setCalendar() {
        calendarView.delegate = self
        
        let dateSelection = UICalendarSelectionSingleDate(delegate: self)
        calendarView.selectionBehavior = dateSelection
    }
    
    fileprivate func applyConstraints() {
        view.addSubview(calendarView)
        
        let calendarViewConstraints = [
            calendarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            calendarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            calendarView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor )
        ]
        
        NSLayoutConstraint.activate(calendarViewConstraints)
    }
    
    func reloadDateView(date: Date?) {
        if date == nil { return }
        let calendar = Calendar.current
        calendarView.reloadDecorations(forDateComponents: [calendar.dateComponents([.day, .month, .year], from: date!)], animated: true)
    }
    
    // 문자열 -> Date 형변환 함수
    func getStringToDate(strDate: String) -> Date {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        dateFormatter.timeZone = NSTimeZone(name: "Ko_KR") as TimeZone?
        
        return dateFormatter.date(from: strDate)!
    }
    
    
    // navigation bar 배경, 타이틀, item 색상 변경
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        appearance.backgroundColor = .primary
        appearance.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
        appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
        
        navigationController?.navigationBar.standardAppearance = appearance
        navigationController?.navigationBar.scrollEdgeAppearance = navigationController?.navigationBar.standardAppearance
        navigationController?.navigationBar.tintColor = .white
        
        // navigation bar 그림자 효과
        navigationController?.navigationBar.layer.masksToBounds = false
        navigationController?.navigationBar.layer.shadowColor = UIColor.primary?.cgColor
        navigationController?.navigationBar.layer.shadowOpacity = 0.8
        navigationController?.navigationBar.layer.shadowOffset = CGSize(width: 0, height: 2)
        navigationController?.navigationBar.layer.shadowRadius = 2
    }
    
}

extension StatViewController: UICalendarViewDelegate, UICalendarSelectionSingleDateDelegate {
    
    func dateSelection(_ selection: UICalendarSelectionSingleDate, didSelectDate dateComponents: DateComponents?) {
        selection.setSelected(dateComponents, animated: true)
        selectedDate = dateComponents
        reloadDateView(date: Calendar.current.date(from: dateComponents!))
    }
    
    // 캘린더에 감정 라벨링
    func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration? {
        
        let date = dateComponents.date!
        
        if days.keys.contains(date) {
            switch days[date] {
            case "기쁨/사랑":
                return .customView {
                    let label = UILabel()
                    label.text = "😘"
                    label.font = UIFont.systemFont(ofSize: 15)
                    label.textAlignment = .center
                    return label
                }
            case "이별/슬픔":
                return .customView {
                    let label = UILabel()
                    label.text = "😢"
                    label.font = UIFont.systemFont(ofSize: 15)
                    label.textAlignment = .center
                    return label
                }
            case "우울":
                return .customView {
                    let label = UILabel()
                    label.text = "🫠"
                    label.font = UIFont.systemFont(ofSize: 15)
                    label.textAlignment = .center
                    return label
                }
            case "멘붕/불안":
                return .customView {
                    let label = UILabel()
                    label.text = "🤯"
                    label.font = UIFont.systemFont(ofSize: 15)
                    label.textAlignment = .center
                    return label
                }
            case "스트레스/짜증":
                return .customView {
                    let label = UILabel()
                    label.text = "😡"
                    label.font = UIFont.systemFont(ofSize: 15)
                    label.textAlignment = .center
                    return label
                }
            default:
                return nil
            }
        } else {
            return nil
        }
    }
}

🔗 참고한 글

1️⃣ Getting UIKit's UICalendarView from iOS 16 fully functioning in a SwiftUI app
2️⃣ [iOS/Swift] 달력 UICalendarView Custom 예제 programmatically

profile
앱 개발자를 꿈꾸는 ✨

0개의 댓글