これはSwiftUIを使ったカレンダービュー作成に関する記事です。LazyVGridを使用します。
月の切り替えと日付の選択を可能にするSwiftUIのカレンダービューをSwiftパッケージ化しました
ビュー構造を作る
カレンダービューでは、列数7でLazyVGrid
を使用します。つまり、7要素毎に改行を追加します。
これを7に設定するのは、1週間の日数が7日だからです。
struct CalendarView: View {
// 表示される月の任意の日に設定します(通常は1日)
let monthToDisplay: Date
init(monthToDisplay: Date) {
self.monthToDisplay = monthToDisplay
}
var body: some View {
LazyVGrid(columns: Array(repeating: GridItem(), count: 7)) {
// TODO
}
}
}
LazyVGrid
では、まず平日の名称を表示し、その後、現在の月のすべての日をループ表示します。
LazyVGrid(columns: Array(repeating: GridItem(), count: 7)) {
// Week day labels
ForEach(["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"], id: \.self) { weekdayName in
Text(weekdayName)
}
// Day number text
Section {
ForEach(monthToDisplay.getDaysForMonth(), id: \.self) { date in
// Only display days of the given month
if Calendar.current.isDate(date, equalTo: monthToDisplay, toGranularity: .month) {
Text("\(date.getDayNumber())")
.padding(8)
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(8)
.id(date)
} else {
Text("\(date.getDayNumber())")
.padding(8)
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(8)
.hidden()
}
}
}
}
任意の月のすべての日を取得
上記の関数において、getDaysForMonth()
はDate
オブジェクトの拡張関数です。この関数は月の第一週の初めの日から最終週の最後の日までの日付を含む配列を生成します。
月の最初の週または最後の週は、前月または翌月の日を含む場合があるため、結果の配列をフィルター処理して、今月の日だけを含むようにします。
extension Date {
func getDaysForMonth() -> [Date] {
guard
let monthInterval = Calendar.current.dateInterval(of: .month, for: self),
let monthFirstWeek = Calendar.current.dateInterval(of: .weekOfMonth, for: monthInterval.start),
let monthLastWeek = Calendar.current.dateInterval(of: .weekOfMonth, for: monthInterval.end)
else {
return []
}
let resultDates = Calendar.current.generateDates(inside: DateInterval(start: monthFirstWeek.start, end: monthLastWeek.end),
matching: DateComponents(hour: 0, minute: 0, second: 0))
return resultDates
}
}
extension Calendar {
func generateDates(inside interval: DateInterval, matching components: DateComponents) -> [Date] {
var dates = [interval.start]
enumerateDates(startingAfter: interval.start,
matching: components,
matchingPolicy: .nextTime
) { date, _, stop in
if let date = date {
if date < interval.end {
dates.append(date)
} else {
stop = true
}
}
}
return dates
}
}
カレンダーを表示する
SwiftUIビューで、CalendarView
ビューコンポーネントを起動します。 monthToDisplay
入力では、表示したい月の任意の日を指定することができます。
struct ContentView: View {
var body: some View {
VStack {
if let targetMonth = Calendar.current.date(byAdding: .month, value: 0, to: Date()) {
Text(targetMonth.getYearMonthString())
CalendarView(monthToDisplay: targetMonth)
}
}
}
}