ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
空間的なSharePlay体験の構築
GroupActivitiesフレームワークを使って、visionOSのための独自の共有コラボレーション体験を作成する方法を見ていきます。このプラットフォームにおけるSharePlayを紹介し、まるで同じ空間にいるような存在感を人々に与える体験をどのように作成するかを学びます。その上で、イマーシブなアプリが参加者間での共有コンテンツを体験する方法についてお話しします。
リソース
関連ビデオ
WWDC23
WWDC21
WWDC19
-
ダウンロード
♪♪ ♪ Willem Mattelaer:こんにちは Willemです Group Activitiesを手掛ける エンジニアです Miaと一緒にお話しするのは 素晴らしい空間的なSharePlayアプリの 構築方法についてです ほかの人が近くにいても遠くにいても 一緒にいて何か行えるというのが SharePlay体験です このプラットフォームでは FaceTime中に同じ場所にいるという 感覚が強くなります このプラットフォームでのFaceTimeでは 空間Persona全員が 一つのウィンドウに縛られるのではなく 部屋の中の物理的空間を占めるのです ほかの人達と一緒にいるという 感覚が増してきます それに加えて 共有コンテキストを導入しています 共有コンテキストとは 全員にとって ほかの全員が同じ相対位置に いるように見えるという事です この説明に 2つの異なる視点を 考えていきます 左側が表すのはConnorの視点で 右側はMiaの視点です ConnorがMiaのPersonaを指し示せば MiaはConnorのPersonaが 自分を指し示したと分かります これを共有体験にも拡張し その中にSharePlayも含まれますが SharePlay内では 全員にとって相対的な同じ位置に 窓が置かれるのです それにはテンプレートを使っています テンプレートは 参加者や 共有アプリがお互いに対して どう相対的に位置づけられるかを 決めるために使われます それによって 空間的な一貫性が生まれます アプリは自ら置き換えを行う必要はなく それをシステムに任せる事ができます アプリが懸念すべき唯一の事は 視覚的一貫性を持つ事です 視覚的一貫性というのは 全員が アプリの中で同じコンテンツを 見ているという事です 例えば ホワイトボードの前に ほかの人達と立っているとします 全員が同じホワイトボードの ビューを持っています 誰かが何かを指し示せば ほかの人は全員 その人が 何を指しているか分かります 私たちがSharePlayの 空間体験で再現したいのはこれです 先ほどの例に戻りますが ここではアプリに 視覚的一貫性がありません アプリでConnorが四角を指し示すと MiaにはConnorのPersonaが 丸を指していると見えています これでは一緒にいるという 感覚が損なわれます ですがアプリに視覚的一貫性があれば MiaはConnorの指しているのが 四角だとはっきり分かります コンテンツとその配置を 全参加者に対して 同期するのはアプリの責任です そうすれば 全員が本当に 一緒にいるかのように 完全に同じアプリを 見ている感覚になります 「Design spatial SharePlay experiences」の セッションで これらの概念についての詳細が出ています 是非 そのセッションを見て 詳しい内容を学んでください このプラットフォームで FaceTimeに導入された 新しいコンセプトを いくつか見たところで SharePlayを採用する時に ウィンドウアプリが考えるべき事と 既存のiOS SharePlayアプリが 空間設定で使われた時に 体験向上のために取るべき 追加のステップについて見ていきましょう のちほどMiaが 驚くような没入感の SharePlay体験を作成するための 新しいAPIをお見せします ウィンドウアプリでSharePlayを サポートするために グループセッション用の新しい システムコーディネータを追加しました システムコーディネータは 2つの事を担当します アクティブなSharePlayセッションの システム状態を受け取る事と SharePlay中 みなさんに 追加の設定提供をさせる事です 私がカバーする2つは ウィンドウアプリにも没入型アプリにも 関連している事で isSpatialフラグと テンプレートの設定についてです 要求される視覚的一貫性の 水準を決める時には 参加者が空間的であるかどうかを 知る事が重要です 例えば こちらはConnor本人の視点です 赤のMiaと通話中で 私がSharePlayで共有した ドキュメントを見ています このシナリオでのMiaの視点は右側で 青のConnorが見えています Connorがスクロールすれば スクロール位置を同期したいわけです そうすれば MiaはConnorが スクロールするのが見えて 視覚的一貫性も保たれます 実際 これはSharePlay中にフリーボードで 平行移動している時に行なっています ですが 空間的でない参加者は ドキュメントを操作している 相手のコンテキストがありません なので Connorがスクロールしても それを同期すべきではなく Miaはドキュメントの同じ場所を 見続ける事になります ローカルの参加者が空間的かどうかを調べ 参加者間で何を同期するか調整するためには まずはグループセッション用の システムコーディネータが必要です ローカルの参加者が空間的であるか知るには ローカルの参加者状態で isSpatialがtrueであるかチェックします 状態を調べるために 使えるlocalParticipantStateプロパティは ローカルの参加者状態の 更新を通してイテレートする 非同期のシーケンスを戻します システムコーディネータはアプリに テンプレートの設定を 提供する方法も提示します テンプレートは空間的一貫性を 確立するために アプリに関連するシステムが 参加者全員をどう配置するのか 決めるために使います 私たちがサポートするのは 3つの異なるテンプレートです Side-by-Sideでは 参加者全員が アーチ状に並んで 共有アプリの方を向いています これはデフォルトのテンプレートで アプリが通常のウィンドウシーンを 使っている場合に適用されます Conversationalテンプレートでは 参加者全員が半円に並んで アプリはその半円の前に置かれます これは アプリのコンテンツに フォーカスが無い場合にピッタリです 例えば SharePlayで音楽の 演奏をしている場合ですね 最後は Surroundテンプレートです この場合 参加者は円になっていて アプリはその円の中心に置かれます このテンプレートが使えるのは アプリがボリュメトリックシーンを 使っている時だけです 参加者とアプリの距離は アプリのサイズに基づいて決まります アプリが大きければ 人々から遠く離れて配置されます アプリが小さければ 近い位置に置かれます SharePlayアプリは システムコーディネータを通して テンプレート設定を提供します FaceTimeはグループセッションが アクティブな間に このテンプレートを適用しようとします SharePlayアプリには 3つの設定が利用できます .noneは システムの デフォルトビヘイビアに従い 縦型アプリでは Side-by-Sideに ボリュメトリックアプリでは Surroundになります これが使用されるデフォルト設定です また .sideBySideだと アプリの種類に関係なく Side-by-Sideテンプレートを 採用しようとします 最後は .conversationalで 名前に対応するテンプレートを 採用しようとします テンプレート設定を構成するには システムコーディネータの設定で グループセッションの参加が始まる前に 設定すればいいのです テンプレートはアプリや参加者を 最適の位置に置くために使われます ですが アプリに複数の ウィンドウシーンがあって 一度に複数が前景化されている場合は どうなるでしょう? この例では アプリに 3つのシーンがあります 左のシーンはブラウズ用のビューです 真ん中の小さいシーンはコンテンツを 簡単にナビゲートするためのものです 最後のシーンは1つのコンテンツについての 詳細情報を提供するものです このすべてのシーンは同時に開けられるので 読みながらコンテンツを 簡単にナビゲートできます SharePlayでは 詳細情報のシーンを 共有シーンにしたいわけです ですが 全部が開いていると 誤ったシーンがテンプレートに 使われるかもしれません それを解決するために シーンの関連付けを追加しました これでシステムはアプリのどのシーンが SharePlayアクティビティを ホストしているのか分かります どのシーンであるか知るのには 2つの理由があります まず ウィンドウの上にあるShareメニューに どのシーンが共有されているのか 表示されるようになるからです 複数のウィンドウが開いている時には 便利なインジケータがあれば うっかり誰かが共有ウィンドウと インタラクトするような恥ずかしい状況を 回避しやすくなります ですが もっと重要なのは テンプレートでどのウィンドウシーンが 使われるのか決定される事です アプリが単一シーンのものであれば 心配する必要はありません 使えるシーンは1つしかないので 自動的にそのシーンがグループセッションに 関連付けされます ですが 複数シーンを持つアプリは SharePlayを採用する時に 関連付けを考慮すべきです 関連付けが採用されていないと 開いているシーンから無作為に選ばれ 不適切なシーンが選ばれるかもしれません では シーンの関連付けの 採用方法を見ていきましょう まずは SharePlayアクティビティを 開始した人にとってどうなるでしょうか グループアクティビティの起動時に 各シーンをチェックして どのシーンが関連付けを 処理できるか確認します グループアクティビティの アクティビティ識別子に対して シーンのアクティブ化条件を 評価するという方法を取ります シーンのアクティブ化条件には 2つのチェック項目があり それは canとprefersです canは そのシーンが関連付け処理の 可能性がある事を意味し prefersはもっと関連付けに ふさわしいシーンへと促します それでは 左側のシーンをチェックして アクティビティ識別子を処理できるか ほかに促すか見て見ましょう CanですがPrefersではありません 次は 真ん中のシーンで 両方ダメです そして最後のシーンを見ると CanもPrefersも大丈夫です 最後のシーンがCanもPrefersも 大丈夫だったので グループセッションにはこれを 関連づける事にします 識別子を処理できるシーンが無ければ 新しいウィンドウシーンを起動します SharePlay体験に 独自の作業シーンが必要な場合は ピッタリです 複数のシーンがCanであり Preferがなければ そのうちの1つのシーンが 無作為に選ばれます グループアクティビティを 有効化せず 受けているだけの 参加者にとっては アプリが既に実行されているなら 同じ論理が適用されます まだアプリが実行されていなければ 起動後の 最初のシーンが 関連付けられます SwiftUIアプリLifecycleを 使っているアプリの シーンアクティブ化条件を特定するには handlesExternalEventsが使えます グループアクティビティの アクティビティ識別子を preferringかallowingの 文字列のセットに入れるだけです アプリが UIKitアプリlife cycleを 使っている場合は プレディケートを特定する事で UIシーンで アクティブ化条件が設定できます シーンのアクティブ化条件について 詳細を学びたい場合は 2019年のセッションを見てください もう1つの例を見ていきましょう ここでは ドキュメントベースのアプリで 各シーンは異なるドキュメントを 表示しています 今からSharePlayを使って 最初のドキュメントでの コラボレーションをしようと思います ここでもグループアクティビティ識別子で 各シーンのアクティブ化条件を 評価していきます 残念ながら これらのシーンには 本質的な違いが無く 全部マッチしているため 誤ったシーンが関連付けられてしまいます この場合は シーンを マッチさせるために使われる 識別子を変えましょう そのために シーンを関連付ける ビヘイビアを追加しました これでアプリは 正しいシーンを 探すのに使われる 識別子が提供できるようになります 例に戻ると 参加者全員が納得するような 各ドキュメントに特有の識別子を 使う事ができます その識別子を使って 正しいシーンを見つけます 今 シーンを評価すると シーンのアクティブ化条件は 簡単に設定できるので 最初のシーンだけがマッチし 正しいシーンの関連付けができました シーンの関連付けビヘイビアの設定で 使われる識別子の特定は グループアクティビティの メタデータで行えます SharePlayセッションに参加する 参加者全員に同じ識別子が 使われる事を忘れずに ですから全員が納得するような ものを使いましょう シーンの関連付けビヘイビアでは 3つのタイプをサポートしています Defaultが最初に見たビヘイビアで シーンの関連付けに使われた識別子が グループアクティビティ識別子になります 名前の通り これが デフォルトのビヘイビアで 明確に特定しなければ これが使われます Contentビヘイビアは カスタムの 識別子を特定させてくれます 各シーンが異なるコンテンツを示し グループアクティビティが そのコンテンツに結びついている アプリのためのビヘイビアです 最後は Noneビヘイビアです Noneビヘイビアはシーンの 関連付けを無効にします つまり 関連付けされるシーンが 無くなるので アプリのどのシーンの上にも 共有バナーが出ないという事です また これは空間的一貫性が 壊れている事も意味します これは特別なケースでのみ使われるべきで 例えば 追加のシーンが不要な 没入型アプリの場合や SharePlayセッションで 表示されるコンテンツが 各参加者のそれぞれのシーンで 異なるような場合です 時には グループセッションに どのシーンが関連付けられているのか 知る必要があります 複数のシーンがシーン関連付けに 選ばれている可能性がある場合です この情報を得るために グループセッションで 公開した新しいプロパティが sceneSessionIdentifierです このプロパティには グループセッションに関連づけられた シーンセッションの識別子が 含まれています 素晴らしいSharePlay体験の構築には シーンの関連付けは欠かせませんので 複数シーンをサポートするアプリには ぜひ 適用してください ではようやく Shareメニューからの SharePlayについてです FaceTimeでは 各ウィンドウの上に 共有バナーがあります 先ほど見たように SharePlay中に どのシーンが共有されているか示します ですが SharePlay中でない時も役立ちます シーンが共有されていない時 シーンを共有できるいろいろな方法を 示してくれます 1つは単純にウィンドウの共有です ほかの人は全員そのウィンドウの 非インタラクティブな ビデオフィードが見えます どのアプリでも 最初から使えます ですが SharePlayアプリなら さらに可能性が広がります ウィンドウの現時点でのコンテンツに 関連のあるグループアクティビティを 公開する事で そのメニューの表面に出てきます SharePlayアクティビティの 発見可能性を向上できる上に AppのUIに専用のSharePlayボタンを 付ける必要もなくなります グループアクティビティの公開は AirDropでSharePlayを 始めるのと同じ方法で行います iOS 17では SharePlayアプリを 開いておく事で AirDropでSharePlayが 始められるようになります グループアクティビティをフェッチするのに システムは表示されているシーンの UIレスポンダチェーン内を探し そのうちの一つのレスポンダの アクティビティアイテム設定で 特定されているグループアクティビティを 見つけようとします そうすると SharePlayコンテンツを 表示しているビューコントローラの アクティビティアイテム設定で グループアクティビティを設定できて それが自動的にピックアップされます アクティビティアイテム設定を行うには まず 有効化できるアクティビティを 作ることから始めます 次に アイテムプロバイダを作成して そこに グループアクティビティを 登録します それからアイテムプロバイダで UIActivityItemsConfigurationを 初期化します 最後は 設定が公開しているのが 正しいメタデータである事を 確認しましょう それがShareメニューで表示されるからです そのためには metadataProviderを UIActivityItemsConfigurationで使い LinkPresentationMetadataキーのために LPLinkMetadataオブジェクトを提供します Shareメニューにはtitleと imageProviderが使われます UIActivityItemsConfigurationReadingに 準拠する自分のクラスを使っても すべてこの通りに作業できます このプラットフォームでウィンドウの SharePlay体験を構築するために 考慮しなければならない事項を 話してきました Mia 没入型アプリの共有体験の 構築方法を説明してくれますか? Mia Ren:もちろんです Willem では 没入型アプリでSharePlayが どうなるか見てみましょう このプラットフォームでは Immersive Spaceを使って 簡単に没入型体験が作成できます Immersive Spaceはアプリのコンテンツが 異なる没入型スタイルで 境界線を越えていく特別なシーンです アプリがImmersive Spaceを開くと システムはほかのアプリを すべて背景化するので ユーザーは限りのない世界で このアプリだけに集中できるのです 下に出ているセッションから Immersive Spaceや Immersion Stylesの詳細が分かります FaceTime中いつでも Immersive Spaceが起動できます プライベートな没入型空間にいる時は 自分だけの世界にいるのであり ほかの人たちとの共有コンテキストを 解除できます そのために システムはほかの人々を 隠してくれます ほかの人々が目にするのは連絡先の写真で 今はその人たちと 一緒にいない事を示しています それでも音声での通話は誰でもできます でもほら FaceTimeで独りぼっちなんて 嫌じゃないですか 没入型世界で魔法の瞬間を共有しましょう 友達と共有したくてたまらない 没入型体験の素晴らしい案が たくさんあるんです 例えば グループアクティビティで こんな風に宇宙空間に みんなで集まって 一緒に美しい地球を探索する案です これを実現するために 共有アプリは みんなで共有できるような グループの没入型空間を 設定する必要があります システムコーディネータの設定で supportsGroupImmersive Spaceフラグが見つかります それを有効化すると アプリがシステムに このアプリのImmersive Spaceは 共有するためのもので 没入型のグループアクティビティを サポートしたい と伝えます このフラグを有効化した グループセッションに人々が参加すると その人たちの没入型空間も グループの没入型空間になります システムは空間の原点を テンプレートの定義した 共有位置へと動かして 空間的一貫性のために 共有の座標系を設定します グループの没入型空間にいる人々は お互いをPersonaとして見ます これでグループの没入型空間に 共有の座標系と人々が置かれたので その中の同じ位置にオプジェクトを置いて 空間的一貫性が作れます 地球探索アクティビティの例では 地球を原点の真上の中心に置いて 原点に対して全員に相対的になるよう 同じ補正を加えるようにします そうすると全員が地球の 同じ位置を見ることになります 次は 視覚的一貫性を維持する方法です 例えば 地球を回転させたければ その方向が全員にとって同期されてるように する必要があります GroupSessionと GroupSessionMessengerを使って 状態を同期する方法についての詳細は 下に出ているSharePlayセッションをどうぞ また 各個人に相対的な UIエレメントをグループの 没入型空間に加える事もできます 例えば 各参加者にこのような 個人的なコントロールメニューを 与える事もできます 各参加者のすぐ前にあります これを使って共有された地球を 簡単に操作できます このコントロールを適切な位置に 置くために知っておくべき事は グループの没入型空間のどこに ローカルユーザーがいるかです GeometryReader3Dで systemExperienceDisplacementの メソッドが利用できます このメソッドは システムが ローカルユーザーからどのくらいの位置に 空間の原点を配置したかを教えてくれます Pose3D構造体を戻して そこには変換値と回転が 両方とも入っています 空間の原点に対してローカルユーザーが どこにいるのか知るためには このポーズが反転できます それを使ってコントロールを 補正反転すると ローカルユーザー対して相対的に コントローラが現れます ですが ユーザーが動くと コントローラの移動は アップデートされません 最初に置かれた時だけ 空間の移動が可能になります これによりその人がどこにいるか 完全に追跡する必要なく コンテンツを簡単に その人に相対的に置く事ができます 地球探索アクティビティのように 共有コンテンツが真ん中にある グループの没入型空間だけを 持つようなアプリには Surroundテンプレートはピッタリです ですが 共有ウィンドウと グループの没入型空間を 両方持つグループアクティビティなら どうでしょう? 例えば 私はここに出ているような 太陽系について学ぶ グループアクティビティを構築したくて 人々は共有ウィンドウで惑星について 読む事から始められるようになっています そしてボタンを押せば グループの没入型空間で 惑星モデルをよく見ることができます どちらのケースでも 人々を 横に並べたいと思います テンプレートで何ができるでしょうか デフォルトでは グループの 没入型空間があるアプリには Surroundテンプレートが採用され 空間の原点は中央に置かれます アプリに共有される ボリュメトリックシーンがある場合も 同じレイアウトが採用されます ですが アプリに共有される 縦型ウィンドウがあると デフォルトで選ばれるのは Side-by-Sideテンプレートで 空間の原点は共有ウィンドウの 真下にあります ですが テンプレート設定はいつでも グループアクティビティにふさわしい ものに変えられます .conversationalテンプレートを選んだり .sideBySideテンプレートを 選んだりできますし 私は太陽系探索アクティビティには このSideBySideが良いと思います 先ほどもあったように テンプレートでの距離は 共有ウィンドウのサイズによって 大幅に調整されます グループの没入型空間しかない場合は どのようにして 距離が適切かを確認できるのか 疑問に思うかもしれませんね その解決のために テンプレートを 調整する便利な道具 コンテンツエクステントがあります テンプレート設定に対する修飾子で 没入型にも ウィンドウベースのグループ アクティビティにも使えます アプリはこの値を コンテンツの中心から ポイントの一番遠いエッジまでの 距離として設定します コンテンツエクステントが設定されると アクティブなテンプレートが この値を取り込み アプリに相対的に人々を配置します それでは テンプレートについて 学んだことを適用して 先ほどの太陽系探索アクティビティの グループセッションを 構築しましょう まずは supportsGroup ImmersiveSpaceを有効化して 没入している間に一緒に オブジェクトが見られるようにします 次に テンプレートの設定を .sideBySideに選んで contentExtentのための修飾子も追加すれば 人々は常に横並びになり 共有コンテンツからの距離も きちんと適切になります 良いですね それでは 人が入ったこのグループアクティビティを 見てみましょう ここでは 私はConnorと一緒に 横に並んで 共有ウィンドウで 惑星をあれこれ見ています あとになって 私はボタンを押し グループの没入型空間に入って 土星を探索します このモデルの美しさに驚いている間に Connorが一緒にいないと気づき 見えるのは彼の 連絡先写真だけになっています ですが実際は Connorは置き去りにされ まだウィンドウを見ています 自分でボタンを押さなければ グループの没入型空間には入れません 先ほど見たように 人々がグループの 没入型空間に一緒にいないというのは 共有コンテキストを 持っていないという事です それでシステムは空間Personaを隠し 連絡先の写真を表示します もし お互いのPersonaがまだ見えていたら Connorは自分の見えない物に対し 私が驚いているのを見て混乱するでしょう グループの没入型空間が 異なる没入スタイルで表示されれば 同じ状況が起こり得ます 例えば 私が完全没入型の環境で 辺りを見回しているけれど Connorはパススルーだという場合です ですが Connorが私について来て 同じグループの没入型空間に 自動的に入れたらいいですよね そして 私と一緒にいない時 私の居場所が分かって 簡単に 同じ場所に来られたらと思います 没入型のグループアクティビティで バラバラになるのを最小限に抑えるために システムコーディネータで利用できる groupImmersionStyleという 素晴らしいツールがあります このツールが提供する オプショナルな 没入スタイルの非同期シーケンスは ほかの人が参加する グループの没入型空間の 特定の没入スタイルをアプリに伝え また その人たちがグループの 没入型空間を去ると アプリにnilと伝えます 例えば 私がグループの没入型空間を開くと Connorのアプリは その没入スタイルを受け取り それと一致するスタイルで グループの没入型空間が開かれます そして一緒にグループの 没入型空間に入ると 再び共有コンテキストを持ちます 同様に Connorがボタンを押して グループの没入型空間を去ると 私のアプリはnilを受け取り 没入型空間が閉じられます このgroup immersion styleのおかげで 全員がいつも一緒にいられます 時には 何か緊急の用事で 一時的に没入型体験から 抜け出たい事がありますね その場合 Digital Crownを 一度押すだけでいいのです この場合 システムは ほかの人たちの邪魔をしたり グループの没入スタイルを 変える事もありません 抜け出ると SharePlayバナーの 通知が表示されて グループの残りの人たちが どこにいるか教えてくれますし グループの没入型空間に簡単に戻れる 再参加のボタンも出ています 「Join」ボタンをタップすると グループの没入型空間設定のために グループの没入スタイルが アプリに送られて来ます このセッションで 随分たくさん見てきましたね 復習しましょう! 共有コンテキストについて学び そしてシステムコーディネータと シーンの関連付けを使って ウィンドウアプリと没入型共有アプリの 両方において どのように 空間的一貫性と視覚的一貫性を 管理するのかについて学びました アプリが人々を適切に配置できるような テンプレート設定を紹介しました そして最後に Shareメニューを使って SharePlayを始める 新しい方法も紹介しました ご視聴ありがとうございました みなさんも是非 SharePlayで 驚くような体験を開発してください ♪
-
-
4:08 - Observe the local participant state
for await session in ExploreActivity.sessions() { guard let systemCoordinator = await session.systemCoordinator else { continue } let isLocalParticipantSpatial = systemCoordinator.localParticipantState.isSpatial Task.detached { for await localParticipantState in systemCoordinator.localParticipantStates { if localParticipantState.isSpatial { // Start syncing scroll position } else { // Stop syncing scroll position } } } }
-
6:10 - Configure the spatial template preferences
for await session in ExploreActivity.sessions() { guard let systemCoordinator = await session.systemCoordinator else { continue } var configuration = SystemCoordinator.Configuration() configuration.spatialTemplatePreference = .sideBySide systemCoordinator.configuration = configuration session.join() }
-
9:10 - Configuring scene activation conditions
@main struct ExploreTogetherApp: App { var body: some Scene { WindowGroup { ContentView() .handlesExternalEvents( preferring: ["com.example.explore-together.activity"], allowing: ["com.example.explore-together.activity"] ) } } }
-
9:30 - Configuring scene activation conditions
class SceneDelegate: NSObject, UISceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // ... scene.activationConditions.canActivateForTargetContentIdentifierPredicate = NSPredicate(format: "self == %@", "com.example.explore-together.activity") scene.activationConditions.prefersToActivateForTargetContentIdentifierPredicate = NSPredicate(format: "self == %@", "com.example.explore-together.activity") } }
-
10:40 - Setting scene association behavior
struct ExploreActivity: GroupActivity { var metadata: GroupActivityMetadata { var metadata = GroupActivityMetadata() // ... metadata.sceneAssociationBehavior = .content("document-1") return metadata } }
-
13:44 - Starting SharePlay
// Create the activity let activity = ExploreActivity() // Register the activity on the item provider let itemProvider = NSItemProvider() itemProvider.registerGroupActivity(activity) // Create the activity items configuration let configuration = UIActivityItemsConfiguration(itemProviders: [itemProvider]) // Provide the metadata for the group activity configuration.metadataProvider = { key in guard key == .linkPresentationMetadata else { return nil } let metadata = LPLinkMetadata() metadata.title = "Explore Together" metadata.imageProvider = NSItemProvider(object: UIImage(named: "explore-activity")!) return metadata } self.activityItemsConfiguration = configuration
-
16:03 - Configure group ImmersiveSpace
for await session in ExploreActivity.sessions() { guard let systemCoordinator = await session.systemCoordinator else { continue } var configuration = SystemCoordinator.Configuration() configuration.supportsGroupImmersiveSpace = true systemCoordinator.configuration = configuration }
-
17:51 - System Experience Displacement
// Use immersiveSpaceDisplacement to offset contents in group immersive space var body: some Scene { ImmersiveSpace(id: "earth") { GeometryReader3D { proxy in let displacement = proxy.immersiveSpaceDisplacement(in: .global).inverse Control() .offset(displacement.position) .rotation3DEffect(displacement.rotation) } } }
-
20:46 - Spatial Template Preferences
// Configure the spatial template preferences with content extent for await session in ExploreSolarActivity.sessions() { guard let systemCoordinator = await session.systemCoordinator else { continue } var configuration = SystemCoordinator.Configuration() configuration.supportsGroupImmersiveSpace = true configuration.spatialTemplatePreference = .sideBySide.contentExtent(200) systemCoordinator.configuration = configuration }
-
22:32 - Receive group immersion style to configure group immersive space
// Receive group immersion style to configure group immersive space for await session in ExploreSolarActivity.sessions() { guard let systemCoordinator = await session.systemCoordinator else { continue } Task.detached { for await immersionStyle in systemCoordinator.groupImmersionStyle { if let immersionStyle { // Open an immersive space with the same immersion style } else { // Dismiss the immersive space } } } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。