#はじめに
SwiftUIが2019年に登場してそろそろ2年になろうとしているところ、仕様も確立しつつあり、SwiftUIに移行し始めている方も多いかと思います。
しかしARKitを使ったアプリとなると話は別で、調べてもあまり情報が出てきません。Apple公式から出ているARアプリのSampleもまだStoryboardによる実装です。
例えばLiDARを使ったPointCloudとか。
https://developer.apple.com/documentation/arkit/visualizing_a_point_cloud_using_scene_depth
確かにARアプリとなるとPinchやRotateなどUIGestureRecognizerによる操作が中心になるので、既存のUIViewControllerとセレクタによる実装の方が直感的にとっつきやすいです。
そういう事情はあれ、これからはSwiftUIによるアプリ開発が主流になると思うので、この記事ではSwiftUIでARKitを使った際のポイントについて書いていきます。
#環境
- Swift 5.3
- Xcode 12.2
- ARKit 4
#UIView/UIViewControllerの実装
まずSwiftUIのプロジェクトからAugmented Reality Appを選択すると以下のように初期コードが実装されます。
import SwiftUI
import RealityKit
struct ContentView : View {
var body: some View {
return ARViewContainer().edgesIgnoringSafeArea(.all)
}
}
struct ARViewContainer: UIViewRepresentable {
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero)
let boxAnchor = try! Experience.loadBox()
arView.scene.anchors.append(boxAnchor)
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {}
}
//Preview部分は省略
SwiftUIでUIKitを使う場合はRepresentableによる実装となります。
このコードではARViewContainerの部分ですね。
このARViewContainerを上のContentViewで読み込むことにより、画面に表示する形となります。
AR空間を表示するARViewはARViewContainerのmakeUIView(context:)
で実装しています。
下のupdateUIView(_:context:)
でARViewの更新を管理することになります。
一方でUIViewContorollerを使う場合はこちら。
import SwiftUI
import RealityKit
struct ContentView : View {
var body: some View {
return ARViewControllerContainer().edgesIgnoringSafeArea(.all)
}
}
struct ARViewControllerContainer: UIViewControllerRepresentable {
typealias UIViewControllerType = ARViewController
func makeUIViewController(context: Context) -> ARViewController {
return ARViewController()
}
func updateUIViewController(_ arViewController: ARViewController, context: Context) {}
}
class ARViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let arView = ARView(frame: .zero)
let boxAnchor = try! Experience.loadBox()
arView.scene.anchors.append(boxAnchor)
self.view = arView
}
}
//Preview部分は省略
UIViewRepresentableと比べてARViewControllerのクラスが増えました。
このARViewControllerは必須というわけではなく、例えばviewDidLoad()
の中身をそのままARViewControllerContainerに移しても使えます。
ですからARViewのみを使うのであればUIViewRepresentableでの実装で問題ないと思います。
#UIGestureRecognizerの実装
UIViewcontrollerRepresentableを使うとこれまでのUIGestureがそのまま使えます。
class ARViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let arView = ARView(frame: .zero)
let boxAnchor = try! Experience.loadBox()
arView.scene.anchors.append(boxAnchor)
self.view = arView
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tap))
self.view.addGestureRecognizer(tapGesture)
}
@objc
func tap(sender: UITapGestureRecognizer) {}
}
ただ、SwiftUIのような新しいフレームワークを出したということは、これまで以上にObjective-Cから脱却していくような感じなりそうな中で、ずっと@objc
を使うのが良いのかどうかは微妙なところです。
しかしながらSwiftUIでGestureを実装しようとすると、最初にonTapGesgure{}
などで値を取得して、それを@State
や@Binding
でRepresentableに送って、そこからSCNNodeやModelEntityなどのプロパティを変更して・・・と手順を踏む必要があります。
恐らくAppleとしてはTapのような基本的な動作はRealityComposerで設定して、RotateやPanのような動作はコードで実装して欲しいというところではないかと思いますが、現状ではこれまで通りの実装でも問題無さそうではあります。
#感想
個人的には最初に書いたようにUIViewControllerとセレクタによる実装はとっつきやすいです。
ただ、今後この方法が使えなくなる可能性もありますし、SwiftUIを使ったもっと簡単な仕組みを実装しそうでもあります。
あとはSwiftUIだとコードを書いたらそのまま複数デバイスで使用することができるので、Storyboardのようにデバイスごとに細かくボタンなどの位置を調整して・・・という作業がなくなるのが非常に助かります。
これはARアプリ以外にも当てはまりますね。
これからAppleはSwiftUIを中心にコンテンツを追加していきますし、ARKitもどんどん発展・改善されていきそうなので、早いところSwiftUIによる実装に慣れておいた方がいいのかなと思いました。