🥽

iOSのSharePlayアプリをvisionOS対応する方法

2024/07/30に公開

SharePlayに対応したiOSアプリのvisionOS版を開発する場合、基本的に何もしなくてもvisionOSでも「従来のSharePlay」は適切に動きます。また、「空間的なSharePlay」も概ね問題なく動作します。ただ、追加で実装した方が良いポイントがいくつかあります。今回はそれらを紹介します。

今回、実際に改修するアプリ

今回は、GroupActivitiesフレームワークを代表するApple純正サンプルコード「DrawTogether」を改修していきます。

https://developer.apple.com/documentation/groupactivities/drawing_content_in_a_group_session

DrawTogetherは単一のアクティビティのみで構成されるシングルウインドウアプリです。iOS/iPadOSに対応しています。

iOS版スクショ

完成後のイメージ

visionOS版完成後スクショ

SharePlayに関係無い改修部分についての紹介は省略します。

ポイント1: システムUIとの連携

visionOSにおいて、FaceTimeで通話している間はウインドウにSharePlayや画面共有のためのシステムUIが表示されます。

システムUIの例

このシステムUIからSharePlayを開始することが出来ます。

システムUIからSharePlayを開始するボタン

この「SharePlayを開始するボタンを表示する実装方法」については以下の記事を参照してください。

https://qiita.com/mjnfhbuvwebwfiejcnw/items/e27c87f37f8c7abb604c

ポイント2: 「空間的なSharePlay」時に視覚的不一致を極力避ける

PhotosPickerの写真選択画面が視覚的不一致を起こしてしまい、ユーザーらを混乱させてしまいます。

PhotosPickerの例

今回は、PhotosPickerの写真選択画面を呼び出す前に、写真選択画面を呼び出そうとしているユーザーに「視覚的不一致が起きることを明示」することで混乱を避けましょう。

まず、ユーザー自身が「空間的なSharePlay」中かどうかをチェック出来るようにします。

@Published private(set) var spatialSharePlaying: Bool?
self.tasks.insert(
    Task {
        if let systemCoordinator = await groupSession.systemCoordinator {
            for await localParticipantState in systemCoordinator.localParticipantStates {
                self.spatialSharePlaying = localParticipantState.isSpatial
            }
        }
    }
)

systemCoordinator.localParticipantStatesからユーザー自身が「空間的なSharePlay」中かどうかのBool値を受け取り、値を保持します。

次に、PhotosPickerの写真選択画面が表示される前に注意書きを表示する実装をします。

@State private var notificationIsPresented: Bool = false
if canvas.groupSession != nil {
    if canvas.spatialSharePlaying == true {
        Button {
            notificationIsPresented = true
        } label: {
            Image(systemName: "photo.fill")
        }
    } else {
        photosPicker()
    }
}
.sheet(isPresented: self.$notificationIsPresented) {
    NavigationStack {
        VStack {
            Text("この画面や、写真選択画面は他の人には見えていません。")
                .font(.largeTitle)
            photosPicker()
        }
        .padding(48)
        .toolbar {
            Button("戻る") {
                notificationIsPresented = false
            }
        }
    }
}

PhotosPickerの対処例

ポイント3: 「視覚的不一致」が前提の場合の事前設定

ポーカーや麻雀のような視覚的不一致が前提のコンテンツの場合、「空間的なSharePlay」をオフにするためにアクティビティとSceneの紐付けを回避する必要があります。

struct MyActivity: GroupActivity {
    var metadata: GroupActivityMetadata {
        var metadata = GroupActivityMetadata()
        metadata.title = "MyActivity"
        metadata.sceneAssociationBehavior = .none // ←
        return metadata
    }
}

DrawTogetherは「視覚的一致」が前提のコンテンツなので、この実装は必要ありません。

ポイント4: テンプレートを設定して立ち位置を調整

「空間的なSharePlay」中のペルソナの立ち位置は、プレーンなウインドウではSide-by-Side(横並びモード)というテンプレートがデフォルトで適用されます。もし、参加者同士の会話がメインとなるアプリ(例えばBGM再生アプリなど)の場合、テンプレートをConversational(会話モード)に変更すると良いでしょう。

テンプレートの種類

self.tasks.insert(
    Task {
        if let systemCoordinator = await groupSession.systemCoordinator {
            var configuration = SystemCoordinator.Configuration()
            configuration.spatialTemplatePreference = .conversational
            systemCoordinator.configuration = configuration
            groupSession.join()
        }
    }
)




余談

【Apple Vision Proを持っている方への相談】「空間的なSharePlay」アプリの動作確認や動画撮影等に協力してくれませんか?

実機が1台だけでは「空間的なSharePlay」を動かすことが出来ません。お互いにApple Vision Pro実機からペルソナでFaceTime通話している時だけ「空間的なSharePlay」を動かすことが出来ます。ただ、残念ながら私にはApple Vision Proを持っている知り合いがいないのです。もし協力してくださる方がいらっしゃれば、こちらのメールアドレス(waggle.slips.0t@icloud.com)に連絡ください!

==== 追記 2024/12/01 ====
12月現在も協力者募集中です!
==== 追記ここまで ====

ペルソナの姿でお願いする私


https://qiita.com/mjnfhbuvwebwfiejcnw/items/e27c87f37f8c7abb604c

https://qiita.com/mjnfhbuvwebwfiejcnw/items/498ad1878dd48ea53412

https://zenn.dev/huiygfutfgvjknj/articles/9990f9ee42a410

https://zenn.dev/huiygfutfgvjknj/articles/5665e31e97284a

Discussion