📌 개요 요약

요즘 주목받는 센서 기반 몰입형 UI 사례를 공유드립니다. 기기의 기울임이나 움직임에 따라 인터페이스가 반응하는 방식으로, 특히 프리미엄 서비스 유도 화면에서 사용자의 시선을 강하게 끄는 데 효과적입니다. 버튼에 적용할 경우 플랫한 버튼보다 훨씬 더 강한 인상을 줄 수 있을 것으로 보입니다.


🛠 구현 구성 요소 (SwiftUI + Core Motion + Metal)

  1. Core Motion으로 기울기 값 수집
  2. SwiftUI에서 실시간 바인딩 & 3D 회전 적용
  3. 색공간 고급 설정 및 메탈 효과 적용
  4. ExtendedRangeViewModifier 적용

💻 코드 템플릿 (Xcode 복붙용)

SwiftUI에서 작동하는 Gyro 기반 UI 템플릿입니다.

기울이면 3D 카드가 반응하며 회전하는 효과가 구현되어 있습니다.

// GyroShaderUIDemoApp.swift
import SwiftUI
import CoreMotion

@main
struct GyroShaderUIDemoApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .modifier(ExtendedRangeViewModifier())
        }
    }
}

class MotionManager: ObservableObject {
    private var motionManager = CMMotionManager()
    @Published var pitch: Double = 0.0
    @Published var roll: Double = 0.0

    init() {
        motionManager.deviceMotionUpdateInterval = 1/60
        motionManager.startDeviceMotionUpdates(to: .main) { motion, error in
            guard let motion = motion else { return }
            self.pitch = motion.attitude.pitch
            self.roll = motion.attitude.roll
        }
    }
}

struct ContentView: View {
    @StateObject var motion = MotionManager()

    var body: some View {
        ZStack {
            Color.black.edgesIgnoringSafeArea(.all)
            RoundedRectangle(cornerRadius: 30)
                .fill(LinearGradient(
                    gradient: Gradient(colors: [.blue, .purple]),
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing))
                .frame(width: 300, height: 300)
                .rotation3DEffect(
                    .degrees(motion.pitch * 20),
                    axis: (x: 1, y: 0, z: 0))
                .rotation3DEffect(
                    .degrees(motion.roll * 20),
                    axis: (x: 0, y: 1, z: 0))
                .drawingGroup(opaque: false, colorMode: .extendedLinear)
        }
    }
}

struct ExtendedRangeViewModifier: ViewModifier {
    func body(content: Content) -> some View {
        content.background(ExtendedRangeBackground())
    }
}

struct ExtendedRangeBackground: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        view.layer.wantsExtendedDynamicRangeContent = true
        return view
    }

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


🔍 구현 난이도 및 팁