ハイライト
UIKit App は書き直さなくても visionOS 上で動作できます。新しいターゲットを追加し、利用できない API に対応し、セマンティックカラーと hover エフェクトでプラットフォームの雰囲気に合わせ、さらに Ornament と RealityKit でコンテンツを 3D 空間へ広げます。
主な内容
ステップ 1: iPad App を visionOS へ移す
UIKit で作った iPad App を visionOS で動かしたい場合、最初の手順は Xcode の General タブで新しい実行先を追加することです。
visionOS の App アイコンも特徴的です。3 枚の画像を重ねて構成され、ユーザーがアイコンを見つめると動的な反応が生まれます。このレイヤー化されたアイコン一式を Asset Catalog に追加します。
(02:01)
ターゲットを追加してそのままビルドすると、エラーに遭遇する場合があります。visionOS は新しいプラットフォームであり、一部の API は適用できません。
利用できない API に対応する
visionOS は iOS 14 より前に非推奨になった API をサポートしません。また、プラットフォーム特性の違いにより、いくつかの API は利用不可としてマークされています。
- UIDeviceOrientation: デバイスが回転できるという前提のため、visionOS には適しません
- UIScreen: 単一画面を表す概念がこのプラットフォームでは成り立ちません
- UITabBar の leadingAccessoryView/trailingAccessoryView: Tab Bar のレイアウト方向が異なります
(03:22)
例のピクセルアート App では UIPencilInteraction を使っていますが、visionOS は Apple Pencil をサポートしないため、この API は利用できません。解決策は条件付きコンパイルで除外することです。
#if !os(xrOS)
// Apple Pencil interaction code
let pencilInteraction = UIPencilInteraction()
pencilInteraction.delegate = self
view.addInteraction(pencilInteraction)
#endif
ビルドエラーを修正すると、App は Shared Space で動作できるようになります。ガラス素材の背景や hover エフェクトはシステムが標準で提供します。
セマンティックカラーとマテリアル: App をプラットフォームになじませる
iPad では白い背景に黒いタイトル文字がきれいに見えても、visionOS のガラス背景上では浮いて見えることがあります。
このプラットフォームではセマンティックカラーが特に重要です。UIColor.label や UIColor.secondaryLabel などのシステムカラーは、プラットフォーム、外観、アクセシビリティ設定に自動で適応します。UILabel がセマンティックカラーを使うと、標準で vibrancy エフェクトも得られます。
(06:58)
Hover エフェクト: インタラクションを知覚しやすくする
visionOS では、ユーザーが要素を見つめると hover エフェクトが発生します。システムコントロールには標準で hover エフェクトがあり、カスタムビューにも追加できます。
UIView には hoverStyle プロパティが追加されました。.highlight または .lift の効果を設定でき、UIShape API で形状をカスタマイズすることもできます。nil に設定すると hover エフェクトを削除できます。
(10:49)
入力方式の変化
visionOS には新しい入力システムが導入されています。
- 視線 + 指のピンチを離す = Tap ジェスチャ
- 視線 + ピンチしたまま手を動かす = Pan ジェスチャ
- 近距離での直接タッチ = 物理的なタッチ
- トラックパッド = ペアリング後に利用可能
- アクセシビリティ = VoiceOver と Switch Control の両方をサポート
このプラットフォームは同時入力を最大 2 つまでサポートします(各手に 1 つ)。例の App では、もともと 4 本指スワイプですべての内容を削除していましたが、visionOS では 2 本指で十分です。
(12:17)
詳細
空間化された UIKit presentations
UIKit の presentations は visionOS 上で空間的なスタイルを得ます。
Sheet: 表示時に presenting view controller を後方へ押し下げ、暗くします。iPad と異なり、sheet の外側をクリックしても自動では閉じません。
Alert: 上部に App アイコンの 2D 表現を配置します。常に後方へ押し下げられるべき view controller から表示されます。
Popover: visionOS では scene の境界に制限されず、App ウィンドウの外へ広がることができます。ただし permittedArrowDirections の設定には注意が必要です。
(14:26)
コード例: Popover の矢印方向を修正する
import UIKit
extension EditorViewController {
@objc func showDocumentPopover(sender: UIBarButtonItem) {
let controller = DocumentInfoViewController(document: pixelDocument)
controller.modalPresentationStyle = .popover
if let presentationController = controller.popoverPresentationController {
presentationController.barButtonItem = sender
if traitCollection.userInterfaceIdiom == .reality {
presentationController.permittedArrowDirections = .any
} else {
presentationController.permittedArrowDirections = .right
}
}
present(controller, animated: true, completion: nil)
}
}
キーポイント:
traitCollection.userInterfaceIdiom == .realityで visionOS 上で動作しているかを判定します- visionOS では popover が scene 境界に制限されないため、
.anyを使ってシステムに最適な位置を選ばせます - iPad では
.rightの制限を維持し、popover が画面外へ出るのを避けます
Ornament: コンテンツをシーンの外へ配置する
Ornament は visionOS の新しい概念で、コンテンツを App の scene 周辺に配置できます。システムコンポーネントでも使われています。SwiftUI の TabView は tab bar を leading edge の ornament に置き、Safari は ornament にナビゲーションバーを配置し、Freeform は下部ツールバーに ornament を使います。
Ornament はメインコンテンツの前方に持ち上げられ、奥行き感を加えます。view controller のライフサイクルに紐づき、sheet が表示されても相対位置を保ちます。
(17:15)
コード例: Ornament を作成する
import UIKit
import SwiftUI
extension EditorViewController {
func showEditingControlsOrnament() {
let ornament = UIHostingOrnament(
sceneAlignment: .bottom,
contentAlignment: .center
) {
EditingControlsView(model: controlsViewModel)
.glassBackgroundEffect()
}
self.ornaments = [ornament]
editorView.style = .edgeToEdge
}
}
キーポイント:
UIHostingOrnamentは SwiftUI コンテンツをホストするため、import SwiftUIが必要ですsceneAlignment: .bottomは ornament を scene 下端に配置しますcontentAlignment: .centerはコンテンツを端の中央にそろえます.glassBackgroundEffect()は ornament にガラス背景を追加しますornamentsは UIViewController の新しいプロパティで、ornament の配列を受け取ります- ornament は view controller のライフサイクルに紐づき、view controller が削除されると自動で消えます
コード例: セマンティックカラーを使う
private let titleLabelTextField: UITextField = {
let textField = UITextField()
textField.textColor = UIColor.label
return textField
}()
private let authorLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.secondaryLabel
return label
}()
キーポイント:
UIColor.labelは主テキスト色で、プラットフォームと外観に自動で適応しますUIColor.secondaryLabelは補助テキストに使い、視覚的な階層を下げます- セマンティックカラーを使う UILabel は自動で vibrancy エフェクトを得ます
- RGB 値のハードコードを避けることで、異なるプラットフォームでも一貫した外観を保てます
コード例: テキストフィールドに recessed な外観を追加する
textField.borderStyle = .roundedRect
キーポイント:
- visionOS では
.roundedRectが自動的に recessed な外観で表示されます - これはシステムがこのプラットフォーム向けに設計した標準スタイルで、追加設定は不要です
コード例: コンテナ背景スタイルをカスタマイズする
class MyViewController: UIViewController {
override var preferredContainerBackgroundStyle: UIContainerBackgroundStyle {
return .glass
}
}
キーポイント:
preferredContainerBackgroundStyleは UIViewController の新しいプロパティです- 選べる値は
.automatic、.glass、.hiddenです - UINavigationController と UISplitViewController は標準でガラス背景を使います
コード例: Hover エフェクトをカスタマイズする
class CollectionViewCell: UICollectionViewCell {
init(document: PixelArtDocument) {
super.init(frame: .zero)
self.hoverStyle = .init(
effect: .highlight,
shape: .roundedRect(cornerRadius: 8.0)
)
}
}
キーポイント:
hoverStyleは UIView の新しいプロパティですeffectには.highlightまたは.liftを選べますshapeはUIShapeAPI を使い、角丸矩形などの形状をサポートしますnilに設定すると hover エフェクトを削除できます
コード例: プラットフォームに応じてジェスチャを調整する
func fourFingerSwipe() {
let gesture = UISwipeGestureRecognizer(
target: self,
action: #selector(self.deleteAll)
)
gesture.direction = .left
if traitCollection.userInterfaceIdiom == .reality {
gesture.numberOfTouchesRequired = 2
} else {
gesture.numberOfTouchesRequired = 4
}
self.view.addGestureRecognizer(gesture)
}
キーポイント:
traitCollection.userInterfaceIdiom == .realityは visionOS 判定の推奨方法です- visionOS は同時入力を最大 2 つまでサポートするため、2 本指スワイプで十分です
- iPad では 4 本指スワイプを維持し、ほかのジェスチャとの衝突を避けます
UIKit で RealityKit を使う
RealityView は RealityKit コンテンツをホストするための新しい SwiftUI view です。UIHostingController を使えば、App 全体を書き直さずに UIKit App の中へ RealityView を埋め込めます。
(21:56)
コード例: UIKit に RealityView を埋め込む
import UIKit
import SwiftUI
import RealityKit
extension EditorViewController {
func showEntityPreview() {
let entityView = PixelArtEntityView(model: entityViewModel)
let controller = UIHostingController(rootView: entityView)
addChild(controller)
view.addSubview(controller.view)
controller.didMove(toParent: self)
prepareEditorInteractions()
}
}
キーポイント:
UIHostingControllerは SwiftUI view を UIKit のビュー階層へ埋め込みますPixelArtEntityViewは内部でRealityViewを使い、RealityKit エンティティをレンダリングします- 標準的な child view controller のライフサイクル管理は
addChild→addSubview→didMove(toParent:)です - この方法により、既存の UIKit App は書き直しなしで 3D 機能を得られます
重要ポイント
-
既存の iPad App を visionOS へ移植する
- 何をするか: 既存の UIKit App(描画ツール、ノート App、リーダーなど)に visionOS ターゲットを追加し、空間内で動作させます
- なぜ取り組む価値があるか: UIKit App はほぼゼロコストで新しい実行環境を得られ、ユーザーはより広い仮想空間であなたの App を使えます
- 始め方: Xcode の General タブで visionOS ターゲットを追加し、ビルドエラー(非推奨 API やプラットフォーム非互換 API)を処理し、ハードコードされた色をセマンティックカラーへ置き換えます
-
Ornament でツールバーを再設計する
- 何をするか: 従来の下部ツールバーやサイドバーを Ornament として切り出し、メインコンテンツ領域を最大化します
- なぜ取り組む価値があるか: Ornament は 2D ウィンドウの境界を越え、ツールコントロールをメインコンテンツの周囲に浮かせるため、遮らず素早くアクセスできます
- 始め方:
UIHostingOrnamentで SwiftUI のツールバー view を包み、適切なsceneAlignmentとcontentAlignmentを設定し、ornament に.glassBackgroundEffect()を追加します
-
App に 3D プレビュー機能を追加する
- 何をするか: UIKit App に RealityView を埋め込み、ユーザーが任意の角度から 3D コンテンツを確認できるようにします
- なぜ取り組む価値があるか: App 全体を書き直す必要はなく、
UIHostingControllerだけで既存 UI に 3D 機能を注入できます - 始め方:
RealityViewを含む SwiftUI view を作成し、UIHostingControllerで UIKit の view controller に埋め込み、child view controller のライフサイクルで管理します
-
空間化された Popover インタラクションを設計する
- 何をするか: visionOS では popover が scene 境界に制限されない特性を活かし、より柔軟なコンテキストメニューを設計します
- なぜ取り組む価値があるか: popover は App ウィンドウの外へ伸びられるため、より自然な指向性のあるインタラクションを提供できます
- 始め方:
permittedArrowDirectionsを確認し、visionOS では.anyを使ってシステムに最適な位置を自動選択させ、プラットフォーム前提のハードコードを避けます
関連セッション
- Meet SwiftUI for spatial computing — 空間コンピューティングプラットフォームにおける SwiftUI の基礎
- Build spatial experiences with RealityKit — RealityKit と RealityView の使い方を深く理解する
- Principles of spatial design — visionOS App の設計を導く空間デザイン原則
- Elevate your windowed app for spatial computing — ウィンドウ型 App を空間コンピューティング環境へ引き上げる上級ガイド
コメント
GitHub Issues · utterances