個人製作で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()
}
}
}
}
}
これで表示されたと思います。