# CalendarView
[SwiftUI 開発記録](https://hackmd.io/29GdyEJPQ2yDAmajfRq0Gw?both#SwiftUI-%E9%96%8B%E7%99%BA%E8%A8%98%E9%8C%B2)

[TOC]
#### 機能追加情報
> [time=Sat, Apr 20, 2024 3:33 AM]
* DatePickerの追加
* カレンダー配置を上部に固定
---
#### now code
```swift=
//
// CalenderView.swift
// Financial_iPhoneApp
//
// Created by 有田健一郎 on 2024/02/29.
//
import SwiftUI
// 費用項目の構造体(表用)
struct ExpenseItem: Identifiable {
let id = UUID() // ユニークなID
let category: String // カテゴリ
let amount: Int // 金額
}
struct CalendarView: View {
// サンプルの費用項目リスト(表)
let expenses = [
ExpenseItem(category: "ランチ", amount: 900),
ExpenseItem(category: "消耗品", amount: 500),
ExpenseItem(category: "コンビニ", amount: 320),
ExpenseItem(category: "図書代", amount: 600),
ExpenseItem(category: "雑費", amount: 480)
]
let calendar = Calendar.current
let formatter = DateFormatter()
let weekDays = ["日", "月", "火", "水", "木", "金", "土"]
@State private var selectedDate: Date = Date()
@State private var selectedDateString: String = ""
@State private var selectedDay: Date?
@State private var isListVisible = false // リストの表示状態
init() {
formatter.dateFormat = "yyyy年 MM月"
}
@State private var isDatePickerVisible = false
// 選択された年と月
@State private var selectedYear: Int = Calendar.current.component(.year, from: Date())
@State private var selectedMonth: Int = Calendar.current.component(.month, from: Date())
// 表示する年の範囲
private let minYear: Int = 2000
private let maxYear: Int = 2024
var body: some View {
VStack {
VStack {
// 月の切り替えボタン
HStack {
Button(action: {
self.selectedDate = self.calendar.date(byAdding: .month, value: -1, to: self.selectedDate)!
}) {
Image(systemName: "chevron.left")
}
Spacer()
// 選択された月の表示
Button(action: {
// 月の表示部分がタップされたらDatePickerを表示する
self.isDatePickerVisible.toggle()
}) {
Text(formatter.string(from: selectedDate))
.font(.title)
.padding()
}
Spacer()
Button(action: {
self.selectedDate = self.calendar.date(byAdding: .month, value: 1, to: self.selectedDate)!
}) {
Image(systemName: "chevron.right")
}
}
.padding(.horizontal)
Divider()
// 曜日の表示
LazyVGrid(columns: Array(repeating: GridItem(), count: 7), spacing: 0) {
ForEach(weekDays, id: \.self) { day in
Text(day)
.frame(maxWidth: .infinity)
.padding(4)
.foregroundColor(self.textColor(for: day))
}
}
.padding(.horizontal)
Divider()
// カレンダーの日付を表示
LazyVGrid(columns: Array(repeating: GridItem(), count: 7), spacing: 0) {
ForEach(getCalendarMatrix(), id: \.self) { week in
ForEach(week, id: \.self) { date in
Button(action: {
if let date = date {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy年MM月dd日"
self.selectedDateString = formatter.string(from: date)
self.selectedDay = date
self.isListVisible = true // 日付が選択されたらリストを表示
} else {
self.selectedDateString = ""
self.selectedDay = nil
self.isListVisible = false // 日付が選択解除されたらリストを非表示
}
}) {
if date != nil {
Text(self.getDayText(date: date!))
.frame(maxWidth: .infinity)
.padding(8)
.foregroundColor(self.textColor(for: date!))
.background(self.selectedDay == date ? Color.green.opacity(0.5) : Color.white) // 背景色を選択状態に応じて変更
.bold(self.selectedDay == date)
} else {
// 前月の日付は空白
Text("")
.frame(maxWidth: .infinity)
.padding(8)
.background(Color.white)
}
}
}
}
}
.padding(.horizontal)
.padding(.bottom, 20) // 下部に余白を追加
Divider() // カレンダーと下部の区切り線
// 選択された日付を表示するテキスト
Text("\(selectedDateString) \(getDayOfWeek(selectedDay))")
.font(.headline)
.padding()
// 費用項目のリストを表示
if isListVisible {
List(expenses) { expense in
Text("\(expense.category): \(expense.amount)円")
}.frame(maxHeight: .infinity)
}
}
.sheet(isPresented: $isDatePickerVisible) {
// 年月のピッカーを表示するためのシート
VStack {
// DatePickerを閉じるボタン
Button(action: {
self.isDatePickerVisible = false
// 選択された年月からDateを生成
self.selectedDate = self.calendar.date(from: DateComponents(year: selectedYear, month: selectedMonth)) ?? Date()
}) {
Text("閉じる")
.foregroundColor(.blue)
.padding()
}
HStack {
// 年のピッカー
Picker(selection: $selectedYear, label: Text("")) {
ForEach(minYear...maxYear, id: \.self) { year in
Text("\(String(year))年").tag(year) // Stringに変換しないとカンマが入ってしまう
}
}
.pickerStyle(WheelPickerStyle())
.labelsHidden()
.frame(maxWidth: .infinity)
// 月のピッカー
Picker("Month", selection: $selectedMonth) {
ForEach(1...12, id: \.self) { month in
Text("\(month)月")
}
}
.pickerStyle(WheelPickerStyle())
.frame(maxWidth: .infinity)
}.toolbar {
}
}.presentationDetents([.height(280)]) // シートの高さ
}
Spacer()
}
}
// 日付から曜日を取得する関数
func getDayOfWeek(_ date: Date?) -> String {
guard let date = date else { return "" }
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "ja_JP") // 日本語のロケールを設定
dateFormatter.dateFormat = "(EEE)" // 曜日を省略形式で表示
return dateFormatter.string(from: date)
}
// 日付または曜日のテキストの色を設定する
func textColor(for component: Any) -> Color {
if let date = component as? Date {
let weekday = calendar.component(.weekday, from: date)
if weekday == 1 { // 日曜日
return .red
} else if weekday == 7 { // 土曜日
return .blue
} else {
return .primary
}
} else if let day = component as? String {
if day == "日" {
return .red
} else if day == "土" {
return .blue
} else {
return .primary
}
} else {
return .primary
}
}
// 日付を取得して文字列に変換する
func getDayText(date: Date?) -> String {
guard let date = date else { return "" }
let day = self.calendar.component(.day, from: date)
return (day == 1 ? "1" : "\(day)")
}
// カレンダーの日付を2次元配列に変換する
func getCalendarMatrix() -> [[Date?]] {
let year = calendar.component(.year, from: selectedDate)
let month = calendar.component(.month, from: selectedDate)
let startOfMonth = calendar.date(from: DateComponents(year: year, month: month, day: 1))!
let startDate = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: startOfMonth))!
let endDate = calendar.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth)!
var calendarMatrix: [[Date?]] = Array(repeating: Array(repeating: nil, count: 7), count: 6)
var currentDate = startDate
var weekIndex = 0
while currentDate <= endDate {
let dayOfWeek = calendar.component(.weekday, from: currentDate) - 1
// 日付が現在の月に属する場合のみ表示
if calendar.component(.month, from: currentDate) == month {
calendarMatrix[weekIndex][dayOfWeek] = currentDate
}
// 日曜日の場合、次の週へ移動
if dayOfWeek == 6 {
weekIndex += 1
}
currentDate = calendar.date(byAdding: .day, value: 1, to: currentDate)!
}
return calendarMatrix
}
}
#Preview {
CalendarView()
}
```
> [time=Sat, Apr 20, 2024 3:29 AM][name=ariken][color=#31b4ed]
---
#### old
```swift=
```
> [time=Sat, Apr 20, 2024 3:29 AM][name=ariken][color=#31b4ed]