ARKit y RealityKit en Swift: detección de planos, AR anchors y escenas aumentadas con SwiftUI

ARKit detecta el entorno físico y RealityKit renderiza objetos 3D sobre él. La combinación de ambos permite colocar muebles virtuales en un salón, ver cómo queda un cuadro en la pared o construir juegos que usan el suelo y las mesas de la habitación real. Con SwiftUI, la integración es más sencilla que con UIKit gracias a ARView en un UIViewRepresentable.

Configurar ARKit: ARWorldTrackingConfiguration

ARKit usa la cámara y los sensores del dispositivo para construir un mapa del entorno. La configuración más completa es ARWorldTrackingConfiguration:

import ARKit
import RealityKit

class ARCoordinator: NSObject, ARSessionDelegate {
    var arView: ARView?

    func configurar() {
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = [.horizontal, .vertical]  // Detectar suelo, techo y paredes
        config.environmentTexturing = .automatic           // Reflexiones realistas

        // Oclusión de personas (A14 en adelante)
        if ARWorldTrackingConfiguration.supportsFrameSemantics(.personSegmentationWithDepth) {
            config.frameSemantics.insert(.personSegmentationWithDepth)
        }

        arView?.session.run(config, options: [.resetTracking, .removeExistingAnchors])
        arView?.session.delegate = self
    }

    // Cuando se detecta un nuevo plano
    func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
        for anchor in anchors {
            if let plano = anchor as? ARPlaneAnchor {
                print("Plano (plano.alignment == .horizontal ? "horizontal" : "vertical") detectado")
            }
        }
    }
}

ARView en SwiftUI con UIViewRepresentable

ARView es una vista UIKit que se envuelve en UIViewRepresentable para integrarse en SwiftUI:

import SwiftUI
import RealityKit
import ARKit

struct ARVistaSwiftUI: UIViewRepresentable {
    @Binding var colocarObjeto: Bool

    func makeUIView(context: Context) -> ARView {
        let arView = ARView(frame: .zero)

        // Configurar AR
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = [.horizontal]
        arView.session.run(config)

        // Añadir coaching overlay (guía visual para el usuario)
        let overlay = ARCoachingOverlayView()
        overlay.session = arView.session
        overlay.goal = .horizontalPlane
        overlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        arView.addSubview(overlay)

        // Gesto para colocar objetos al tocar
        let toque = UITapGestureRecognizer(target: context.coordinator,
                                            action: #selector(Coordinator.manejarToque(_:)))
        arView.addGestureRecognizer(toque)
        context.coordinator.arView = arView

        return arView
    }

    func updateUIView(_ uiView: ARView, context: Context) { }

    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    class Coordinator: NSObject {
        var arView: ARView?

        @objc func manejarToque(_ reconocedor: UITapGestureRecognizer) {
            guard let arView else { return }
            let punto = reconocedor.location(in: arView)
            colocarModelo(en: punto, arView: arView)
        }

        func colocarModelo(en punto: CGPoint, arView: ARView) {
            // Raycasting: proyectar el toque sobre superficies detectadas
            let resultados = arView.raycast(from: punto,
                                             allowing: .estimatedPlane,
                                             alignment: .horizontal)
            guard let primer = resultados.first else { return }

            // Crear entidad 3D en la posición del raycast
            let mesh = MeshResource.generateBox(size: [0.1, 0.1, 0.1], cornerRadius: 0.01)
            let material = SimpleMaterial(color: .orange, isMetallic: true)
            let modelo = ModelEntity(mesh: mesh, materials: [material])

            // Crear anchor en la posición AR
            let anchor = AnchorEntity(world: primer.worldTransform)
            anchor.addChild(modelo)
            arView.scene.addAnchor(anchor)
        }
    }
}

Raycasting: colocar objetos con precisión

El raycasting proyecta un rayo desde el punto de toque en pantalla hacia el mundo real, devolviendo la intersección con superficies detectadas:

// Raycasting a planos existentes (más preciso)
let resultados = arView.raycast(from: puntoPantalla,
                                 allowing: .existingPlaneGeometry,
                                 alignment: .horizontal)

// Raycasting a planos estimados (funciona sin plano confirmado todavía)
let resultados2 = arView.raycast(from: puntoPantalla,
                                  allowing: .estimatedPlane,
                                  alignment: .any)

if let hit = resultados.first {
    let posicion = hit.worldTransform.translation  // simd_float3
    // hit.worldTransform contiene posición + orientación de la superficie
}

Gestos AR: mover y rotar modelos

RealityKit tiene gestos AR integrados que funcionan directamente con los modelos:

// En el makeUIView, añadir gestos a la arView:
arView.installGestures([.translation, .rotation, .scale], for: modelo)

// El modelo debe tener CollisionComponent para que los gestos funcionen:
modelo.generateCollisionShapes(recursive: true)
modelo.components.set(InputTargetComponent())

App de decoración: ejemplo completo

struct AppDecoracion: View {
    @State private var modeloSeleccionado: String = "silla"
    @State private var colocarActivo = false

    let modelos = ["silla", "mesa", "lampara", "sofá"]

    var body: some View {
        ZStack(alignment: .bottom) {
            ARVistaSwiftUI(colocarObjeto: $colocarActivo)
                .ignoresSafeArea()

            // Panel de selección de muebles
            ScrollView(.horizontal) {
                HStack(spacing: 16) {
                    ForEach(modelos, id: .self) { nombre in
                        Button(nombre.capitalized) {
                            modeloSeleccionado = nombre
                        }
                        .padding(.horizontal, 16)
                        .padding(.vertical, 8)
                        .background(modeloSeleccionado == nombre ? Color.blue : Color.gray.opacity(0.5))
                        .foregroundStyle(.white)
                        .clipShape(Capsule())
                    }
                }
                .padding()
            }
            .background(.ultraThinMaterial)
        }
    }
}

ARCoachingOverlayView: guiar al usuario

ARCoachingOverlayView muestra instrucciones visuales automáticas para que el usuario mueva el dispositivo y permita a ARKit mapear el entorno. Se activa automáticamente cuando el tracking quality es insuficiente y desaparece cuando se alcanza el objetivo configurado. Es el estándar recomendado por Apple para el onboarding de apps AR.

Resumen

ARKit proporciona el mapa del entorno real (planos, anclas, posición del dispositivo) y RealityKit lo popula con objetos 3D. El flujo básico es: configurar ARWorldTrackingConfiguration con detección de planos, envolver ARView en UIViewRepresentable, usar raycasting para convertir toques en posiciones 3D y añadir entidades ModelEntity o modelos USDZ en esas posiciones. ARCoachingOverlayView gestiona el onboarding de tracking y los gestos integrados de RealityKit permiten mover y escalar objetos sin código adicional.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP