티스토리 뷰

달력 뷰 미리 보기

Calendar 구조체도 알아봤으니 이제 달력 구현을 위하여 뷰를 구성해 보겠습니다.

위의 사진과 같은 심플한 달력을 만들 겁니다.
각각 뭐하는 친구들인지 설명도 적어드릴게요!

  • titleLabel : 2022년 01월과 같이 년, 월 정보를 보여줍니다.
  • previousButton : 이전 달로 이동하는 버튼입니다.
  • nextButton : 다음 달로 이동하는 버튼입니다.
  • todayButton : 현재 달로 이동합니다.
  • weekStackView : 일요일부터 토요일까지 요일을 보여줍니다.
  • collectionView : 1일부터 날짜를 보여줍니다.



달력 뷰 구성하기

프로젝트 생성 및 초기 설정

iOS App으로 프로젝트를 생성하겠습니다.

Product Name에 원하는 앱 이름을 적어주세요.

저는 스토리보드 없이 코드로만 작성할 거라서 스토리보드를 삭제했습니다.
스토리보드 없이 뷰 컨트롤러 연결하는 방법은 여기에서 확인해주세요!

import UIKit

final class ViewController: UIViewController {

    private lazy var scrollView = UIScrollView()
    private lazy var contentView = UIView()
    private lazy var titleLabel = UILabel()
    private lazy var previousButton = UIButton()
    private lazy var nextButton = UIButton()
    private lazy var todayButton = UIButton()
    private lazy var weekStackView = UIStackView()
    private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())

ViewController에 필요한 프로퍼티를 생성해줍니다.
작은 화면에서도 잘리지 않고 잘 보였으면 해서 UIScrollView도 생성해줬습니다.

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .systemBackground
        self.configure()
    }

    private func configure() {
        self.configureScrollView()
        self.configureContentView()
        self.configureTitleLabel()
    }

    private func configureScrollView() {

    }

    private func configureContentView() {

    }

    private func configureTitleLabel() {

    }

viewDidLoad()에서 뷰의 색상을 .systemBackground로 바꿔주고, 설정 메서드를 모아 놓은 메서드인 configure()를 불러주겠습니다.
configure()에서는 scrollView, contentView, titleLabel을 설정해 주는 메서드를 불러주세요.

    private func configureScrollView() {
        self.view.addSubview(self.scrollView)
        self.scrollView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.scrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
            self.scrollView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
            self.scrollView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
            self.scrollView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor)
        ])
    }

    private func configureContentView() {
        self.scrollView.addSubview(self.contentView)
        self.contentView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.contentView.topAnchor.constraint(equalTo: self.scrollView.topAnchor),
            self.contentView.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor),
            self.contentView.trailingAnchor.constraint(equalTo: self.scrollView.trailingAnchor),
            self.contentView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor),
            self.contentView.widthAnchor.constraint(equalTo: self.scrollView.widthAnchor)
        ])
    }

self.viewscrollView를 넣고 scrollViewcontentView를 넣어주었습니다.
위아래로만 스크롤할 거니 contentViewwidthscrollViewwidth와 같게 제약사항을 줍니다.

이제 이 contentView에 타이틀, 요일 스택 뷰, 달력 등을 넣어주면 됩니다.

 

년, 월 타이틀

    private func configureTitleLabel() {
        self.contentView.addSubview(self.titleLabel)
        self.titleLabel.text = "2000년 01월"
        self.titleLabel.font = .monospacedSystemFont(ofSize: 18, weight: .bold)
        self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.titleLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 20),
            self.titleLabel.centerXAnchor.constraint(equalTo: self.contentView.centerXAnchor)
        ])
    }

contentViewtitleLabel을 넣고 텍스트, 폰트 설정을 주고 상단에서 20 떨어진 위치 중앙에 배치를 해줍니다.

그 후 실행해 보면 위와 같은 화면을 확인할 수 있습니다.

 

버튼

이전 달, 다음 달, 이번 달로 이동하는 버튼을 설정해주겠습니다.

    private func configure() {
        self.configureScrollView()
        self.configureContentView()
        self.configureTitleLabel()
        self.configurePreviousButton()
        self.configureNextButton()
        self.configureTodayButton()
    }

설정 메서드를 만들어주고 configure()에서 불러줍니다.
앞으로 만드는 메서드들은 생성 후 모두 configure()에서 불러주면 됩니다!

    private func configurePreviousButton() {
        self.contentView.addSubview(self.previousButton)
        self.previousButton.tintColor = .label
        self.previousButton.setImage(UIImage(systemName: "chevron.left"), for: .normal)
        self.previousButton.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.previousButton.widthAnchor.constraint(equalToConstant: 44),
            self.previousButton.heightAnchor.constraint(equalToConstant: 44),
            self.previousButton.trailingAnchor.constraint(equalTo: self.titleLabel.leadingAnchor, constant: -5),
            self.previousButton.centerYAnchor.constraint(equalTo: self.titleLabel.centerYAnchor)
        ])
    }

    private func configureNextButton() {
        self.contentView.addSubview(self.nextButton)
        self.nextButton.tintColor = .label
        self.nextButton.setImage(UIImage(systemName: "chevron.right"), for: .normal)
        self.nextButton.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.nextButton.widthAnchor.constraint(equalToConstant: 44),
            self.nextButton.heightAnchor.constraint(equalToConstant: 44),
            self.nextButton.leadingAnchor.constraint(equalTo: self.titleLabel.trailingAnchor, constant: 5),
            self.nextButton.centerYAnchor.constraint(equalTo: self.titleLabel.centerYAnchor)
        ])
    }

