1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[SwiftUI]SwiftUIでカレンダー実装〜UIViewCalenderをUIViewRepresentableとCoordinatorでカスタマイズ〜

Last updated at Posted at 2025-09-06

SwiftUIでカレンダーを使う

今回、個人開発のアプリでカレンダーを使おうかなーと思って探しましたがSwiftUI製でいいものが見つかりませんでした。
そこで、UICalendarViewというUIKit製のカレンダーがあったのでこちらをSwiftUIで使ってみることにしました。

UICalendarView

iOS16以降であれば使用できるカレンダーコンポーネントです。
カレンダーの表示から、日付の下にラベル表示できたり等、色々と拡張性を持ったカレンダーになります。
ただ、SwiftUI用ではないので使うためには色々と準備が必要になります。

UIViewRepresentableを使ってUICalenderViewをSwiftUIで使う

UIKitとSwiftUIの具体的な変換方法は以下です。

  1. UIViewRepresentableプロトコルに準拠した構造体を定義
  2. プロトコルの実装(makeUIView,updateUIView)
  3. SwiftUIのビューから呼ぶ

1. UIViewRepresentableプロトコルに準拠した構造体を定義

UICalendarViewはUIKitの部品です。そのままではSwiftUIで使用できません。
そこで、UIViewRepresentableプロトコルを使います。
UIViewRepresentableはUIKitの部品をSwiftUIで使えるようにしてくれます。

プロトコルを実装したコード全体を記載します。
構造体名はCalenderViewとしています。

struct CalenderView: UIViewRepresentable {
    // ビューの更新時の処理を記述する
    func updateUIView(_ uiView: UIViewType, context: Context) {
        // 更新時の処理を記載
    }

    // UIKit製の部品を作成する
    func makeUIView(context: Context) -> some UIView {
        let calenderView = UICalendarView()
        calenderView.delegate = context.coordinator
        return calenderView
    }
}

これからプロトコルの実装詳細を説明していきます。

プロトコルの実装

UIViewRepresentableプロトコルを使うためには、以下二つのメソッドを実装する必要があります。

  • makeUIView
  • updateUiView

それぞれ、役割を説明します。

makeUIView

UIKitの部品を作成するメソッドです。
今回は、UICalenderViewのインスタンスを作成して返却しています。
このメソッドは作成時の一度しか行われません。
更新などが行われた際には次に紹介するupdateUiViewメソッドを使って更新時の処理を記述してください。

func makeUIView(context: Context) -> some UIView {
    let calenderView = UICalendarView()
    calenderView.delegate = context.coordinator
    return calenderView
}

updateUIView

SwiftUIのView変化や更新があった際にUIKitのビューを更新するために用います。
例えば、親ビューから@Bindingで値を受け取る変数があった場合、親ビューで変数が更新されると子ビューのupdateUIViewも呼ばれます。
SwiftUIのビューとUIKitの連動をサポートするメソッドですね。
なお、今回はカレンダーの作成だけなのでupdateUiViewの実装は空にしてあります。

func updateUIView(_ uiView: UIViewType, context: Context) {
    // 更新時の処理を記載
}

SwiftUIのビューから呼ぶ

では、SwiftUIのから呼び出す準備が整ったので使ってみましょう。

mport SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            CalenderView()
                .padding()
        }
        .padding()
    }
}

struct CalenderView: UIViewRepresentable {
    func updateUIView(_ uiView: UIViewType, context: Context) {
    }

    func makeUIView(context: Context) -> some UIView {
        let calenderView = UICalendarView()
        return calenderView
    }
}

メインのビューからCalenderViewを呼び出します。
といってもインスタンス化しているだけです。

いい感じのカレンダーが表示されました。
このままではただカレンダーが表示されただけなので、日付の下にアイコンなどを表示させてみましょう。

UICalenderViewで日付の下に絵文字を表示する(Coordinatorの利用)

