WWDC Quick Look 💓 By SwiftGGTeam
Go further with Complications in WidgetKit

Go further with Complications in WidgetKit

元の動画を見る

ハイライト

watchOS 9 では、時計のコンプリケーションに WidgetKit が導入され、iOS 16 ロック画面ウィジェットと同じ API ベースを共有します。しかし、時計にはいくつかの独自の要件があります。文字盤の角 (accessoryCorner) の位置には、円形の内容と円弧状の補助ラベルの両方を表示する必要があります。円形コンプリケーションには、一部の文字盤 (インフォグラフ ダイヤルなど) にテキストを表示するための余分なベゼル スペースがあります。

主要内容

Apple Watch のコンプリケーションを行う際の難しさはデータだけではありません。文字盤上のスペースは非常に小さく、同じ内容がコーナー、ベゼル、円形スロット、特大文字盤に表示されます。また、古い ClockKit では、開発者が多くのテンプレートやファミリーを扱う必要があったため、移行にコストがかかりました。

watchOS 9 の変更点は、ウォッチフェイスのコンプリケーションを WidgetKit に接続することです。開発者は引き続き SwiftUI ビュー、タイムライン、構成を作成します。このシステムは、コンテンツをさまざまなウォッチフェイスの場所に配置し、古い ClockKit Complication が新しい WidgetKit バージョンに自動的にアップグレードできるようにする役割を果たします。

このセッションでは、watchOS のユニークな部分についてのみ説明します。重要なポイントは次の 3 つです。accessoryCornerアーク補助コンテンツ、accessoryCircularInfograph ウォッチフェイスの追加タブ、およびCLKComplicationWidgetMigrator移行マッピング。

Coffee Tracker の例が随所に登場します。その日のコーヒー、紅茶、炭酸飲料の摂取量を記録し、体内のカフェインを追跡します。この例はコンプリケーションで機能します。ユーザーはダイヤルを見て、現在のカフェイン レベルを知ることができます。

詳細

アクセサリコーナー: 円形の本体と円弧の補助コンテンツ

03:06accessoryCornerwatchOS 9でウォッチフェイスのコーナーに追加されたWidgetKitファミリーです。円形の本体は通常のSwiftUIに従って描画され、補助的なコンテンツは文字盤によって描画されます。まずは本体のみのバージョンから見ていきましょう。

struct CornerView: View {
    let value: Double

    var body: some View {

        ZStack {
            AccessoryWidgetBackground()
            Image(systemName: "cup.and.saucer.fill")
                .font(.title.bold())
                .widgetAccentable()
        }

    }
}

キーポイント:

  • CornerViewは SwiftUI ビューであり、入力値はvalue、これは後でゲージに使用されます。 -ZStack背景とアイコンを重ねて、角のコンプリケーションの丸い本体を形成します。 -AccessoryWidgetBackground()システムが認識するアクセサリ コンテキストを提供します。 -Image(systemName:)コーヒーカップのアイコンを表示するにはSFシンボルを使用します。 -.widgetAccentable()アイコンを WidgetKit のアクセント カラー レンダリングに参加させます。

(03:27) 円弧状のコンテンツを追加するために、watchOS 9 は以下を提供します。widgetLabel。存在するaccessoryCornerここで、ラベルは次のようになりますTextGaugeまたはProgressView。ウォッチフェイスはラベルの内容を取り出し、それを曲線ラベルまたはコーナーの進行状況として描画します。

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")
            }
        }


    }
}

キーポイント:

  • .widgetLabelコンテンツを円の本体に描画する代わりに、補助的なコンテンツをダイヤルに与えます。 -Gauge(value:in:)カフェイン値をマッピングする0...500範囲。 -Text("MG")ゲージ単位のタグです。 -currentValueLabel現在の整数値を表示します。 -minimumValueLabelそしてmaximumValueLabelウォッチフェイスの範囲のエンドポイントを提供します。
  • ラベルが表示されると、円弧の内容のためのスペースを残すために、円形ボディが自動的に縮小されます。

AccessoriesCircular: showsWidgetLabel を使用して、ベゼルの有無に応じて位置を調整します

04:24accessoryCircularゲージも表示可能。通常の円形スロットには追加のベゼル テキストはなく、コア情報を本体に配置する必要があります。

struct CircularView: View {
    let value: Double

    var body: some View {

        Gauge(value: value,
              in: 0...500) {
            Text("MG")
        } currentValueLabel: {
            Text("\(Int(value))")
        }
        .gaugeStyle(.circular)

    }
}

キーポイント:

  • CircularView同じものを使うvalueカフェイン値を表します。 -Gaugeまだ使用中0...500範囲。 -currentValueLabel現在の値をゲージに入力します。 -.gaugeStyle(.circular)ゲージをコンプリケーションに適した円形のスタイルにレンダリングします。

(04:34) Infograph ダイヤルの円形コンプリケーションには、余分なベゼル スペースがあります。ここに長いテキストを入力できますwidgetLabel

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)
    }
}

キーポイント:

  • let mg = value.inMG()数値をラップするMeasurement<UnitMass>
  • 本体には円形のゲージで現在値が表示されます。 -.widgetLabelに追加"Caffeine"インフォグラフのベゼル位置に使用されるテキスト。 -MeasurementFormatterシステムが他の質量単位に変更されないように、受信単位を保持します。 -inMG()明らかにDoubleミリグラムに換算します。

(05:12) 問題は、同じことですaccessoryCircularはさまざまなダイヤル位置に表示されます。ウィジェットのラベルが表示されるものと、表示されないものがあります。 watchOS 9 が提供するのはshowsWidgetLabelビューが現在の場所に基づいてコンテンツ密度を選択できるようにする環境値。

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)
    }
}