똑같이 contentView에 넣어주고 색상, 이미지, 제약 사항을 설정해줍니다.

    private func configureTodayButton() {
        self.contentView.addSubview(self.todayButton)
        self.todayButton.setTitle("Today", for: .normal)
        self.todayButton.setTitleColor(.systemBackground, for: .normal)
        self.todayButton.backgroundColor = .label
        self.todayButton.layer.cornerRadius = 5
        self.todayButton.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.todayButton.widthAnchor.constraint(equalToConstant: 60),
            self.todayButton.heightAnchor.constraint(equalToConstant: 30),
            self.todayButton.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10),
            self.todayButton.centerYAnchor.constraint(equalTo: self.titleLabel.centerYAnchor)
        ])
    }

이번 달로 이동하는 버튼은 타이틀과 배경색을 모두 설정해주었습니다.
약간 둥근 사각형을 만들고 싶어서 cornerRadius를 5로 주었습니다.
위치는 contentView.trailing 에서 -10 떨어진 위치에 todayButton.trailing이 오게 설정해주었습니다.

버튼 설정이 끝났습니다.

 

요일 스택 뷰

    private func configureWeekStackView() {
        self.contentView.addSubview(self.weekStackView)
        self.weekStackView.distribution = .fillEqually
        self.weekStackView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.weekStackView.topAnchor.constraint(equalTo: self.titleLabel.bottomAnchor, constant: 40),
            self.weekStackView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 5),
            self.weekStackView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -5)
        ])
    }

weekStackView의 distribution을 .fillEqually로 설정하여 스택 뷰에 들어가는 아이템이 모두 같은 크기를 가지게 해 줍니다.
스택 뷰 설정이 끝났으니 아래에서 스택 뷰에 들어갈 아이템을 생성해주겠습니다.

    private func configureWeekLabel() {
        let dayOfTheWeek = ["일", "월", "화", "수", "목", "금", "토"]

        for i in 0..<7 {
            let label = UILabel()
            label.text = dayOfTheWeek[i]
            label.textAlignment = .center
            self.weekStackView.addArrangedSubview(label)

            if i == 0 {
                label.textColor = .systemRed
            } else if i == 6 {
                label.textColor = .systemBlue
            }
        }
    }

배열에 요일을 넣어 준 후 for문을 돌며 UILabel을 만들어 weekStackView에 넣어줍니다.

요일 스택 뷰 설정도 끝났습니다!

 

달력 컬렉션 뷰

이제 진짜 마지막입니다.
가장 중요한 collectionView설정만 해주면 끝입니다!

커스텀 셀을 위하여 새로운 파일을 만들어줍니다.

Cocoa Touch Class를 선택해 준 후 Class이름을 설정해주세요.

    static let identifier = "CalendarCollectionViewCell"
    private lazy var dayLabel = UILabel()

CalendarCollectionViewCellidentifierdayLabel을 추가해 줍니다.

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.configure()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.configure()
    }

    private func configure() {
        self.addSubview(self.dayLabel)
        self.dayLabel.text = "0"
        self.dayLabel.font = .boldSystemFont(ofSize: 12)
        self.dayLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.dayLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 5),
            self.dayLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor)
        ])
    }

required init?(coder: NSCoder)override init(frame: CGRect)에서 configure()를 불러줍니다.
잘 보이는지 확인하기 위해 텍스트를 "0"으로 설정해줬습니다.

다시 ViewController로 돌아가겠습니다.

    private func configureCollectionView() {
        self.contentView.addSubview(self.collectionView)
        self.collectionView.dataSource = self
        self.collectionView.delegate = self
        self.collectionView.register(CalendarCollectionViewCell.self, forCellWithReuseIdentifier: CalendarCollectionViewCell.identifier)
        self.collectionView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.collectionView.topAnchor.constraint(equalTo: self.weekStackView.bottomAnchor, constant: 10),
            self.collectionView.leadingAnchor.constraint(equalTo: self.weekStackView.leadingAnchor),
            self.collectionView.trailingAnchor.constraint(equalTo: self.weekStackView.trailingAnchor),
            self.collectionView.heightAnchor.constraint(equalTo: self.collectionView.widthAnchor, multiplier: 1.5),
            self.collectionView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
        ])
    }

collectionView를 설정해주는 메서드를 생성한 후 collectionViewdataSourcedelegateself로 설정해줍니다.
아까 생성한 커스텀 셀을 넣어주기 위해서 register도 해줍니다.

extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

}

ViewController에서 UICollectionView 설정에 필요한 프로토콜 세 가지를 채택합니다.

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 42
    }

테스트용으로 42개의 셀을 생성해주겠습니다.

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CalendarCollectionViewCell.identifier, for: indexPath) as? CalendarCollectionViewCell else { return UICollectionViewCell() }
        return cell
    }

아까 만들어준 커스텀 셀을 collectionView에 넣어주도록 설정합니다.


    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = self.weekStackView.frame.width / 7
        return CGSize(width: width, height: width * 1.3)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return .zero
    }

셀의 크기와 간격을 위와 같이 설정해줍니다.

실행시켜 보면 위와 같은 화면을 만날 수 있습니다.
달력 뷰 구성이 끝났습니다!

다음부터 만들어진 뷰로 본격적인 달력 구현에 들어가겠습니다!

 


 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함