今更カレンダーなんて作りたくなかったけど、ライブラリ使いたくないし意外とコードが落ちてなくて
こちらの記事を参考にしたが結構めちゃくちゃだったので。
自分のように最小限コピペで動かせるものが欲しい人もいると思うので共有します。
Extension
日付と色のextensionです。
Ex.swift
extension UIColor {
class var lightBlue: UIColor {
return UIColor(red: 92.0 / 255, green: 192.0 / 255, blue: 210.0 / 255, alpha: 1.0)
}
class var lightRed: UIColor {
return UIColor(red: 195.0 / 255, green: 123.0 / 255, blue: 175.0 / 255, alpha: 1.0)
}
}
extension Date {
func string(format: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = format
return formatter.string(from: self)
}
}
MonthDateManager
MonthDateManager.swift
import Foundation
final class MonthDateManager {
private let calendar = Calendar.current
private (set) var days: [Date] = []
private var firstDate: Date! {
didSet {
days = createDaysForMonth()
}
}
var monthString: String {
return firstDate.string(format: "YYYY/MM")
}
init() {
var component = calendar.dateComponents([.year, .month], from: Date())
component.day = 1
firstDate = calendar.date(from: component)
days = createDaysForMonth()
}
func createDaysForMonth() -> [Date] {
// 月の初日の曜日
let dayOfTheWeek = calendar.component(.weekday, from: firstDate) - 1
// 月の日数
let numberOfWeeks = calendar.range(of: .weekOfMonth, in: .month, for: firstDate)!.count
// その月に表示する日数
let numberOfItems = numberOfWeeks * 7
return (0..<numberOfItems).map { i in
var dateComponents = DateComponents()
dateComponents.day = i - dayOfTheWeek
return calendar.date(byAdding: dateComponents, to: firstDate)!
}
}
func nextMonth() {
firstDate = calendar.date(byAdding: .month, value: 1, to: firstDate)
}
func prevMonth() {
firstDate = calendar.date(byAdding: .month, value: -1, to: firstDate)
}
}
CalendarCell
セルはラベルだけです。
土曜日は青くするといった要件はモデルの初期化に打ち込みます。
CalenderCell.swift
import UIKit
final class CalendarCell: UICollectionViewCell {
private var label: UILabel = {
let it = UILabel()
it.textAlignment = .center
it.translatesAutoresizingMaskIntoConstraints = false
return it
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(label)
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: contentView.topAnchor),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
label.leftAnchor.constraint(equalTo: contentView.leftAnchor),
label.rightAnchor.constraint(equalTo: contentView.rightAnchor),
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(model: Model) {
label.text = model.text
label.textColor = model.textColor
}
}
extension CalendarCell {
struct Model {
var text: String = ""
var textColor: UIColor = .black
}
}
extension CalendarCell.Model {
init(date: Date) {
let weekday = Calendar.current.component(.weekday, from: date)
if weekday == 1 {
textColor = .lightRed
} else if weekday == 7 {
textColor = .lightBlue
} else {
textColor = .gray
}
text = date.string(format: "d")
}
}
ViewController
ViewController.swift
final class ViewController: UIViewController {
private let dateManager = MonthDateManager()
private let weeks = ["日","月", "火", "水", "木", "金", "土"]
private let itemSize: CGFloat = (UIScreen.main.bounds.width - 60) / 7
private lazy var calenderCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 10
layout.itemSize = CGSize(width: itemSize, height: 50)
let collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView.backgroundColor = .white
collectionView.register(CalendarCell.self, forCellWithReuseIdentifier: "cell")
collectionView.delegate = self
collectionView.dataSource = self
return collectionView
}()
private func setUpNavigationBar() {
navigationController?.navigationBar.barTintColor = UIColor(red: 255/255, green: 132/255, blue: 214/255, alpha: 1)
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.titleTextAttributes = [.foregroundColor : UIColor.white]
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "next",
style: .plain,
target: self,
action: #selector(actionNext)
)
navigationItem.leftBarButtonItem = UIBarButtonItem(
title: "back",
style: .plain,
target: self,
action: #selector(actionBack)
)
title = dateManager.monthString
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
calenderCollectionView.frame.size.width = view.bounds.width
calenderCollectionView.frame.size.height = 500
view.addSubview(calenderCollectionView)
setUpNavigationBar()
}
@objc private func actionNext() {
dateManager.nextMonth()
calenderCollectionView.reloadData()
title = dateManager.monthString
}
@objc private func actionBack() {
dateManager.prevMonth()
calenderCollectionView.reloadData()
title = dateManager.monthString
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return section == 0 ? weeks.count : dateManager.days.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CalendarCell
if indexPath.section == 0 {
let day = weeks[indexPath.row]
let model = CalendarCell.Model(text: day, textColor: .black)
cell.configure(model: model)
} else {
let date = dateManager.days[indexPath.row]
cell.configure(model: CalendarCell.Model(date: date))
}
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.section == 0 {
return
}
title = dateManager.days[indexPath.row].string(format: "YYYY/MM/dd")
}
}
今回は参考元の記事を尊重したコードを公開しましたが
実際には前月の日付は表示したくない、スクロールしたいとかいった要望があると思います。
にしても今更カレンダーなんて作りたくなかった。
素直にライブラリ使えばよかった。