WidgetKit 実装について
iOS 14 から利用可能になった WidgetKit ですが、これまでの Today Widget とは異なる点が多く、戸惑う箇所が多々ありました...。ここでは、調査したことをまとめて、書いておきたいと思います。
主な参考サイト
WidgetKitのUIを作る上で、UIKit (UIView) は使用できる?
WidgetKitでは、UIKitは使用不可です。そのため、画面要素はすべて SwiftUI で作成する必要があります。
既存のTodayWidgetから、UIViewを使い回せたら楽だなと思ったんですが...簡単に事は運べないのでした...。
ちなみに、UIViewRepresentable(UIKitのViewをSwiftUIで利用するためのプロトコル)を実装したViewを、Widget上に置くと、「進入禁止標識」が表示されてしまい、使用できない感じになってます。
参考 : ios - Why do some views appear as a red no entry sign in widgets? - Stack Overflow
旧来のTodayWidgetと、WidgetKitの共存は可能?
プロジェクトに両方を含めるのは可能です。アプリでTodayWidgetとWidgetKitの両方を実装していた場合は、以下のようになるようです。
- iOS13以下の端末 → TodayWidgetが使用できる (WidgetKitは使用できない)
- iOS14の端末 → TodayWidget, WidgetKit が使用できる
参考 : Today Extensions Deprecated? | Apple Developer Forums
注: 参考サイトには「iOS14の端末では、TodayWidgetは使えない」のようなことも書いてありますが、自分で試したらiOS 14 でも TodayWidget 動作しました。
対応サイズ(Small, Medium, Large)を固定するには? (Smallだけのみとか)
- Widget の body のところで、supportedFamilies を設定します
// Smallだけに対応する場合
.supportedFamilies([.systemSmall])
// SmallとLargeに対応する場合
.supportedFamilies([.systemSmall, .systemLarge])
参考リンク : WidgetFamily | Apple Developer Documentation
表示サイズ(Small, Medium, Large)に応じて、描画を切り替えるのはどうやって行う?
View の body の中で、Switch文を使って切り替えます。詳しくは↓のリンクを参照ください。
タップ操作は可能?(Widget上にボタンなどを配置可能?)
ボタンなどのコントロールは、機能しません。Viewに配置することは可能ですが、タップしてもイベントが来ないみたいでした。
Widgetタップ時は、カスタムURLを使用して、アプリ本体を起動する動作のみ可能です。
- WidgetSizeがSmallのときは、Widgetをタップすると、アプリ本体が起動します(これはWidgetのdefaultの動作)。
- WidgetSizeがMediumかLargeの時は、クリックするViewによって起動URLを変えることが可能です(Viewに対してLinkを設定する)
参考:
- swift - How to add Buttons to a iOS 14 Widget - Stack Overflow
- widgetURL(_:) | Apple Developer Documentation
ViewにLinkを設定する方法は?
// リンクを設定できます
Link(destination: URL(string: "sample-app-url")!) {
Text("click to launch app")
}
// 画像にもリンクを設定できます
Link(destination: URL(string: "sample-app-url")!) {
Image(systemName: "hand.thumbsup.fill")
}
参考:
アプリ本体からのデータ参照はどうやって行う?
- AppGroup を使って行うのが、定番のようです。このあたりは、Today Widgetの頃から変わらずですね。
参考リンク : Sharing data with a Widget
アプリ本体でデータを更新したとき、Widgetに反映するには?
WidgetCenter を使用します。
//*** アプリ本体側のソースコード ***//
import WidgetKit
// Widgetにデータ更新を通知する
if #available(iOS 14.0, *) {
WidgetCenter.shared.reloadAllTimelines()
}
アプリに複数のWidgetがあり、そのうち1つを更新したい時は reloadTimelines(ofKind: "widget-identifier")
を使用します。
参考: WidgetCenter | Apple Developer Documentation
Placeholder, SnapShot, Timelineとは何か?
- Placeholder : 端末の設定の変更時に利用される画面
- SnapShot : Widget選択画面や、Widgetの初期表示などに使用される画面
- Timeline : Widgetの画面更新時間を、配列的に指定できる([9:00, 10:00, 11:00]のように)。時間になると、Widgetが再描画される。
IntentTimelineProvider に getSnapshot(), getTimeline() というのがあるので、そこでSnapShot / TimeLineに必要なデータを設定する。
説明が難しい...これに関しては、参考リンクを参照していただくほうが良いと思います。