キーポイント:

  • @Environment(\.showsWidgetLabel)ウィジェットのラベルが現在の場所に表示されるかどうかを読み取ります。 -showsWidgetLabel == trueこのとき、本体にはコーヒーカップのアイコンのみが配置され、ラベルには数字のテキストが配置されます。 -showsWidgetLabel == false、ユーザーがカフェイン情報を失うことを防ぐために、本体は円形のゲージに切り替わります。
  • 両方のブランチは同じビューからのものであるため、2 つのウィジェットに分割する必要はありません。 -mgFormatterそしてinMG()ラベルのコピーでは引き続きミリグラム単位が使用されるようにしてください。

(05:47) 特大ダイヤルも使用accessoryCircular。システムは円形コンテンツを自動的に拡大します。 Apple のアドバイスは明確です。キャンバスが大きくなったからといって、より多くの情報をキャンバスに詰め込まず、コンテンツ階層を通常の循環コンプリケーションと一貫性を保つようにしてください。

ClockKit から WidgetKit への移行

(06:45) 移行は 2 段階のプロセスです。最初のステップは、古い ClockKit テンプレートを WidgetKit の SwiftUI ビューに書き直すことです。 watchOS 9 では、古い 12 の ClockKit ファミリが 4 つの WidgetKit ファミリに統合されています。accessoryRectangularaccessoryCircularaccessoryCorneraccessoryInline

(08:25) 2 番目のステップは、ユーザーがダイヤルに設定した古いコンプリケーションをアップグレードする方法をシステムに指示することです。この目的のために、ClockKit は追加しましたCLKComplicationWidgetMigrator。アプリが更新されると、ウォッチフェイスはアプリバンドル内のウィジェットを確認し、古いウィジェットを呼び出します。CLKComplicationDataSource移行構成を生成します。

(09:47) 最も直接的な方法は、既存のデータ ソースに独自の移行ツールを提供させることです。

var widgetMigrator: CLKComplicationWidgetMigrator {
    self
}

キーポイント:

  • widgetMigratorこれは、ClockKit データ ソースの新しい移行エントリです。
  • 戻るself現在のデータ ソースが移行プロトコル自体を実装していることを示します。
  • システムはこのオブジェクトを使用して、古いコンプリケーション記述子を新しい WidgetKit 構成にマップします。

(09:56) 新しい WidgetKit コンプリケーションが静的構成の場合は、静的移行構成を返します。

func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? {
    CLKComplicationStaticWidgetMigrationConfiguration(kind: "CoffeeTracker", extensionBundleIdentifier: widgetBundle)
}

キーポイント:

  • widgetConfiguration(from:)古いものを受け取るCLKComplicationDescriptor
  • 戻り値はオプションですCLKComplicationWidgetMigrationConfiguration
  • CLKComplicationStaticWidgetMigrationConfiguration静的な WidgetKit 構成に使用されます。 -kind: "CoffeeTracker"新しいウィジェットの種類を指します。 -extensionBundleIdentifierウィジェットを含む拡張バンドルを指します。

(10:03) 新しいコンプリケーションがインテント設定を使用する場合は、インテント移行設定を返します。 Apple は、インテント オブジェクトを両方の場所に作成できるように、ウォッチ アプリとウィジェット拡張機能の両方にインテント定義を含める必要があることを特に注意しています。

func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? {
    CLKComplicationIntentWidgetMigrationConfiguration(kind: "CoffeeTracker", extensionBundleIdentifier: widgetBundle, intent: intent, localizedDisplayName: "Coffee Tracker")
}

キーポイント:

  • CLKComplicationIntentWidgetMigrationConfigurationインテントベースの WidgetKit コンプリケーションに対応します。 -kindそしてextensionBundleIdentifierまだ新しいウィジェットを配置しています。 -intent古いコンプリケーションに対応する設定ステータスを保存します。 -localizedDisplayName移行された構成の表示名を指定します。
  • インテント定義が欠落している場合、ウォッチ アプリまたは拡張機能は移行に必要なインテント オブジェクトを作成できない可能性があります。

重要ポイント

  • カフェイン ダイヤル ステータスを作成: 使用しますaccessoryCircularInfograph ベゼルに渡された現在のカフェイン レベルを表示しますwidgetLabel「カフェイン」とミリグラムを表示します。入り口は、Gauge.gaugeStyle(.circular)そして.widgetLabel

  • 飲酒または立食コーナーを作成する 合併症: を使用しますaccessoryCornerアイコンを円形の本体に配置します。Gauge入れますwidgetLabel。ユーザーはアイコンと隅の曲線の進行状況を確認できます。

  • 同じビューでマルチダイヤル調整を行う: 循環コンプリケーションで読み取る@Environment(\.showsWidgetLabel)。ベゼルがある場合は本体にアイコンを配置し、ベゼルがない場合は本体にゲージを表示することで情報の欠落を防ぎます。

  • ClockKit から WidgetKit へのスムーズな移行を行う: まず古いテンプレートを SwiftUI ビューに変更してから実装しますCLKComplicationWidgetMigrator。ユーザーがアプリを更新すると、元々ウォッチフェイスに配置されていた古いコンプリケーションが新しい WidgetKit バージョンにアップグレードされます。

  • 特大の制限されたバージョンを作成: 通常のを再利用しますaccessoryCircularコンテンツをシステムに増幅させます。特大キャンバスに余分なレイヤーを追加せず、フォント サイズ、シンボル、配置のみを最適化します。

関連セッション

コメント

GitHub Issues · utterances