ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
WidgetKitのコンプリケーションによるさらなる進化
WidgetKitを使用して、文字盤の美しいコンプリケーションを作成する方法をご確認ください。WidgetKitに含まれるwatchOS固有の機能について紹介します。また、既存のClockKitのコンプリケーションからの移行を可能にする方法もお伝えします。 WidgetKitの詳細については、WWDC22の「コンプリケーションとウィジェット:リローデッド」をご確認ください。
リソース
- Creating accessory widgets and watch complications
- Emoji Rangers: Supporting Live Activities, interactivity, and animations
- Migrating ClockKit complications to WidgetKit
- WidgetKit
関連ビデオ
Tech Talks
WWDC22
WWDC20
-
ダウンロード
♪ ♪ こんにちは watchOSの ソフトウェアエンジニアの August Jokiです WidgetKitの コンプリケーションを 進化させる方法を紹介します 最初にWidgetKitの コンプリケーションの基本を カバーしているこちらの トークをご覧ください このセッションでは Watchの文字盤に表示される コンプリケーションの 概念について説明します 私のWWDC20のセッション 「SwiftUIでコンプリケーションを 構築する」でより具体的な色合いや SwiftUIの描画を解説しています 本セッションでは WidgetKitの watchOSならではの機能と ClockKitの コンプリケーションを WidgetKitに移行する方法 について説明します Coffee TrackerのサンプルApp からインスピレーションを得て このセッションで 例として使用します このAppは 一日に飲んだ コーヒー 紅茶 ソーダの数を 記録し 体内のカフェイン量を 経時的に確認します watchOSの特徴から 説明しましょう iOS 16では iPhoneのロック画面に コンプリケーションスタイルの ウィジェットを導入しました watchOS 9では Watchのコンプリケーションに WidgetKitを導入しました Watchの文字盤には 画面の角に表示される 独自のコンプリケーションが 搭載されています それを表現するには accessoryCornerという 独自のWidgetKit ファミリーが必要です 独自のプレゼンテーションの 一部は SwiftUIのView によって指定された 補助的なコンテンツなので コンテンツの一部として レンダリングされません 代わりにWatchの文字盤で レンダリングされます
円形部分はSwiftUIの 標準的なレンダリングで 補助コンテンツは 角の曲線部分です
あるいは インフォグラフの 文字盤の中にもあります
accessoryInlineファミリーは 文字盤で優れた動作をします 文字盤に応じて複数の レンダリング方法があります 時にはフラットに 時には文字盤に合わせた カーブを描きます
独自の機能のサポート方法を Coffee Tracker Appを WidgetKitを使用してアップデートし 確認していきましょう
iOS 16の新しい3つの ウィジェットファミリーの AccessoryRectangular accessoryCircular accessoryInlineに加え watchOS 9では accessoryCornerという ファミリーが追加されました
accessoryCornerは 下部の地図や心拍数のように 大きな円形コンテンツとして 表示されたり 上部のCoffe Trackerや月のように 小さな円形コンテンツと カーブしたラベルやゲージと ともに表示もできます
内側の補助コンテンツ表示を 制御するため watchOS 9に加わった 新しいビューモディファイア を紹介しましょう
コーナーコンプリケーション を見てみましょう
大きな円形のコンテンツ スタイルから始めて SF Symbolと背景を持つ ZStackを作成します SwiftUIのコンテンツは 自動的に円形に切り取られ コーナーコンプリケーション のデザインにあわせられます
内側のカーブしたコンテンツ 追加のため widgetLabel ビュー モディファイアを使用します 文字盤はモディファイアを 抽出し ファミリーと 文字盤のスタイルに適した コントロールを描画します 円形のコンテンツは自動的に 縮小しスペースを確保します accessoryCornerでは ウィジェットのラベルに SwiftUIのテキスト ゲージ progressViewを指定できます
accessoryCornerのみがwidgetLabel対象の 唯一のファミリーではありません accessoryCircularファミリーで 使用方法を見てみましょう インフォグラフの文字盤には 内側に4つの円形の コンプリケーション が搭載されています Coffee Trackerの円形の コンプリケーションは 中央上部にあり ダイアルに文字が 入っているのが特徴です これからそのテキストを 追加する方法を紹介します
円形のコンプリケーション デザインには コーナーコンプリケーション でwidgetLabelのゲージを 前面の中心へと移動させるのが 適切だと思いました インフォグラフの中央上部の 位置を利用するため ゲージにwidgetLabelを追加し 円形のコンテンツに収まらない 長いベゼル領域に テキストを表示します しかしメインビューと その上のテキストの間に 冗長な情報があります 円形のコンテンツを コーナーコンプリケーション からの格好いいコーヒーカップ SF Symbolに変えれば綺麗です しかし ベゼルのない 円形のコンプリケーションを 表示する文字盤に 切り替えると カフェイン情報が 消えてしまします 幸い どちらのケースでも コンプリケーションを 動作させることができます
コンプリケーションを更新し showsWidgetLabelという Environmentプロパティ を追加しました ウィジェットラベルの 内容が表示される文字盤上に コンプリケーションがある 場合は常にtrueになります
そしてshowsWidgetLabelの値に 対応するようコンテンツを 変更すると 適切な情報を 持たせることができます 先程accessoryCircularが 文字盤に表示される 2種類の方法を紹介しました もう1つ知っておくべきこと があります 特大文字盤は 長い間時刻を大きく表示する 素晴らしい方法として 利用されてきました 円形のコンプリケーションを サポートしています accessoryCircularファミリーを 使用し 特大文字盤のスタイルに マッチするようコンテンツを 自動的に拡大します ご注意ください このフェイスは単一の 大きなコンプリケーションを 搭載する設計ですが キャンバスサイズが 大きくなっても コンプリケーションに 要素を詰め込まないでください コンテンツはCircularファミリー と同じであるべきです 先ほどの通り文字盤のウィジェットには あと2つファミリーがあります accessoryRectangularと accessoryInlineです widgetLabelを表示する長方形の コンプリケーションの文字盤はありません accessoryInlineはすでに widgetLabelとして動作します インラインコンテンツから 画像やテキストを抽出し レンダリングして文字盤の スタイルに合わせます 次は マイグレーションについてです マイグレーションには 2つのパートがあります 既存のClockKitのコードを WidgetKitに書き換えること そしてマッピングを提供することで 文字盤にセットした 既存のコンプリケーションを アップグレードできます WidgetKitの採用すると 新しいコンテンツや文字盤編集のために ClockKitデータソースは システムから要求されなくなります
WidgetKitをWatchにもたらす と同時に watchOS 9では 文字盤がアップデートされ リッチコンプリケーションに 対応しました その結果 コンプリケーションを12種類 から4種類に 減らすことができました 長方形とコーナーは accessoryRectangularと accessoryCornerに 直接マッピングされます 3つのグラフィックCircular スタイルのClockKitファミリーは 1つのaccessoryCircular WidgetKitファミリーになりました utilitarianSmallFlatや utilitarianLargeの場所に accessoryInlineファミリー が使用されます
これまでutilitarianSmall だった多くの場所が accessoryCornerファミリー を使うよう更新されました
WidgetKitとSwiftUIビュー そしてステートドリブンデザインが ClockKitのテンプレート に取って代わりました WidgetKitにはよく似た タイムラインとエントリがあります これらはClockKitに触発 されたものです つまり コンプリケーション データソースは 静的または インテント型のWidgetKit 構成に上手く移行できます
WidgetKitが サポートする構成の種類や 一般的な サポートについては オリジナルのWidgetKitの セッションをご確認ください ClockKitに最後のAPIを追加し コンプリケーションを システムで自動的に 移行できるようにしました これにより 既存の文字盤に 搭載されている コンプリケーションを 自動的に新しいWidgetKit ベースのコンプリケーション にアップグレードできます AppがWatchで アップデートされると 文字盤は Appのバンドルに ウィジェットがあるかどうか チェックします 見つかれば ClockKitの データソースを起動し 既存のコンプリケーションの マイグレーションを生成します この時点から CLKComplicationDataSourceは ClockKitコンプリケーション が入った共有の文字盤を 受けるとマイグレーションを 要求するため実行されます 新しい文字盤が共有されるたび マイグレーションを要求します 一貫した経験を提供するため マイグレーションを保つのです 美しいWidgetKitの コンプリケーションの作成を 終えたら 新しいプロパティの widgetMigratorを追加し 新しいMigratorプロトコルに準拠した オブジェクトを提供できます コンプリケーションデータ ソースのままか他のタイプか
CLKComplication WidgetMigratorプロトコルは CLKComplicationDescriptors から文字盤ウィジェットへ マイグレーション設定を提供 する単一関数を備えています 新しいAPIを採用する最も 簡単な方法は データソース を新しいMigratorプロトコル に準拠させることです
WidgetKitコンプリケーション が静的な設定を使用する場合 静的なマイグレーション設定 を提供します ウィジェット コンプリケーションで インテント使用する場合は相当する マイグレーション設定があります インテントに基づく マイグレーション設定を提供 する場合はWidget Extention だけてなく Watch Appにも インテント定義を含める必要 があることにご注意ください これで両方の場所でインテント オブジェクトを作成できます WidgetKitはWatchの クリエイティブな コンプリケーション作成を可能にし 体験を劇的に簡素化します ご視聴 ありがとうございました
-
-
3:06 - Large Corner
struct CornerView: View { let value: Double var body: some View { ZStack { AccessoryWidgetBackground() Image(systemName: "cup.and.saucer.fill") .font(.title.bold()) .widgetAccentable() } } }
-
3:27 - Corner with Gauge
struct CornerView: View { let value: Double var body: some View { ZStack { AccessoryWidgetBackground() Image(systemName: "cup.and.saucer.fill") .font(.title.bold()) .widgetAccentable() } .widgetLabel { Gauge(value: value, in: 0...500) { Text("MG") } currentValueLabel: { Text("\(Int(value))") } minimumValueLabel: { Text("0") } maximumValueLabel: { Text("500") } } } }
-
4:24 - Circular Gauge
struct CircularView: View { let value: Double var body: some View { Gauge(value: value, in: 0...500) { Text("MG") } currentValueLabel: { Text("\(Int(value))") } .gaugeStyle(.circular) } }
-
4:34 - Circular Gauge with Widget Label
struct CircularView: View { let value: Double var body: some View { let mg = value.inMG() Gauge(value: value, in: 0...500) { Text("MG") } currentValueLabel: { Text("\(Int(value))") } .gaugeStyle(.circular) .widgetLabel { Text("\(mg, formatter: mgFormatter) Caffeine") } } var mgFormatter: Formatter { let formatter = MeasurementFormatter() formatter.unitOptions = [.providedUnit] return formatter } } extension Double { func inMG() -> Measurement<UnitMass> { Measurement<UnitMass>(value: self, unit: .milligrams) } }
-
4:51 - Circular Stack with Widget Label
struct CircularView: View { let value: Double var body: some View { let mg = value.inMG() ZStack { AccessoryWidgetBackground() Image(systemName: "cup.and.saucer.fill") .font(.title.bold()) .widgetAccentable() } .widgetLabel { Text("\(mg, formatter: mgFormatter) Caffeine") } } var mgFormatter: Formatter { let formatter = MeasurementFormatter() formatter.unitOptions = [.providedUnit] return formatter } } extension Double { func inMG() -> Measurement<UnitMass> { Measurement<UnitMass>(value: self, unit: .milligrams) } }
-
5:12 - Circular Stack or Gauge
struct CircularView: View { let value: Double @Environment(\.showsWidgetLabel) var showsWidgetLabel var body: some View { let mg = value.inMG() if showsWidgetLabel { ZStack { AccessoryWidgetBackground() Image(systemName: "cup.and.saucer.fill") .font(.title.bold()) .widgetAccentable() } .widgetLabel { Text("\(mg, formatter: mgFormatter) Caffeine") } } else { Gauge(value: value, in: 0...500) { Text("MG") } currentValueLabel: { Text("\(Int(value))") } .gaugeStyle(.circular) } } var mgFormatter: Formatter { let formatter = MeasurementFormatter() formatter.unitOptions = [.providedUnit] return formatter } } extension Double { func inMG() -> Measurement<UnitMass> { Measurement<UnitMass>(value: self, unit: .milligrams) } }
-
9:47 - Widget Migrator
var widgetMigrator: CLKComplicationWidgetMigrator { self }
-
9:56 - Static Migration Configuration
func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? { CLKComplicationStaticWidgetMigrationConfiguration(kind: "CoffeeTracker", extensionBundleIdentifier: widgetBundle) }
-
10:03 - Intent Migration Configuration
func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? { CLKComplicationIntentWidgetMigrationConfiguration(kind: "CoffeeTracker", extensionBundleIdentifier: widgetBundle, intent: intent, localizedDisplayName: "Coffee Tracker") }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。