LoginSignup
12
10

More than 3 years have passed since last update.

SwiftUIでARKitを触って考えたこと

Last updated at Posted at 2020-12-24

はじめに

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での実装で問題ないと思います。

ちなみに上の2つのサンプルを実装するとこうなります。
out.gif

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による実装に慣れておいた方がいいのかなと思いました。

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10