
-
visionOS에서 SwiftUI로 장면 설정하기
visionOS 앱에서 윈도우, 볼륨 및 몰입형 공간을 향상시킬 수 있는 흥미로운 새 API를 알아보세요. 장면이 다시 시작되거나 고정된 상태에서 동작을 미세 조정할 수 있습니다. 클리핑 마진 및 스냅핑으로 주변 환경에 맞춰 볼륨이 조정되도록 설정할 수 있습니다. Mac에서 Vision Pro로 몰입형 콘텐츠를 스트리밍하세요. 볼륨과 몰입형 공간으로 기존 UIKit 기반 앱을 업그레이드하는 법도 소개합니다.
챕터
리소스
- Adopting best practices for persistent UI
- Canyon Crosser: Building a volumetric hike-planning app
- Petite Asteroids: Building a volumetric visionOS game
- Tracking accessories in volumetric windows
관련 비디오
WWDC25
-
비디오 검색…
안녕하세요, 저는 SwiftUI 팀의 엔지니어 Miguel입니다 이 비디오에서는 visionOS 26의 장면에 추가된 놀라운 새 기능을 다룹니다 그리고 자체 장면을 연출하는 방법을 알아봅니다
visionOS의 세 가지 장면 유형은 윈도우, 볼륨, 몰입형 공간입니다 앱은 이들을 결합해 독특하고 흥미로운 경험을 만들 수 있습니다
오늘은 세 유형 모두에 적용되는 새로운 API와 볼륨 및 몰입형 공간에 중점을 둔 API를 살펴보겠습니다 제 친구 Maks, Amanda, Trevor가 BOTanist를 위한 흥미로운 개선 사항을 개발 중입니다 로봇이 공중 정원에서 아름다운 식물을 키우도록 돕는 게임이죠
저는 로봇 친구가 셰익스피어 작품의 인물을 만들어 연기할 수 있도록 장면과 장식을 빌드하는 앱을 만들고 있습니다
앱을 열면 무대 선택 화면이 나옵니다 새 무대를 만들겠습니다
이제 무대에서 BOTanist를 움직여 장면을 설정할 수 있습니다
좋아하는 연극을 로봇 친구가 재연하게 할 수 있죠 로보와 줄리엣이라는 작품처럼요
visionOS 26의 새로운 장면 API으로 앱을 개선하는 방법을 알아보겠습니다
먼저 윈도우를 실행하고 방에 고정할 때의 동작을 정의하는 새로운 라이프사이클 API를 살펴보겠습니다
그리고 사용자의 주변 환경에 맞게 조정되는 새로운 볼류메트릭 향상 사항을 소개합니다
이어서 RemoteImmersiveSpace를 추가해 macOS 앱의 Apple Vision Pro에서 장면을 미리 봅니다
그런 다음 기존 UIKit 앱에 대해 이 놀라운 볼류메트릭 및 몰입형 경험에 새로운 장면 브리징 API를 추가하며 마무리합니다
그러면 실행과 고정부터 보겠습니다
visionOS 26은 상당히 유용한 몇몇 macOS 라이프사이클 API를 제공합니다 장면 복원 및 앱 실행을 관리하는 API와 고유 윈도우를 만드는 API를 살펴보겠습니다
visionOS 26에서 이제 사용자는 윈도우, 볼륨, 새로운 위젯을 물리적 주변 환경의 특정 방에 고정해 유지할 수 있습니다
따라서 가상 콘텐츠가 해당 공간에서 더 생생하게 느껴집니다 이 고정된 윈도우는 사용된 방에 연결됩니다
나중에 이 방으로 돌아오면 창문이 다시 생생해집니다
고정을 활용한 장면 복원이 좋네요 모든 윈도우를 그대로 두고 원할 때마다 돌아갈 수 있습니다
일반적으로 사용자는 모든 윈도우를 제자리에 고정하고 시스템이 이를 복원하는 기능을 기대합니다 대부분의 장면에 복원을 선호하죠 시스템이 자동으로 이 작업을 수행합니다
하지만 일부 윈도우는 이렇게 유지하는 것이 의미가 없을 수도 있습니다
환영 화면, 특정 앱 상태에 연결된 도구 윈도우 같은 컨텍스트 기반 UI, 로그인 메시지 같은 완료된 일회성 동작처럼 일시적인 요소에는 장면 복원을 비활성화하는 것을 고려하세요
사용자가 멋진 로봇의 연기를 제대로 감상할 수 있도록 앱에 몰입형 모드를 추가했습니다 도구 막대는 앞에 있는 별도의 도구 윈도우로 분리되어 몰입 상태에서도 무대 컨트롤에 쉽게 접근할 수 있습니다
몰입형 공간은 복원되지 않습니다
그래서 이 방으로 돌아오면 몰입형 공간이 다시 연출되지 않습니다
하지만 사용자가 공간에 도구 윈도우를 고정했다면 수정할 부분 없이 단독으로 나타납니다
이 예상치 못한 상태를 피하려면 restorationBehavior(.disabled) 수정자를 WindowGroup에 추가해 도구 윈도우를 복원 및 고정에서 명시적으로 옵트아웃하면 됩니다
이제 나중에 이 방으로 다시 돌아오면 추가 윈도우가 다시 나타나지 않죠
앱을 실행하면 새로 시작할 수 있는 첫 번째 윈도우가 열립니다
UIKit에서는 새로운 destructionConditions API와 .systemDisconnection 속성을 사용해 UI 장면의 복원을 비활성화할 수 있습니다 자세한 내용은 문서를 확인하세요
경우에 따라서는 시작할 장면을 동적으로 변경하는 것이 앱에 유리할 수 있습니다
예를 들어 앱의 첫 번째 실행 시 무대 선택 전에 인사말을 전하는 환영 창을 추가하고 싶습니다
실행 시 앱의 상태에 따라 표시할 윈도우를 맞춤화하려면 defaultLaunchBehavior 수정자를 사용하면 됩니다
여기서는 앱을 처음 실행하는 경우 환영 윈도우를 presented로 표시해 이 윈도우를 우선시합니다
해당 윈도우가 나타난 후에는 이 값을 끌 수 있습니다 더 이상 이 윈도우를 표시할 필요가 없으니까요
선택한 실행 윈도우의 역할은 Info.plist의 Application Scene Manifest에서 선호하는 기본 장면 세션 역할 속성과 일치해야 한다는 점에 유의하세요
즉 기본 장면 세션 역할을 Window로 설정하면 앱 실행 시 시스템에서 일반 윈도우 장면만 고려합니다
이 경우 볼륨은 무시됩니다 defaultLaunchBehavior로 우선시하더라도 말입니다 따라서 원하는 장면이 세션 역할과 일치하도록 해야 합니다
defaultLaunchBehavior 수정자에는 한 가지 유용한 요령이 숨어 있습니다
앞서 방으로 돌아왔을 때 도구 패널이 돌아오지 않아야 하며 이를 위해 restorationBehavior를 사용하는 방법을 말씀드렸죠 여기도 비슷한 문제가 있습니다
현재 크라운을 눌러 몰입형 공간을 닫은 후 Tools(도구) 윈도우를 닫으면
앱을 다시 실행했을 때 이 윈도우가 다시 표시됩니다
복원에서와 마찬가지로 예상치 못한 상태가 발생하는 거죠
앱을 재개하면 안전한 상태에서 무대 생성 윈도우로 시작되었으면 합니다
이를 위해 Tools(도구) 윈도우에 defaultLaunchBehavior(.suppressed) 수정자를 추가하면 됩니다 Home(홈) 보기에서 앱을 다시 실행할 때 시스템이 해당 윈도우를 다시 가져오지 않도록 하는 겁니다
일반적으로 보조 화면에는 .suppressed defaultLaunchBehavior 위주로 사용해 사용자가 예상치 못한 상태에 처하는 것을 방지해야 합니다
UIKit에서 같은 동작을 달성하려면 UIScene의 destructionConditions에 userInitiatedDismissal 옵션을 추가하면 됩니다
visionOS 26은 또한 고유 윈도우 만들기도 지원합니다
이러한 윈도우는 복제할 수 없습니다 한 번에 하나의 고유 인스턴스만 존재할 수 있습니다
Mac에서처럼 WindowGroup 대신 Window API를 선언하면 됩니다
이 윈도우로 게임 윈도우 영상 통화 등 중요한 인터페이스의 중복을 방지할 수 있습니다
인스턴스가 하나만 필요한 보충 기능을 제공할 때도 사용할 수 있습니다
환영 윈도우는 인스턴스가 하나면 됩니다 따라서 고유 Window로 바꿉니다
하지만 메인 무대 볼륨은 WindowGroup으로 유지합니다 동시에 여러 무대를 만들 수 있도록요
좋습니다 앱의 라이프사이클이 개선되었죠 이렇게 제자리에 고정되었을 때와 앱 실행 시 보여야 하는 윈도우를 맞춤화했습니다 그리고 필요한 곳에 고유 윈도우를 마련했습니다
visionOS 26에서는 더 심층적인 볼륨을 위해 새로운 향상 사항이 많이 추가되었습니다
새로운 표면 스냅 기능 프레젠테이션 개선 사항과 Clipping Margins API를 살펴보겠습니다
바로 시작해 볼까요
visionOS 26의 새 기능으로 윈도우와 볼륨을 물리적 환경에 스냅할 수 있습니다 윈도우를 표면에 가까이 옮기면 됩니다 복원 가능한 윈도우의 경우 제자리에 영구적으로 고정됩니다
윈도우의 뒷면을 벽과 같은 수직면에 스냅할 수 있습니다 볼륨의 하단을 바닥, 테이블 등의 수평면에 스냅할 수도 있습니다
visionOS 26에 새로 추가된 위젯도 똑같이 스냅할 수 있습니다
visionOS 앱에 위젯을 추가하는 방법은 “위젯의 새로운 기능”을 참고하세요
윈도우와 볼륨의 경우 스냅 이벤트에 대한 정보를 얻을 수도 있습니다
제 앱에서 볼륨을 테이블에 스냅해 수평으로 고정할 수 있습니다
일단은 이렇게 시작인데 제 공간에서 무대가 더 실제처럼 느껴졌으면 좋겠습니다 볼륨을 테이블에 스냅했을 때 로봇이 테이블 바로 위에 서 있으면 좋겠고요
새로운 SurfaceSnappingInfo API로 스냅 상태에 대한 정보를 얻을 수 있습니다
이 API는 윈도우의 일반적인 스냅 상태를 판단할 간단한 isSnapped 속성을 제공합니다
고급 사용 사례에서는 스냅된 표면의 ARKit 분류를 확인할 수 있습니다 이 수준의 세부 정보에는 사용자 권한이 필요합니다 방법을 보여 드리겠습니다
스냅된 표면 상세 정보를 활성화하려면 가장 먼저 ‘Application Wants Detailed Surface Info’ (앱이 자세한 표면 정보 원함) 키를 YES(예)로 설정하고 ‘Privacy World-Sensing Usage-Description (개인정보 보호 월드 센싱 사용 설명)’에 권한을 요청할 때 표시할 설명을 설정해야 합니다
두 키는 앱의 Info.plist에서 설정할 수 있습니다
이 작업을 완료한 후 코드로 넘어갑니다
환경의 surfaceSnappingInfo를 가져오고요
onChange에서 장면이 현재 스냅되었는지 확인합니다 그리고 스냅 표면 분류에 접근할 권한이 있는지 확인합니다
authorizationStatus를 확인하면 필요시 자동으로 사용자에게 권한을 요청합니다
테이블에 스냅했을 때 플랫폼을 무대 아래에 숨기고 싶은데요 이를 추적하기 위해 상태 변수를 사용합니다
이렇게 변경을 적용한 결과 볼륨을 테이블에 스냅할 수 있고 로봇은 환경에서 마음껏 연기할 수 있죠 좋아요
또한 테이블 주변을 걸어다니면 시야를 가리는 벽이 테이블에서 숨겨지도록 했습니다 항상 볼륨을 볼 수 있게요
onVolumeViewpointChange 수정자를 사용해 장면의 시점 변화에 반응하는 방식입니다
Owen이 BOTanist에 추가한 방법은 WWDC24의 “볼륨 및 몰입형 공간 자세히 알아보기”를 참고하세요
사용자가 무대에 새로운 소품을 배치할 수 있으면 좋겠는데요 볼륨의 도구 막대에 각종 소품이 있는 팝오버를 추가할 수 있습니다
마침내 기어 왕의 비극이라는 작품을 재현할 수 있겠군요
이전에는 프레젠테이션이 Windows에서만 지원되었습니다
visionOS 2.4에서는 중첩된 프레젠테이션 지원이 추가되어 시트에서 표시되는 팝오버나 오너먼트에서 표시되는 빠른 메뉴가 가능했습니다
visionOS 26에서는 프레젠테이션에 새 소스 세트가 도입되었습니다 볼륨, 볼륨 오너먼트 RealityViews 첨부 파일에서 표시할 수도 있고 PresentationComponent를 사용해 RealityKit에서 직접 표시할 수도 있습니다
RealityKit에서 첨부 파일과 PresentationComponent로 프레젠테이션을 사용하는 방법은 “함께하면 더욱 탁월한 SwiftUI 및 RealityKit”을 참고하세요
작은 하위 집합에 국한되지 않으며 메뉴, 툴팁, 팝오버, 시트, 경고 확인 대화상자 등 모든 프레젠테이션 유형을 사용할 수 있습니다
SwiftUI로 이러한 프레젠테이션을 만드는 방법은 문서를 확인하세요
이러한 프레젠테이션에는 3D 콘텐츠에 가려졌을 때 보이게 하는 특별한 시각적 처리가 있습니다
기본적으로 프레젠테이션은 가리는 콘텐츠와 미묘하게 섞입니다
하지만 가리는 콘텐츠를 그대로 뚫고 나오거나 뒤에 숨도록 맞춤화할 수 있습니다
맞춤화하려면 subtle, prominent 또는 none 옵션을 사용하면 됩니다
이러한 옵션을 프레젠테이션에 적용하는 데는 presentationBreakthroughEffect 수정자를 사용합니다
프레젠테이션 외의 요소에서는 breakthroughEffect 수정자로 동일한 효과를 얻을 수 있습니다
이제 프레젠테이션으로 원하는 곳에 맞춤형 UI를 추가할 수 있죠 더 추가해 볼까요
무대 장식을 바꾸는 팝오버 메뉴를 하나 더 추가했습니다 이를 통해 로봇 친구를 옛날식 극장에서 열대 섬으로 데려올 수 있습니다 폭풍우라는 작품에 딱이네요
이 세트는 잠재력이 무한합니다 그런데 활기를 좀 더하는 게 좋겠군요 폭포는 어떨까요? 천둥 치는 구름도요
하지만 동작의 중심이 혼잡해 보여서는 안 되겠죠 이때 새로운 Clipping Margins를 사용하면 됩니다
볼륨에 새로운 preferredWindowClippingMargins API를 사용하면 장면 경계 외부에 콘텐츠를 렌더링할 수 있습니다
이 콘텐츠는 대화형이 아닙니다 따라서 시각적 장식에만 사용해야 합니다
이러한 경계는 시스템에서 허용되지 않을 수도 있습니다 이에 대비해 windowClippingMargins 환경 변수로 허용된 여백을 읽습니다
어떻게 작동하는지 볼까요
preferredWindowClippingMargins API로 클리핑 여백을 지정 가능합니다 여기서는 여백을 하단에 둡니다
미터 단위의 maxWaterfallHeight에 PhysicalMetric의 pointsPerMeter 인수를 곱해 지점으로 변환하고요
그런 다음 windowClippingMargins 환경 변수로 허용된 여백을 읽습니다
이것으로 폭포 크기를 조정해 여백 내에서 렌더링할 수 있습니다
최소 여백과 폭포 높이를 사용해 허용치에 관계없이 항상 폭포 모델 전체가 렌더링되도록 하겠습니다
완성입니다 훨씬 낫죠 구름이 폭풍우 분위기를 더해 주며 콘텐츠가 위로 이동되지 않고 폭포가 베이스 플레이트 아래쪽으로 렌더링되어 섬과 로봇에 시선이 집중됩니다
BOTanist에 기름칠은 잘 했겠죠?
이렇게 최신 연극 작품이 생동감 넘치는 모습으로 준비되었습니다 콘텐츠는 표면 스냅과 클리핑 여백으로 물리적 공간에 맞게 조정됩니다 프레젠테이션으로는 강력한 인터페이스를 만들어 완벽한 장면을 연출할 수 있습니다
이제 앱의 몰입형 경험을 개선하는 방법을 살펴보겠습니다
몰입형 공간은 사용자 주변의 공간 경험을 가져옵니다 visionOS 26에서는 새롭고 유용한 방법으로 더 멋진 몰입형 공간을 만들 수 있습니다
바로 몰입형 스타일, macOS의 원격 몰입형 공간, 그리고 Metal 렌더링을 위한 Compositor 기반 몰입형 공간의 개선 사항을 갖춘 세계 중심 재설정 이벤트입니다 사용자는 탐색할 때 Digital Crown을 길게 눌러 주변 앱 경험의 중심을 재설정할 수 있습니다
앱이 ARKit 데이터를 사용하는 경우 나중에 사용하고자 저장한 위치가 무효화될 수 있습니다
새로운 onWorldRecenter 보기 수정자로 세계 중심 재설정 이벤트를 수신해 알림을 받을 수 있습니다
이는 새로운 좌표 시스템을 기준으로 위치를 다시 계산하고 저장하는 데 유용합니다
visionOS 26에는 몰입형 공간에 사용 가능한 다양한 몰입 스타일에 대한 새로운 맞춤화도 있습니다
점진적 몰입 스타일은 몰입형 공간을 부분적으로 표시하면서 사용자가 현실 감각을 유지하도록 하는 데 좋습니다
몰입형 콘텐츠는 포털 안에 표시되며 Digital Crown을 돌려 포털 크기를 조절할 수 있습니다
점진적 몰입 스타일에서는 이 몰입 범위를 맞춤화할 수 있죠
visionOS 26에서는 포털의 영상비도 맞춤화할 수 있습니다 기존 가로 방향 영상비나 새로운 세로 방향 영상비를 사용할 수 있습니다
세로 방향 경험에는 세로 방향 영상비를 사용하면 좋습니다 iPhone 게임을 Apple Vision Pro로 가져올 때처럼요 격렬한 모션이 포함된 경험일 때도 주변 환경이 안정 상태면 사용자가 더 편안하게 느끼기 때문에 유용합니다
이 영상비를 지정하려면 몰입 범위에서와 마찬가지로 점진적 스타일에 매개변수를 사용하면 됩니다
점진적 스타일 외에 혼합형 몰입 스타일의 몰입형 공간을 위한 새로운 맞춤화 기능도 있습니다
몰입 스타일을 혼합으로 설정하면 몰입형 공간의 콘텐츠가 현실의 주변 환경과 융화됩니다
이것이 제 앱의 기본 스타일이고요
visionOS 26에서는 시스템 환경과도 융화될 수 있습니다 로봇의 최신 작품을 달에서 볼 수도 있다는 거죠
immersiveEnvironmentBehavior 장면 수정자에 coexist 동작을 사용하면 됩니다
단 사용자가 현실 주변 환경에 주의하지 않아도 되는 혼합 몰입형 공간에 사용하세요
앱에 추가한 소품이 마음에 들지만 사용자는 새로운 장면을 만들 때 자체 모델을 가져오고 싶을 것입니다 좋아하는 macOS 앱에서 이러한 모델을 만들 수 있습니다
Mac에서 이러한 모델을 직접 사용할 수 있도록 무대 생성 기능을 그대로 유지해 앱을 macOS로 가져왔습니다
빠른 반복을 위해 무대를 macOS에서 visionOS로 전송하지 않고 장면을 몰입형 공간에서 바로 미리 볼 수 있다면 좋지 않을까요?
visionOS 26과 macOS Tahoe는 RemoteImmersiveSpaces를 추가해 이를 지원합니다 원격 몰입형 공간 덕분에 CompositorLayer를 사용하여 Metal에서 Mac의 앱 코드 및 리소스로
콘텐츠를 렌더링해 Vision Pro에서 몰입형 경험으로 표시할 수 있죠
앱에서 실제 작동 모습을 볼까요
Mac 앱에서 Metal을 사용해 새로운 몰입형 공간을 빌드하고 “Preview in Vision Pro(Vision Pro에서 미리 보기)” 버튼을 추가했습니다
이를 클릭하면 대상 Vision Pro 기기를 선택하라고 표시됩니다
Vision Pro에서 연결 요청을 수락합니다
그러면 ImmersiSpace가 열려 로봇의 최신 쇼에 추가한 새로운 소품이 표시됩니다
CompositorLayer가 포함된 RemoteImmersiveSpace 장면을 추가한 결과죠
이것이 visionOS에서 표시되고 메인 무대 등 나머지 장면은 계속 Mac에서 직접 표시됩니다
CompositorLayer와 ARKitSession을 원격 Vision Pro 기기에 맞게 조정하는 방법은 “몰입감 넘치는 앱을 위한 Metal 렌더링의 새로운 기능”을 보세요
원격 몰입형 공간에 CompositorLayer를 사용하면 Metal로 몰입형 경험을 만드는 데 매우 유용합니다
하지만 CompositorLayer는 View가 아니므로 ImmersiveContent처럼 View가 필요한 컨텍스트에 사용할 수 없습니다
그래서 지금까지는 환경 변수 및 View 수정자를 CompositorLayer에 사용할 수 없었습니다
visionOS 26은 CompositorContent 빌더 유형을 새로 추가하므로 CompositorLayer에 SwiftUI를 십분 활용 가능합니다
이제 SwiftUI 보기에서처럼 환경 변수를 이용하거나 수정자를 추가하거나 상태 변수를 사용할 수 있습니다
CompositorContent는 scenePhase, openWindow 등 여러 유용한 환경 변수와 onImmersionChange 및 onWorldRecenter 같은 수정자를 도입합니다
덕분에 원격 몰입형 공간과 visionOS에서 직접 실행되는 일반 몰입형 공간 모두에서 CompositorLayer가 더 강력해집니다
CompositorContent를 사용하도록 앱을 업그레이드한 결과 사용 가능한 몰입형 공간 수정자를 다시 만나고 이를 앱에 적용할 방법을 확인할 수 있어 좋았습니다
이렇게 몰입형 공간의 새로운 기능을 알아보았습니다 세계 중심 재설정 이벤트 몰입감 스타일을 위한 새로운 맞춤화 RemoteImmersiveSpace를 통한 Mac의 몰입형 공간 그리고 CompositorContent를 보았죠
이 모든 기능으로 앱이 멋져졌습니다 제 앱 중 다른 것에도 이렇게 멋진 볼류메트릭 경험을 추가해 볼까 합니다
하지만 제 앱 중 일부는 UIKit으로 빌드되었고 UIKit은 볼륨 및 몰입형 공간을 지원하지 않습니다 하지만 장면 브리징이 있죠
장면 브리징으로 SwiftUI 볼륨 및 몰입형 공간을 추가해 기존 UIKit 앱을 3차원으로 만들 수 있습니다
Safari를 생각해 보세요 SwiftUI 보기를 사용하지만 UIKit 라이프사이클로 빌드되었죠
Safari는 새로운 공간 브라우징에 장면 브리징을 잘 활용 중입니다
이 방법도 알아보겠습니다
SwiftUI 장면을 UIKit 앱에 브리징하려면 먼저 UIHostingSceneDelegate에서 확장되는 클래스 유형을 만듭니다
이 유형으로 익숙한 장면 본문 구문을 사용해 rootScene 속성에 SwiftUI 장면을 선언할 수 있습니다
이제 다른 UIKit 장면처럼 UISceneSessionActivationRequest를 생성해 이 장면을 요청할 수 있습니다
여기서는 호스팅 대리자 클래스를 전달합니다 장면, 그리고 열려는 장면의 ID를 선언하는 역할을 하죠
이제 activateSceneSession과 함께 이 요청을 전송하면 됩니다
configurationForConnecting에 호스팅 대리자 클래스를 설정해 외부 이벤트에 응답할 수도 있고요
이 API에는 상응하는 AppKit API도 있어 SwiftUI 장면을 기존 macOS AppKit 앱에 브리징할 수 있습니다
이제 제 앱은 visionOS 26의 새 기능을 최대로 활용하고 있죠 제자리 고정 표면 스냅 그리고 Mac에서 원격 열기 등을요 얼른 친구들에게 자랑하고 싶군요
이제 여러분의 앱을 살펴보세요 장면을 감사해 제자리 고정 및 복원을 최대한 활용 중인지 확인하세요
스냅 및 클리핑 여백으로 사용자의 주변 환경에 맞게 장면을 조정하고
원격 몰입형 공간으로 Vision Pro에서 macOS 앱의 콘텐츠에 몰입 기능을 더하세요
이제 막은 내려가지만 앱에서 쇼는 한 장면 한 장면 계속됩니다 시청해 주셔서 감사합니다
-
-
4:10 - Disabling restoration
// Disabling restoration WindowGroup("Tools", id: "tools") { ToolsView() } .restorationBehavior(.disabled)
-
4:36 - Disabling restoration in UIKit
// Disabling restoration windowScene.destructionConditions = [ .systemDisconnection ]
-
5:02 - Specifying launch window
// Specifying launch window @AppStorage("isFirstLaunch") private var isFirstLaunch = true var body: some Scene { WindowGroup("Stage Selection", id: "selection") { SelectionView() } WindowGroup("Welcome", id: "welcome") { WelcomeView() .onAppear { isFirstLaunch = false } } .defaultLaunchBehavior(isFirstLaunch ? .presented : .automatic) // ... }
-
6:39 - "suppressed" behavior
// "suppressed" behavior WindowGroup("Tools", id: "tools") { ToolsView() } .restorationBehavior(.disabled) .defaultLaunchBehavior(.suppressed)
-
7:44 - Unique window
// Unique window @AppStorage("isFirstLaunch") private var isFirstLaunch = true var body: some Scene { // ... Window("Welcome", id: "welcome") { WelcomeView() .onAppear { isFirstLaunch = false } } .defaultLaunchBehavior(isFirstLaunch ? .presented : .automatic) WindowGroup("Main Stage", id: "main") { StageView() } // ... }
-
10:24 - Surface snapping
// Surface snapping @Environment(\.surfaceSnappingInfo) private var snappingInfo @State private var hidePlatform = false var body: some View { RealityView { /* ... */ } .onChange(of: snappingInfo) { if snappingInfo.isSnapped && SurfaceSnappingInfo.authorizationStatus == .authorized { switch snappingInfo.classification { case .table: hidePlatform = true default: hidePlatform = false } } } }
-
14:41 - Clipping margins
// Clipping margins @Environment(\.windowClippingMargins) private var windowMargins @PhysicalMetric(from: .meters) private var pointsPerMeter = 1 var body: some View { RealityView { content in // ... waterfall = createWaterfallEntity() content.add(waterfall) } update: { content in waterfall.scale.y = Float(min( windowMargins.bottom / pointsPerMeter, maxWaterfallHeight)) // ... } .preferredWindowClippingMargins(.bottom, maxWaterfallHeight * pointsPerMeter) }
-
16:44 - World recenter
// World recenter var body: some View { RealityView { content in // ... } .onWorldRecenter { recomputePositions() } }
-
17:58 - Progressive immersion style
// Progressive immersion style @State private var selectedStyle: ImmersionStyle = .progressive var body: some Scene { ImmersiveSpace(id: "space") { ImmersiveView() } .immersionStyle( selection: $selectedStyle, in: .progressive(aspectRatio: .portrait)) }
-
18:37 - Mixed immersion style
// Mixed immersion style @State private var selectedStyle: ImmersionStyle = .progressive var body: some Scene { ImmersiveSpace(id: "space") { ImmersiveView() } .immersionStyle(selection: $selectedStyle, in: .mixed) .immersiveEnvironmentBehavior(.coexist) }
-
20:14 - Remote immersive space
// Remote immersive space // Presented on visionOS RemoteImmersiveSpace(id: "preview-space") { CompositorLayer(configuration: config) { /* ... */ } } // Presented on macOS WindowGroup("Main Stage", id: "main") { StageView() }
-
20:48 - 'CompositorLayer' is a 'CompositorContent'
// 'CompositorLayer' is a 'CompositorContent' struct ImmersiveContent: CompositorContent { @Environment(\.scenePhase) private var scenePhase var body: some CompositorContent { CompositorLayer { renderer in // ... } .onImmersionChange { oldImmersion, newImmersion in // ... } } }
-
23:00 - Scene bridging
// Scene bridging import UIKit import SwiftUI // Declare the scenes class MyHostingSceneDelegate: NSObject, UIHostingSceneDelegate { static var rootScene: some Scene { WindowGroup(id: "my-volume") { ContentView() } .windowStyle(.volumetric) } } // Create a request for the scene let requestWithId = UISceneSessionActivationRequest( hostingDelegateClass: MyHostingSceneDelegate.self, id: "my-volume")! // Send a request UIApplication.shared.activateSceneSession(for: requestWithId)
-
-
- 0:00 - 서론
visionOS 26에서는 장면, 창, 볼륨, 몰입형 공간에 대한 새로운 API를 사용하여 보다 역동적이고 인터랙티브한 앱을 만들 수 있습니다. 새로운 기능에는 수명 주기 API, 볼류메트릭 향상, Apple Vision Pro에서 미리 볼 수 있는 ‘RemoteImmersiveSpace’, UIKit 앱에 대한 Scene Bridging API가 포함됩니다.
- 2:11 - 시작 및 고정하기
visionOS 26의 새로운 API는 앱 실행 및 장면 복원을 관리하고 보다 강력한 몰입형 경험을 만들 수 있도록 도와줍니다. 사람들은 이제 창, 볼륨, 위젯을 특정 방에 고정해 가상 콘텐츠가 물리적 환경에서 유지되도록 할 수 있습니다. 누군가 관련 방으로 돌아오면 시스템은 고정된 창을 자동으로 복원하지만, ‘restorationBehavior(.disabled)’ 수정자를 사용하여 시작 화면 또는 상황에 맞는 UI와 같은 일시적인 요소에 대한 복원을 비활성화할 수 있습니다. ‘defaultLaunchBehavior’ 수정자를 사용하여 앱 상태에 따라 표시할 창을 동적으로 선택하여 앱의 실행 동작을 사용자 지정할 수도 있습니다. 복제할 수 없는 고유한 창은 이제 Window API를 통해 지원되어 게임 창이나 화상 통화와 같은 중요한 인터페이스의 복제를 방지합니다.
- 8:15 - 볼류메트릭 개선 사항
이제 볼륨에 대한 몇 가지 주요 개선 사항이 도입됩니다. ‘SurfaceSnappingInfo’ API를 활용하면 창과 볼륨이 벽이나 테이블과 같은 물리적 표면에 스냅되는 시점을 모니터링할 수 있습니다. 이 기능을 사용하면 플랫폼을 숨기거나 소품을 조정하는 등 스냅 상태에 따라 장면을 조정할 수 있어 더욱 강력한 몰입형 경험을 제공합니다. ARKit은 사용자의 허가를 받아 앱에 스냅된 표면에 대한 세부 정보를 제공할 수 있습니다. 또한 이번 업데이트에서는 프레젠테이션 기능이 확장되어 특별한 시각적 처리를 통해 3D 콘텐츠 뒤에도 계속 표시하면서 볼륨, 장식, RealityKit 내 다양한 소스에서 생성된 프레젠테이션을 표시할 수 있습니다. ‘presentationBreakthroughEffect’ 수정자를 사용하여 시각적 처리를 사용자 정의합니다. 또한, 새로운 Clipping Margins API를 사용하면 볼륨의 장면 경계 외부에 있는 인터랙티브하지 않은 콘텐츠를 렌더링하여 폭포 또는 구름과 같은 시각적 효과를 향상시키는 동시에 주요 콘텐츠가 초점이 맞춰지고 방해받지 않도록 할 수 있습니다. ‘windowClippingMargins’ 환경 변수를 읽어 시스템에서 여백을 부여했는지 확인합니다.
- 15:58 - 몰입형 공간
이제 전 세계 재중심화 이벤트에 대응할 수 있어 사람들은 주변에서 앱의 경험을 재조정할 수 있습니다. ‘onWorldRecent’ 뷰 수정자를 사용하여 이 이벤트를 수신하고 새로운 좌표 시스템을 기반으로 위치를 다시 계산하여 저장합니다. visionOS 26에서는 몰입형 스타일을 위한 새로운 사용자 정의 기능도 제공합니다. 이제 점진적 몰입 스타일에 세로 화면 비율이 포함되어 수직 경험이 지원되고 움직임이 많은 콘텐츠 시청 시 편안함이 향상되었습니다. 혼합 몰입형 스타일을 통해 이제 콘텐츠는 시스템 환경과 완벽하게 조화를 이루어 더욱 몰입감 넘치는 시나리오를 만들 수 있습니다. 이러한 혼합을 활성화하려면 ‘immersiveEnvironmentBehavior’ 장면 수정자와 ‘coexist’ 동작을 함께 사용하세요. 이제 앱을 macOS로 가져와 ‘RemoteImmersiveSpaces’를 사용하여 Apple Vision Pro에서 몰입형 공간으로 장면을 직접 미리 볼 수 있습니다. 이 기능을 사용하면 반복 작업 및 협업이 더 빨라집니다. ‘CompositorLayer’를 사용하여 Metal이 포함된 콘텐츠를 Mac에 렌더링하고 Apple Vision Pro에 표시합니다. ‘CompositorContent’ 빌더 유형이 도입되면서 ‘CompositorLayer’에서 SwiftUI의 모든 기능을 사용할 수 있어 원격 및 로컬 모두에서 몰입형 경험을 만들고, 관리하며, 환경 변수에 액세스하고, 수정자를 추가하며, 상태 변수를 사용하기가 더 쉬워졌습니다.
- 22:16 - 장면 브리징
visionOS 26의 Scene Bridging을 사용하면 기존 UIKit 앱에 SwiftUI 볼륨 및 몰입형 공간을 추가할 수 있습니다. ‘UIHostingSceneDelegate’를 확장하면 SwiftUI 장면을 만들고 ‘UISceneSessionActivationRequest’를 사용하여 요청할 수 있어 Safari와 같은 앱에서 공간 브라우징을 구현하고 다른 앱에서 새로운 visionOS 기능을 활용하도록 지원할 수 있습니다.
- 24:01 - 다음 단계
예제 앱을 사용하면 사람들은 특정 위치에 고정되고, 표면에 스냅되며, Mac에서 원격으로 열리는 장면을 만들 수 있습니다. 여러분의 앱을 살펴보고 이와 같이 새로운 기능을 최대한 활용하는지 확인하세요.