実装手順は以下です。
日付の下に絵文字(「🎉」)を表示してみましょう。

UICalendarViewDelegate

UICalendarViewDelegateプロトコルのcalendarViewメソッドを使います。
このメソッドを実装するだけで、絵文字を表示など自由にカスタマイズできます。
(もちろん絵文字だけでなく画像など色々と表示可能です)
ただ、このままではUICalendarViewDelegateプロトコルを実装してもカレンダービューに渡す方法がありません。

Coordinator

ここで使われるのがCoordinatorです。CoordinatorはSwiftUIでUIKitのイベントやデータを管理したり設定するための仕組みです。
CoordinatorでUICalenderViewDelegateを実装して、UICalenderViewのdelegateに渡します。

Coordinatorクラスの実装を記載します。

class Coordinator: NSObject, UICalendarViewDelegate {
    private let parent: CalenderView
    init(parent: CalenderView) {
        self.parent = parent
    }

    // カレンダーに表示するデータ
    func calendarView(
        _ calendarView: UICalendarView,
        decorationFor dateComponents: DateComponents
    ) -> UICalendarView.Decoration? {
        // 絵文字を表示
        return .customView {
            let emoji = UILabel()
            emoji.text = "🎉"
            return emoji
    }
}

parentに親ビューを渡します。
UICalendarViewDelegateプロトコルの中のcalendarViewメソッドを実装することで表示を変えられます。
表示させたいビューを作成して返却します。返却する際に戻り値を合わせるため、.customViewで戻り値をUILabelからUICalendarView.Decorationに変えています。
簡単ではありますが、Coordinatorクラスを実装できました。これをCalenderViewに定義して使います。

Coordinatorを利用する

Coordinatorを作成したので利用してみましょう。
CalenderViewのインナークラスとして定義します。

struct CalenderView: UIViewRepresentable {
    func updateUIView(_ uiView: UIViewType, context: Context) {
    }
    
    func makeUIView(context: Context) -> some UIView {
        let calenderView = UICalendarView()
        calenderView.delegate = context.coordinator
        return calenderView
    }

    func makeCoordinator() -> Coordinator {
        // Coordinatorに自分自身を渡す
        Coordinator(parent: self)
    }

    class Coordinator: NSObject, UICalendarViewDelegate {
        private let parent: CalenderView
        init(parent: CalenderView) {
            self.parent = parent
        }

        // dateComponentsには日付のデータが入っている
        // カレンダー作成の際に一日ずつ呼ばれている
        func calendarView(
            _ calendarView: UICalendarView,
            decorationFor dateComponents: DateComponents
        ) -> UICalendarView.Decoration? {
            return .customView {
                let emoji = UILabel()
                emoji.text = "🎉"
                return emoji
            }
        }
    }

Coordinatorクラス追加に際して、makeCoordinatorメソッドを追加しています。
このメソッドはmakeUIViewより前に自動的に呼ばれるメソッドで他メソッドからcoordinatorを参照可能にします。
(メソッドの順番がmakeCoordinator->makeUIViewの順になります)
Coordinatorを作ると、makeUIViewなどの引数のcontext内に格納されて渡されます。
メソッド内でUICalendarViewのdelegateフィールドにCoordinatorを使って設定しましょう。
動かしてみましょう。

今回は、日付の指定をしていないので全部の日付で絵文字が表示されています。
日付の条件とか表示させる絵文字を変えることで色々と表示/非表示がカスタマイズできます。

終わりに

SwiftUIでカレンダーの作成とちょっとしたカスタマイズを見てきました。
UIKit製のコンポーネントをSwiftUIへの変換など、慣れないと色々と戸惑うことが多いと思います。
さらにカスタマイズには、delegateの実装が必要でそれを設定するためにCoordinatorを使う必要があります。(ややこしい)
今回はカレンダーの例でしたが、他のUIKitの部品でも応用が効くと思います。
色々と試してみてください。

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?