LoginSignup
9
3

More than 3 years have passed since last update.

Swift UIでSceneKitを使う

Posted at

個人製作でSceneKitでARを実装することがあったのでメモります

SwiftUIをVIPERとCombineで書いていきます

CALayerの表示

SceneKitのカメラからの入力を表示するためにCALayerを表示するための UIViewControllerRepresentable を用意します

import SwiftUI

struct CALayerView: UIViewControllerRepresentable {
    var caLayer: CALayer
    let frame: CGRect // frameの大きさ

    func makeUIViewController(context: UIViewControllerRepresentableContext<CALayerView>) -> UIViewController {
        let viewController = UIViewController()

        viewController.view.layer.addSublayer(caLayer)
        caLayer.frame = frame

        return viewController
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<CALayerView>) {
        caLayer.frame = frame
    }
}

セッションの操作

InteractorにSceneKitのセッションのセットアップなどのメソッドを定義します

import ARKit
import Foundation
import SceneKit
import SwiftUI

final class ArInteractor: NSObject, ARSCNViewDelegate {
    private let sceneView = ARSCNView()

    func setupAR() -> CALayer {
        sceneView.scene = SCNScene()
        sceneView.autoenablesDefaultLighting = true
        sceneView.delegate = self

        return sceneView.layer
    }

    func startSettion() {
        let configuration = ARWorldTrackingConfiguration()

      // SceneKitで3Dオブジェクトのdetectionをしたかったのでリソースを読み込ませる
        guard let detectionObjects = ARReferenceObject.referenceObjects(
            inGroupNamed: "AR Resources",
            bundle: nil
        ) else {
            fatalError("Missing expected asset catalog resources.")
        }
        configuration.detectionObjects = detectionObjects
        sceneView.session.run(configuration)
    }

    func stopSettion() {
        sceneView.session.pause()
    }

    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
      // この中で3Dオブジェクトを検知した際の処理を書く
    }
}

ViewとSceneKitを繋ぐ

PresenterでViewとInteractorの繋ぎ込みをして、ViewからSceneKitを操作できるようにします。

import Combine
import Foundation
import SwiftUI

final class ArPresenter: ObservableObject {
    private let router = ArRouter()
    private let interactor = ArInteractor()

    @Published var previewLayer: CALayer {
        willSet {
            objectWillChange.send()
        }
    }

    init() {
        previewLayer = interactor.setupAR()
    }

    func onAppearArView() {
        interactor.startSettion()
    }

    func onDisappearArView() {
        interactor.stopSettion()
    }
}

ARSCNViewを表示させる

最後にViewで先ほど作ったCALayerViewコンポーネントを使ってARSCNViewのCALayerを流し込んで表示させます

import SwiftUI

struct ArView: View {
    @ObservedObject var presenter: ArPresenter

    var body: some View {
        ZStack {
            VStack {
                ZStack {
                    CALayerView(
                        caLayer: presenter.previewLayer,
                        frame: CGRect(x: 0, y: 0, width: 300, height: 300)
                    )
                }
                .onAppear {
                    self.presenter.onAppearArView()
                }
                .onDisappear {
                    self.presenter.onDisappearArView()
                }
            }
        }
    }
}

これで表示されたと思います。

9
3
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
9
3