3
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SwiftUIでカレンダービューを作り(LazyVGridを使って100行未満のコードで)

Last updated at Posted at 2022-03-02

これはSwiftUIを使ったカレンダービュー作成に関する記事です。LazyVGridを使用します。

F9972033-1538-4111-A8A2-893FF787B8CC.jpeg

月の切り替えと日付の選択を可能にする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)
            }
        }
    }
}


:relaxed: Twitter @MszPro

:sunny: 私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。

3
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?