【iOS 17】Widgetにインタラクティブな機能とアニメーション効果を追加する
iOS 17では、Widgetに対してインタラクティブな機能と、アニメーション効果を追加することができるようになりました。
本記事ではiOS 17でWidgetに追加されたインタラクティブ機能と、アニメーション効果の実現方法について書きます。
アニメーション効果の追加
SwiftUIを使用して作られる通常のアプリでは、状態を表す変数(@State
や@Binding
がついた変数)の変更が影響するViewに対して、withAnimation(_:_:)
を付加することでアニメーションを実現することができます。
しかし、Widgetは状態を持つことができません。その代わり、特定の時間にレンダリングされるViewに対して提供する、エントリのタイムラインを作成します。特定の時間にタイムラインから提供されるエントリを元に作成したViewをWidget上に表示することで、Widgetの表示を更新しています。
Xcode 15では、プロジェクトを再ビルドするだけで、エントリ間で変更があったコンテンツを含むViewを、WidgetKitが自動で特定してくれるようになります。
特定したViewに対して、WidgetKitがアニメーション効果を付与することで、コンテンツの変化がアニメーションで表現されるようになります。
以下の画像はWidget内のText
の内容に変更があった時に、デフォルトで付与されるアニメーション効果です。
contentTransition(_:)
contentTransition(_:)
を使用することで、コンテンツ変更時のトランジションタイプを変更することができます。
-
.numericText()
-
.opacity
-
.interpolate
-
.symbolEffect
animation(_:value:)
animation(_:value:)
を使用することで、トランジションにアニメーション効果を付与することもできます。
transition(_:)
変更が入ったコンテンツを含むViewだけではなく、特定のViewのまとまりに対してトランジション効果を付与することもできます。
トランジション効果を付与したいViewにid(_:)
を指定します。id(_:)
の引数には変更を監視したい値を指定します。そして、transition(_:)
でトランジションタイプを指定することで、複数のViewに対してトランジション効果を付与することができます。サンプルコードでは、entry.count
に変更が入ると、VStack
で囲んだView全体に対して、transition(_:)
で指定したトランジション効果が実行されます。
インタラクティブ性の実現
Button
とToggle
を使用することで、Widgetにインタラクティブ性を持たせることができます。
処理の流れ
Widgetは、以下の1 ~ 4を繰り返すことでインタラクティブ性を実現することができます。
- Widget上の
Button
またはToggle
をタップ -
Button
またはToggle
初期化時に指定したIntent
を実行 - タイムラインのリロードを開始
- Widgetに表示しているViewを更新
ButtonとToggleに追加された新しいイニシャライザ
Button
とToggle
に新しいイニシャライザが追加されています。
Button
またはToggle
がタップされた時に実行するIntent
を初期化時に指定します。
App Intent
AppIntent
プロトコルに準拠した構造体を定義することで、Intent
を作成することができます。AppIntent
プロトコルに準拠するためにはtitle
とperform()
を定義する必要があります。
perform()
メソッド内にButton
またはToggle
がタップされた時に実行したい処理を記述します。
サンプルアプリの実装
今回はカウンター機能を持ったWidgetを作成します。
Counterモデルクラス
数値をインクリメント、デクリメント、現在の数値を取得するメソッドを定義したモデルクラスを準備します。
Intent
IncrementIntent
では数値をインクリメントする処理を、DecrementIntent
では数値をデクリメントする処理を、各perform()
メソッド内に記述しています。
Widget
Button
の引数にボタンがタップされた時に実行したいIntent
を指定しています。Intent
が実行されるたびに、カウンターの数値がインクリメント/デクリメントされます。
そして、タイムラインがリロードされ、タイムラインはカウンターの数値を元に新しいエントリを作成します。
カウンターの数値を含んだエントリを元に、Viewがレンダリングされることで、表示される数値が更新されるような動きになります。
実行結果
補足
「CounterWidget」という名称でWidgetエクステンションをプロジェクトに追加した後に、以下のコードを「CounterWidget.swift」にコピペしていただくと、カウンター機能を持ったWidgetを簡単に作成することができます。
参考資料
・Bring widgets to life
Discussion