本記事では以下の内容を説明します。
既存のアプリケーションへのウィジェットの追加
アプリケーションターゲットの作成
既存の iOS アプリケーションへのウィジェットの追加は簡単です。ターゲット Widget Extension
を追加してください。
さて、 Include Configuration Intent
というボックスを選択していることを確認してください。この記事のパート2では、その構成ファイルが必要になるためです。
データ構造
これで WidgetExample-Widget
という新しいフォルダーが表示されます。クリックしてファイル WidgetExample_Widget.swift
を開き、そのファイルのコンテンツを削除して、このガイドに従います。
ウィジェットに表示したいデータのデータ構造を作成できます。この例では、猫の情報を表示します!
struct CatEntry: TimelineEntry {
var date: Date
var name: String
var lastFed: Date
var lastPlayedWith: Date
}
IntentTimelineProvider
構造の作成
IntentTimelineProvider
構造は、次の3種類のコンテンツを提供します:
1> placeholder
(プレースホルダー)は、ウィジェットがロードされているときに表示されます。
2> getSnapshot
は、ウィジェットギャラリーに表示されます。
3> getTimeline
は、実際のウィジェット表示に使用されます。
まず、タイプ IntentTimelineProvider
に確認するプログラム構造体を作成し、次に Entry
のタイプを定義します。
struct CatProviderStatic: TimelineProvider {
typealias Entry = CatEntry
func getSnapshot(in context: Context, completion: @escaping (CatEntry) -> Void) {
//TODO
}
func getTimeline(in context: Context, completion: @escaping (Timeline<CatEntry>) -> Void) {
//TODO
}
func placeholder(in context: Context) -> CatEntry {
//TODO
}
}
getSnapshot
機能については、あなたのウィジェットが何の情報を提供するかをユーザーが理解できるよう、ウィジェットビューに例の値を提供することができます:
func getSnapshot(in context: Context, completion: @escaping (CatEntry) -> Void) {
let entry = CatEntry(date: Date(), name: "猫の名前", lastFed: Date(), lastPlayedWith: Date())
completion(entry)
}
プレースホルダービューについては、以下のように空の値または例の値を表示できます:
func placeholder(in context: Context) -> CatEntry {
return CatEntry(date: Date(), name: "猫の名前", lastFed: Date(), lastPlayedWith: Date())
}
そして、タイムライン表示については、表示する実際のコンテンツを提供できます。
この例では、ただ静的データ値を表示します。あなたのアプリケーションで、Core Data
(そのデータを共有する方法についての記事はこちら)、またはオンラインから、CloudKit
から、または UserDefaults
からのコンテンツをフェッチできます。
func getTimeline(in context: Context, completion: @escaping (Timeline<CatEntry>) -> Void) {
let entry = CatEntry(date: Date(), name: "ネコノヒー", lastFed: Date(), lastPlayedWith: Date())
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
タイムラインに複数のアイテムを追加する
タイムラインに複数のアイテムを追加することもできます。ウィジェットがタイムラインのアイテムを自動的にチェックし、アイテムに示された時刻になったらリロードを行います。
func getTimeline(in context: Context, completion: @escaping (Timeline<CatEntry>) -> Void) {
var timelineEntries = [CatEntry]()
if let date1 = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) {
let entry = CatEntry(date: date1, name: "ネコノヒー", lastFed: date1, lastPlayedWith: date1)
timelineEntries.append(entry)
}
if let date2 = Calendar.current.date(byAdding: .hour, value: 2, to: Date()) {
let entry = CatEntry(date: date2, name: "ネコノヒー", lastFed: date2, lastPlayedWith: date2)
timelineEntries.append(entry)
}
let timeline = Timeline(entries: timelineEntries, policy: .atEnd)
completion(timeline)
}
ウィジェットビューのデザイン
ウィジェットの SwiftUI ビューをデザインできるようになりました。
struct CatWidgetView: View {
@Environment(\.widgetFamily) var family
var entry: CatEntry
var body: some View {
VStack {
if family == .systemMedium || family == .systemLarge {
Image("kitty")
.resizable()
.frame(width: 50, height: 50)
.padding(.vertical, 5)
}
Text(entry.name)
.font(.headline)
.padding(1)
Text("最後に遊んだ時間 " + entry.lastPlayedWith.getString())
.font(.caption)
.padding(.horizontal)
Text("最後に餌をあげた時間 " + entry.lastFed.getString())
.font(.caption)
.padding(.horizontal)
}
}
}
ウィジェットのサイズを確認するには @Environment(\.widgetFamily) var family
変数を使用できます。
この例では、ウィジェットのサイズが画像の表示に十分な大きさである場合の猫の画像を表示しています。
if family == .systemMedium || family == .systemLarge {
Image("kitty")
.resizable()
.frame(width: 50, height: 50)
.padding(.vertical, 5)
}
ウィジェットアプリケーションをコーディングします
これで、ウィジェットアプリケーションをコーディングできるようになりました。
@main
struct CatWidget: Widget {
var body: some WidgetConfiguration {
IntentConfiguration(kind: "CatWidget", intent: ConfigurationIntent.self, provider: CatProvider()) { entry in
CatWidgetView(entry: entry)
}.configurationDisplayName("猫")
.description("いつ猫に餌をあげたり遊んだりしたか見てみましょう。")
}
}
これで、シミュレーターでアプリを実行して、設計したばかりのウィジェットを画面に追加できるようになりました。
ユーザーがウィジェットを構成できるようにします
お天気ウィジェットを使ったことがあれば、ウィジェットを長押しして構成することにより、表示される都市をユーザーが選択できることをご存じでしょう。Intents
フレームワークとTargetを使用して、この機能を追加することができます。
Intents
プログラムターゲットの追加
この段階では UI 要素は不要なため、オプション Include UI Extension
のチェックは外して結構です。
作成された Intents ターゲットページで、Supported Intents
という名前のセクションを見つけます。ConfigurationIntent
という名前のアイテムを新規作成します。名前は [インテント名]Intent
とします。インテント名は左側の .intentdefinition
ファイル内に表示されます。
.intentdefinition
の構成
次に、以前に作成したウィジェット内の WidgetExample_Widget.intentdefinition
ファイルにつき、新しく作成された Intents
拡張機能をインテントのターゲットとして追加します。
画面左側の Configuration
をクリックします。
右側で、以下の画像と構成が同じであることを確認してください。
次に、cat
という名の新しいパラメータを追加します。
新たに作成された cat
パラメータの設定画面で、猫の識別子を入力として使用するため、タイプ Type
に String
を選択します。
IntentHandler
の構成
次に、IntentHandler.swift
ファイルを開きます。このファイルでは、ユーザーがウィジェットの構成を行うオプションを提供します。この例では、オプションは猫の識別子となります。
クラスの INExtension
タイプの隣に ConfigurationIntentHandling
キーワードを追加すると、Xcode ソフトウェアが自動で次に追加すべき関数の名前を表示してくれます。
class IntentHandler: INExtension, ConfigurationIntentHandling {
...
}
この例では、完成した IntentHandler.swift
ファイルは以下のとおりとなります。
class IntentHandler: INExtension, ConfigurationIntentHandling {
func provideCatOptionsCollection(for intent: ConfigurationIntent, searchTerm: String?, with completion: @escaping (INObjectCollection<NSString>?, Error?) -> Void) {
let catIdentifiers: [NSString] = [
"ネコノヒー",
"ムギ",
"アズキ"
]
let allCatIdentifiers = INObjectCollection(items: catIdentifiers)
completion(allCatIdentifiers, nil)
}
override func handler(for intent: INIntent) -> Any {
// This is the default implementation. If you want different objects to handle different intents,
// you can override this and return the handler you want for that particular intent.
return self
}
}
関数 provideCatOptionsCollection
では、値のリストを入力する必要があります。これらの値は、実際には User Defaults
、Core Data
、オンラインのいずれかからフェッチできます。この例では値がハードコーディングされています。
「App Extensions」で「Core Data」を使用する
IntentTimelineProvider
を作成
本記事のパート1では、TimelineProvider
を使用しました。今回は、IntentTimelineProvider
を使用します。
IntentTimelineProvider
と TimelineProvider
の間のデータ構造はほぼ同一です。追加の typealias
を宣言する必要がある点が違いとなります。
typealias Intent = ConfigurationIntent
もう1つの違いは、次のとおりです。各関数で、インテントの選択を表す追加のパラメーター ConfigurationIntent
を取得します。
struct CatProvider: IntentTimelineProvider {
typealias Intent = ConfigurationIntent
typealias Entry = CatEntry
func placeholder(in context: Context) -> CatEntry {
let entry = CatEntry(date: Date(), name: "", lastFed: Date(), lastPlayedWith: Date())
return entry
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (CatEntry) -> Void) {
let entry = CatEntry(date: Date(), name: "猫の名前", lastFed: Date(), lastPlayedWith: Date())
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<CatEntry>) -> Void) {
let entry = CatEntry(date: Date(), name: configuration.cat ?? "", lastFed: Date(), lastPlayedWith: Date())
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
configuration.cat
プロパティを使用して、ユーザーが選択したオプションの値を読み取ることができます。
let entry = CatEntry(date: Date(), name: configuration.cat ?? "", lastFed: Date(), lastPlayedWith: Date())
CatWidget
のコードを更新します。
パート1では StaticConfiguration
を使用しましたが、このパートでは IntentConfiguration
( Supported Intents
で設定した名前)を使用します。
@main
struct CatWidget: Widget {
var body: some WidgetConfiguration {
IntentConfiguration(kind: "CatWidget", intent: ConfigurationIntent.self, provider: CatProvider()) { entry in
CatWidgetView(entry: entry)
}.configurationDisplayName("猫")
.description("いつ猫に餌をあげたり遊んだりしたか見てみましょう。")
}
}
シミュレーターでプログラムを実行できるようになりました。ウィジェットを長押しすると Edge Widget
というオプションが表示され、猫の名前を変更できます。
ウィジェットの再読み込み
ウィジェット内のコンテンツに変更が加えられた場合には、メインの iOS アプリケーションでウィジェットの再読み込み機能を手動でコールすることができます。
// import WidgetKit
WidgetCenter.shared.reloadAllTimelines()
例えば、ToDoアプリとToDo項目の数を表示するウィジェットを持っているとします。ユーザーがToDo項目を完了または追加したときに、ウィジェットをリロードすることができます。
「App Extensions」で「Core Data」を使用する