스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
공간 컴퓨팅을 위한 SwiftUI
태양계를 둘러보고 visionOS를 위한 SwiftUI를 살펴보세요! 윈도우, 볼륨, 공간으로 완전히 새로운 앱을 만드는 방법을 알아보세요. visionOS에서 SwiftUI를 사용하는 방법을 보여 주기 위해 앱 빌드 과정을 보여드립니다. 천문학 앱을 만들어 3D 콘텐츠를 추가하고 완벽한 몰입 경험을 통해 사람들을 별로 이동시킵니다.
챕터
- 0:00 - Introduction
- 5:29 - Windows
- 14:06 - Volumes
- 20:35 - Full Spaces
리소스
관련 비디오
WWDC23
-
다운로드
♪ 부드러운 힙합 연주곡 ♪ ♪ 안녕하세요 '공간 컴퓨팅을 위한 SwiftUI'입니다 저는 Andrew고 SwiftUI 팀 엔지니어죠 공간 컴퓨팅을 기대하고 있습니다 혁신적인 앱을 구축하는 새로운 가능성을 열죠 이 세션에서는 새로운 플랫폼을 위해 멋진 앱을 만드는 방법을 알려드리고 그것을 가능하게 하는 SwiftUI의 강력한 새 기능을 소개합니다 공간 컴퓨팅을 위한 앱을 만들 때 가장 좋은 방법은 SwiftUI죠 SwiftUI와 함께 담대하고 새로운 미래로 가기 위해 볼륨과 같은 새로운 3D 기능과 전체 공간과 같은 몰입형 경험 새로운 3D 제스처와 이펙트, 레이아웃 RealityKit과의 통합 등 많은 기능을 추가했습니다 새로운 기능은 SwiftUI에만 있죠 여러분 앱의 기존 코드가 있다면 대부분 새 플랫폼에서 작동할 겁니다 새로운 코드라면 SwiftUI로 작성하는 게 제일 좋은 방법이죠 SwiftUI에 대한 깊은 믿음을 갖고 시스템을 처음부터 SwiftUI로 설계했습니다 앱의 핵심 재료인 버튼, 토글, TabView와 핵심 요소인 홈뷰와 제어 센터는 물론 익숙한 앱인 TV, Safari와 Freeform의 3D 보드와 같은 새로운 경험 Keynote의 몰입형 리허설까지 SwiftUI가 이 모든 것과 더 많은 걸 구동합니다 SwiftUI를 통해 앱의 사용자 인터페이스를 묘사하고 시스템이 지능적인 기본값을 선택하게 하여 새로운 개념을 한 번만 배우면 어디에든 적용할 수 있습니다 새로운 플랫폼에서는 더욱 유용해졌죠 기존 SwiftUI 지식은 전혀 새로운 모습과 느낌으로 매끄럽게 전환됩니다 버튼을 예로 들죠 시스템의 버튼은 여러분께 익숙한 다른 플랫폼의 버튼과 공통점이 많습니다 예를 들어 macOS에는 버튼의 기본값이 테두리가 있는 형식이죠 하지만 버튼에 핵심적인 차이점이 있으며 새로운 플랫폼의 이디엄에 맞춘 것입니다 시뮬레이터를 확대하여 자세히 봅시다 이렇게 테두리가 있는 버튼은 활기 있는 소재의 배경을 사용하죠 모든 버튼은 시선, 손 포인터 입력에 반응하여 분명한 호버 효과를 주죠 또한 크기를 줄여 눌렀을 때 음향 피드백을 제공하기도 합니다 내비게이션 바에 있는 버튼의 경우 사용자가 버튼을 바라보면 자동으로 툴팁과 버튼의 레이블을 표시합니다 TabView를 살펴보면 앱의 가장자리에 위치해 있는데 빠르고 쉬운 탐색을 제공하면서 앱 콘텐츠를 방해하지 않죠 TabView가 확장되면 자세한 정보를 표시하는데 보기만 해도 탭의 레이블을 보여 줍니다 TabView는 플랫폼의 독특한 특성을 SwiftUI가 활용하는 걸 보여 주는 예시죠 여러분이 바라보는 곳의 인터페이스가 반응하는 것처럼요 이렇게 세심한 설계를 앱의 핵심 요소인 탐색과 프레젠테이션 컨트롤과 상호작용에 적용했죠 지능적인 플랫폼 기본값을 통해 앱이 플랫폼에 처음부터 적응할 수 있게 하여 여러분 앱의 핵심 기능에 집중하도록 했습니다 거기서 멈추지 않았죠 SwiftUI는 새로운 API 일체를 포함하며 3D 환경에 맞춤 제작됐습니다 API는 장면에서 시작하죠 SwiftUI의 앱은 장면으로 구성되며 장면은 뷰로 구성됩니다 장면은 가장 높은 단계의 앱 진입 지점을 구성하죠 WindowGroup이 1개 이상의 윈도우를 표시하는 것처럼요 공간 컴퓨팅에서는 앱을 구성하는 장면이 세 가지입니다 윈도우, 볼륨, 전체 공간이죠 윈도우는 전통적이고 익숙한 인터페이스 구축에 좋은데 Safari나 Freeform처럼 풍성하고 완성된 앱부터 진입 지점의 역할을 하는 메뉴나 마음 챙기기와 같은 몰입형 경험에 어울리죠 볼륨은 새로운 3D 윈도우의 형식으로 한정된 공간 안에 오브젝트나 경험을 나타내도록 설계됐습니다 볼륨은 다른 앱과 함께 표시할 수 있어 가벼운 3D 경험을 구축하기에 알맞으며 Quick Look을 통해 3D 모형을 미리 보거나 친구와 FaceTime으로 보드게임을 할 수 있죠 마지막으로 전체 공간이 있는데 풍성한 몰입형 앱을 만드는 새로운 방식입니다 전체 공간일 땐 여러분 앱이 완벽히 제어하며 다른 앱의 윈도우를 숨겨 콘텐츠를 어디에든 가져갈 수 있죠 전체 공간이 실제 세상을 보완하여 사람들이 주변 환경과 교감하게 할 수도 있고 놀랍고 새로운 경험의 완전한 몰입감을 선사할 수도 있죠 Keynote의 사실적인 리허설부터 짜릿하고 새로운 게임까지 전체 공간은 새로운 가능성의 세계를 엽니다 이런 장면 종류는 함께 사용되도록 설계했죠 고유의 유스 케이스에 맞춰 설계됐으며 나름대로 혼합하여 앱에 맞게 활용할 수 있습니다 윈도우에서 볼륨이 나오게 하여 건축 모형의 간단한 3D 모형을 미리 보여 줄 수도 있죠 아니면 볼륨에서 전체 공간을 제공해 사용자에게 완전한 몰입 경험을 선사할 수도 있죠 같은 종류의 장면을 여러 개 사용할 수도 있는데 앱을 여러 부분으로 정리할 수 있으며 사람들이 각 윈도우를 여닫을 수 있습니다 이제 각 장면을 더 자세히 알아보죠 먼저 윈도우입니다 저는 천문학을 좋아하고 별을 구경하는 동료와 함께 앱을 제작하여 다른 사람이 지구와 태양계를 배우는 걸 도우려고 하죠 앱 제작의 시작이 좋은데 이렇게 멋진 소개 페이지를 윈도우에 만들었습니다 하지만 더 큰 계획을 세웠죠 윈도우를 보여 주기 위해 다른 플랫폼에서도 작동하는 WindowGroup API를 사용합니다 WindowGroup을 사용하여 다수의 윈도우 생성을 자동으로 지원하는데 macOS나 iPadOS와 같죠 앱에 새로운 기능을 추가하려는 중인데 태양계와 우주에 관한 재미있는 사실을 수집할 수 있는 라이브러리를 통해 동료 우주 학도들이 계속 배우도록 도와주려고 합니다 하지만 앱의 어느 곳에 이 기능을 넣어야 할지 막힌 상태죠 그 질문에 답하기 위해 윈도우의 구조를 기본 요소로 나누어서 생각해 봅시다 시스템의 윈도우는 아름다운 유리 배경으로 시작하여 사람들이 주변 환경을 계속 볼 수 있도록 하고 콘텐츠의 가독성을 높이죠 윈도우 안에 다른 플랫폼에서 익숙한 탐색 컨테이너를 사용할 수 있습니다 예를 들어 TabView를 이용해 앱의 최상위 요소를 정리하여 윈도우의 왼쪽에 표시할 수 있죠 또한 탐색 스택과 분할 화면을 이용하여 앱의 계층 구조를 정리하고 풍부한 정보를 표시하고 iPadOS와 비슷한 구조를 따를 수 있죠 목록도 정보를 정리할 수 있는 강력한 도구로 아름답고 새로운 스타일과 잘 어울립니다 앱에 상호작용성을 추가하기 위해 기본 컨트롤인 버튼, 토글, 피커를 사용할 수 있죠 라이브러리 뷰에 TabView를 적용하면 앱의 최상위 진입 지점에 항상 접근할 수 있어 탭 한 번으로 지식을 얻을 수 있죠 그러려면 TabView에 콘텐츠를 담고 tabItem에는 탭 별로 레이블을 제공합니다 멋지네요 TabView의 모습이 독특한데 윈도우의 테두리 밖에 매달려 있죠 TabView는 SwiftUI에 추가한 새로운 개념의 좋은 예입니다 바로 오너먼트죠 오너먼트는 보완이 되는 뷰를 앱 윈도우에 맞춰 배치할 수 있고 윈도우의 경계 밖으로 확장할 수 있습니다 이렇게 하면 윈도우에 속하지 않고 추가 컨트롤을 표시할 수 있고 앱의 콘텐츠를 방해하지 않죠 직접 오너먼트를 만들 수 있는 .ornament 모디파이어도 있습니다 제가 작업했던 라이브러리 상세 뷰를 살펴보도록 하죠 이 페이지의 작업을 시작했는데 몇 개의 섹션으로 나눴습니다 요약 텍스트와 흥미로운 통계 가로로 스크롤할 수 있는 재미있는 사실이죠 근데 이 섹션에 빠진 게 있습니다 그게 뭔지 알아내기 위해 앱의 소재를 자세히 살펴보죠 윈도우는 기본적으로 유리 배경에 고정되어 있습니다 유리를 사용하여 새롭고 활기 있는 소재를 통해 앱의 시각적 계층 구조를 제공할 수 있게 했죠 유리는 환경에 자동으로 적응하여 어디에 사용하든 콘텐츠의 가독성을 유지하고 어떤 설정에서도 앱이 멋져 보이도록 합니다 그 말의 의미는 이 플랫폼에서 앱의 다크 및 라이트 모드가 없다는 거죠 소재가 힘든 일을 대신해 줍니다 소재를 활용하여 라이브러리 탭이 어울리도록 하고 가독성을 높이기 위해 통계표부터 작업해 보죠 지금은 통계표 섹션이 제목과 표로 구성된 VStack입니다 소재를 적용한 배경을 통해 이 카드가 돋보이게 합시다 그렇게 하려고 보통 소재를 사용했는데 활기 있고 어두운 배경을 윈도우의 유리 위에 추가하여 카드의 형태를 고정하고 상세 텍스트의 가독성을 개선합니다 모서리가 둥근 사각형에 여백을 추가하여 카드의 느낌을 잘 살렸죠 재미있는 사실 카드도 똑같이 적용하겠습니다 훨씬 낫네요 이제 재미있는 사실 카드로 가서 확대하여 자세히 보죠 이 카드는 버튼으로 만들어서 카드를 눌렀을 때 사실에 관한 자세한 설명을 보여 줍니다 커스텀 버튼 스타일을 통해 멋진 소재의 배경을 제공했죠 버튼의 콘텐츠는 제목과 상세 내용 각주로 구성돼 있어 사람들이 탭하여 자세히 보는 걸 유도합니다 '더 알아보기' 텍스트가 조금 무거워 보이는군요 수정하기 위해 부수적 배경 스타일을 텍스트에 적용하죠 부수적 스타일은 배경 소재에 자동으로 맞춰 활기를 적용하는 기능으로 콘텐츠에 시각적인 무게감을 주면서 부수적 텍스트의 가독성도 유지했습니다 계층 구조의 형태 스타일은 상대적인 시각적 무게감을 표현하기에 좋고 SwiftUI가 자동으로 맞춰 콘텐츠를 기반으로 특정 소재를 사용하죠 이러한 기능과 다른 형태 스타일과 소재 API를 이용하여 여러분의 앱에 멋진 인터페이스를 만들어 어떤 플랫폼과 맥락에서도 멋져 보이게 하세요 다음은 상호작용을 살펴보죠 공간 컴퓨팅을 통해 앱과 상호작용하는 새로운 방식이 생겼습니다 가장 일반적인 형태의 상호작용은 특정 요소를 보고 간접적인 핀치 제스터로 탭을 수행하는 거죠 손을 이용하여 직접 상호작용할 수도 있는데 손을 뻗어 터치하는 겁니다 연결된 트랙패드이나 손 제스처를 통해 포인터를 사용하여 정확한 입력이 가능하죠 시스템과 연결된 하드웨어 키보드도 사용할 수 있고 키보드 단축키와 집중 모드를 지원하며 핵심 모디파이어를 통해 앱의 생산성을 올립니다 마지막으로 시스템이 다른 플랫폼에서 익숙한 손쉬운 사용 기술을 지원하는데 VoiceOver나 스위치 제어 같은 기능이 있죠 가장 좋은 건 SwiftUI가 처음부터 손쉬운 사용이 가능하게 만들어져 여러분 대신 힘든 일을 하여 모든 사람이 여러분의 앱을 통해 똑같은 경험을 할 수 있다는 겁니다 SwiftUI에서 익숙한 제스처는 모두 잘 작동하죠 TapGesture나 DragGesture 같은 모든 형태의 상호작용에 자동으로 맞춥니다 새로운 제스처를 추가하여 풍성한 3D 상호작용을 가능하게 했는데 RotateGesture3D를 통해 두 손이나 연결된 트랙패드로 3차원으로 회전할 수 있죠 물론 다른 플랫폼에 있던 손쉬운 사용 API와 기술도 잘 작동합니다 VoiceOver, 로터와 Dynamic Type과 색상 반전이 있죠 이 중 많은 기능은 플랫폼을 위해 재설계됐습니다 Dwell Control은 오직 시선을 이용하여 앱을 탐색할 수 있는 기능이죠 이러한 기능을 자세히 알아보고 모두가 손쉽게 사용할 수 있는 앱을 만들고 싶다면 '손쉽게 사용할 수 있는 공간 경험 만들기'를 살펴보세요 공간 컴퓨팅의 상호작용을 쉽고 직관적으로 해 주는 핵심 툴은 호버 효과입니다 예를 들어 상호작용 뷰를 보는 것만으로도 시스템이 미묘한 강조 효과를 뷰 위에 띄워 피드백을 제공하고 상호작용하게 해 주죠 이 효과는 입력 방식에 따라 자동으로 맞춰져 상호작용을 한다는 확신을 주게 됩니다 호버 효과를 유용하게 사용하면 반응적인 피드백은 물론 목표 선택을 돕죠 호버 효과는 사람들이 보고 있는 것에 반응할 수 있는 유일한 방식입니다 이 효과는 여러분 앱의 프로세스 밖에서 적용되어 개인 정보를 보호하죠 호버 효과는 대부분의 컨트롤인 버튼, 토글, 텍스트 영역 등에 자동으로 적용됐습니다 SwiftUI에서 제공하는 자체 스타일을 사용하면 여러분 앱에도 자동으로 이런 효과가 나타나죠 커스텀 컨트롤이나 스타일을 사용할 때는 호버 효과를 넣어 반응성과 쉬운 사용성을 느끼게 하세요 제 앱에 추가했던 재미있는 사실 카드로 돌아가 호버 효과가 어떻게 작동하는지 알아봅시다 카드의 모습이 보기 좋습니다 하지만 큰 문제가 있죠 커스텀 ButtonStyle을 사용했기 때문에 제가 호버 효과를 직접 제공해야 합니다 이게 없으면 카드가 상호작용한다는 피드백을 줄 수 없죠 다행인 건 수정이 정말 쉽다는 겁니다 저의 ButtonStyle은 여유 공간을 추가하고 버튼의 레이블에 커스텀 소재 배경을 적용했죠 빠져 있는 호버 효과를 수정하려면 ButtonStyle에 hoverEffect 모디파이어를 추가하면 됩니다 이는 맥락에 적절한 효과를 자동으로 선택 하죠 이 경우에는 하이라이트 효과입니다 더 자세히 보죠 제가 이 카드 버튼을 보면 섬세한 하이라이트 효과가 나타나서 상호작용이 된다는 걸 명확하게 드러내죠 제 버튼의 배경 모양과 효과가 자동으로 맞춰져서 재미있는 사실을 배울 수 있는 기회를 놓치지 않게 했습니다 멋진 윈도우 앱을 만드는 방법이 더 많이 있죠 자세히 알아보려면 '공간 컴퓨팅을 위한 윈도우 앱 개선하기'를 통해 멀티플랫폼 앱을 업데이트하는 방식을 알아보고 오너먼트를 추가하는 방법을 배우고 호버 효과, 소재 등을 자세히 살펴보세요 이제 앱을 다음 차원인 볼륨으로 가져가 보죠 함께 별을 보는 동료들이 우리가 고향이라고 부르는 행성을 새롭게 바라보면 좋겠습니다 볼륨은 그걸 위한 완벽한 기능이죠 앱에 볼륨을 추가하려면 메인 윈도우에 사용했던 WindowGroup 장면을 쓰고 입체적인 윈도우 스타일을 명시하면 됩니다 또한 윈도우의 3D 크기에 대한 기본값을 제공하여 콘텐츠에 맞게 설정하죠 이제 볼륨을 시뮬레이터에서 확인해 봅시다 볼륨의 콘텐츠 안에 RealityKit의 Model3D API를 사용하여 디자이너가 제작한 3D 지구 모델을 표시하죠 Model3D 덕분에 앱에 3D 콘텐츠를 추가하는 게 정말 쉽습니다 Model3D는 이미지와 비슷하여 3D 콘텐츠를 쉽게 로딩하고 표시할 수 있죠 이미지와 달리 Model3D는 비동기적으로 로딩하는데 3D 콘텐츠를 로딩하고 표시하는 데 시간이 걸리기 때문입니다 AsyncImage 뷰와 비슷하게 Model3D가 자동으로 콘텐츠가 로딩될 때까지 플레이스홀더 뷰를 표시하죠 아니면 완벽히 제어해서 자체 플레이스홀더를 표시할 수도 있습니다 여기서 주목해야 하는 건 Model3D가 또 다른 SwiftUI 뷰라는 거죠 3D를 SwiftUI 앱에 가져오는 건 여러분에게 익숙한 개념을 바탕으로 하는 것이며 레이아웃 시스템, 시각 효과 제스처 등의 자연스러운 확장입니다 이 예시를 통해 이해해 보도록 하죠 실감 나는 지구를 만들기 위해 컨트롤을 추가하려고 합니다 이미 제어판 UI를 만들었고 지구 모형 앞에 배치하려고 하죠 그렇게 하려면 ZStack 레이아웃을 이용할 수 있습니다 ZStack과 같은 레이아웃은 여러분 콘텐츠의 깊이를 자동으로 인지하고 폭과 높이도 마찬가지죠 Model3D는 기본적으로 콘텐츠가 3차원에 맞도록 크기를 설정했으며 2D의 이미지와 같습니다 사실 레이아웃 시스템 전체가 여러분 콘텐츠의 깊이와 가용 공간을 인지하고 그것에 맞게 레이아웃을 조정하죠 여러분의 앱에서 깊이감을 제어할 수 있는 새로운 모디파이어도 있습니다 새로운 padding3D 모디파이어로 SwiftUI 뷰의 앞면과 뒷면 사이에 공간을 추가하여 컨트롤에 여유 공간을 마련해 주는 거죠 볼륨은 모든 각도에서 볼 수 있게 설계되어 앱의 콘텐츠가 모든 차원에서 어떻게 배치됐는지 생각해 봐야 합니다 마지막으로 컨트롤이 진짜처럼 보이게 하려면 glassBackgroundEffect 모디파이어를 사용하여 표준 윈도우에서 사용했던 아름다운 유리 재질을 부여하도록 하죠 컨트롤이 아주 좋아 보입니다 3D 지구에 추가하고 싶은 건 쉬운 방법으로 지구를 돌려 무작위의 장소로 가는 거죠 제가 다음 휴가지를 정할 때 즐겨 사용하는 방법입니다 그렇게 하려면 Model3D에 3D 회전 효과를 추가해야 하는데 이 경우에는 y축에 해당하죠 state 변수를 사용하여 회전 상태를 추적하겠습니다 또 탭 제스처를 추가하고 회전이 시작되면 무작위로 회전을 바꾸고 통통 튀는 애니메이션을 추가하도록 하죠 이제 돌려 봅시다 네, 서반구가 나왔군요 여름 계획의 선택지가 줄었습니다 여기서 우리가 지구에 적용한 회전 효과가 진정한 3D라는 거죠 기하 효과를 업그레이드했고 새로운 3D 기능의 사용법은 이미 알고 있죠 규모, 오프셋 커스텀 3D 트랜스폼을 포함합니다 이것도 지구를 살펴보는 멋진 방식이지만 더 낫게 만들 수 있죠 이를 위해 RealityView를 사용하겠습니다 새로운 SwiftUI 뷰로 RealityKit의 모든 능력에 쉽게 접근할 수 있죠 RealityView를 통해 클로저를 제공하여 RealityKit 콘텐츠를 로딩하고 만듭니다 지구 모형을 표시하기 위해 ModelEntity를 생성하고 로딩을 기다리도록 하죠 로딩이 끝나면 RealityView의 콘텐츠에 추가해 표시할 겁니다 async-await를 RealityView의 클로저에서 직접 사용할 수 있죠 Model3D처럼 콘텐츠가 로딩될 때까지 자동으로 플레이스홀더를 표시합니다 이제 지구 개체를 로딩했으니 RealityKit API의 풍부한 라이브러리에 접근하여 콘텐츠를 빛나게 할 시간이죠 여기서는 지구에 조명을 추가하여 밝은 느낌을 주려고 합니다 이미 RealityKit 코드를 작성하여 이미지 기반의 조명을 추가하려고 했는데 그걸 여기에 호출하죠 이제 정말 멋지네요 RealityView와 관련하여 다룰 내용이 많죠 RealityView를 통해 사용자 인터페이스와 풍부한 3D 경험을 혼합하는 것이 어느 때보다 쉬워졌습니다 RealityKit은 풍부한 행동을 추가하게 해 주는데 커스텀 소재와 셰이더 물리와 복잡한 애니메이션 등 아주 많죠 RealityView와 RealityKit을 자세히 알아보고 싶다면 우리가 준비한 세션에서 내용을 더 깊이 다룹니다 'RealityKit으로 공간 경험 구축하기'에서 더 자세한 내용을 살펴보세요 지금은 RealityView의 2개의 멋진 기능에 초점을 맞추죠 제스처와 첨부입니다 RealityView에서는 SwiftUI의 제스처가 자동으로 작동하여 생동감 있는 3D 콘텐츠를 어느 때보다 쉽게 만들 수 있죠 아까 추가했던 탭 제스처에 더하여 제가 탭한 곳에 마커를 배치하고 싶습니다 그래야 정확한 여행지를 알 수 있으니까요 이럴 땐 SpatialTapGesture를 사용하면 탭한 지점의 3D 위치를 알 수 있죠 지구 개체 중 어느 곳에 탭했는지 식별하기 위해 targetedToAnyEntity 제스처 모디파이어를 사용하겠습니다 그럼 제가 원하는 정보를 제공받죠 제가 탭한 개체와 그 개체의 상대적 위치를 통해 제 핀의 위치를 찾을 수 있습니다 지구에 핀을 표시하려면 RealityView 첨부를 사용할 수 있죠 첨부는 커스텀 SwiftUI 뷰와 RealityKit 개체를 혼합하는 작업에 유용합니다 다른 SwiftUI 뷰를 RealityView의 첨부 클로저에 직접 추가하여 RealityView 어느 곳이든 RealityKit 개체를 배치할 수 있게 되죠 여기 핀 첨부를 추가하고 태그를 부여하여 식별 및 사용할 겁니다 그리고 update 클로저에서 제 첨부의 개체를 찾은 뒤 RealityView의 콘텐츠에 추가하여 표시할 거죠 그리고 그 개체를 탭 위치에 맞게 배치할 겁니다 다시 한번 돌려 보죠 이제 짐을 싸야겠네요 멋진 입체적 앱을 만들 수 있는 가능성에 들떠 있고 전용 세션을 준비하여 SwiftUI 앱을 새로운 차원으로 가져가는 걸 돕고자 합니다 SwiftUI와 RealityKit으로 풍부한 3D 상호작용을 구축하고 레이아웃에 깊이감을 추가하는 내용이 있죠 마지막으로 앱을 전체 공간으로 가져갈 건데 SwiftUI로 풍성하고 몰입감 있는 3D 경험을 만드는 새로운 방식입니다 전체 공간에서는 여러분이 완벽히 제어하죠 콘텐츠를 환경의 아무 곳에나 배치하여 창의적인 새로운 방식으로 실제 환경을 보완할 수 있죠 아니면 사람을 완벽히 에워싸서 주변 환경을 숨기고 놀라운 경험을 만들 수도 있습니다 이제 우주 공간으로 가서 태양계를 만나보죠 이미 메인 윈도우의 페이지를 제작하여 우주 비행사를 꿈꾸는 동료들에게 우주를 볼 수 있도록 했습니다 이제 저의 공간을 만들고 이 버튼을 통해 접속할 수 있게 해야 하죠 앱에 전체 공간을 추가하려면 새로운 ImmersiveSpace 장면을 추가하면 됩니다 WindowGroups 때와 같죠 안에는 우주 콘텐츠에 대한 루트 뷰를 제공합니다 또한 공간의 ID를 제공하여 메인 윈도우에서 프로그램적으로 열 수 있도록 하죠 이 공간을 열기 위해 새롭게 추가된 openImmersiveSpace 환경 액션을 사용합니다 버튼 안에 해당 액션을 호출하고 공간의 ID를 통과시키죠 그리고 이렇게 이동했습니다 시작이 좋군요 이전에는 할 수 없던 방식으로 지구를 가까이 볼 수 있어 진짜 같은 구름처럼 풍성한 디테일을 감상할 수 있습니다 근데 뭔가 빠져 있죠 이 공간에 완벽히 몰입하고 싶습니다 그러려면 공간의 강력한 툴을 사용해야 하죠 몰입 스타일입니다 전체 공간에는 여러 개의 몰입 스타일이 있어서 스타일 사이를 바로 전환할 수 있죠 혼합 몰입은 우주의 콘텐츠가 실제 세상과 공존하여 가벼운 경험에 잘 맞고 사람들의 공간을 보완합니다 완벽한 몰입은 여러분의 앱이 완벽한 몰입 상태가 되어 사람들의 주변 공간을 가리고 놀라운 세상으로 이동시키죠 점진적 몰입은 중간 상태로 사람들이 실제 세상과 주변에 인지할 수 있도록 합니다 점진적 몰입을 통해 사람들이 기기에 있는 Digital Crown을 이용하여 자신에게 맞는 몰입도를 조절할 수 있죠 제 앱이 완벽히 다른 세상처럼 느껴지려면 완벽한 몰입 상태가 잘 맞을 것 같습니다 그렇게 하려면 ImmersiveSpace 장면으로 돌아가서 새로운 immersionStyle 모디파이어를 추가하고 완벽한 몰입 스타일을 선택하죠 여기서 지원하는 스타일의 목록을 제공하고 현재의 선택을 제공하여 즉석에서 스타일을 바꿀 수 있습니다 완벽한 몰입을 사용할 때는 시스템이 사람들의 실제 주변 환경을 완전히 가리죠 그래서 가상 환경을 제공하여 앱을 사용하는 사람들에게 몰입감을 줘야 합니다 그럴 때 우주만큼 좋은 환경이 또 있을까요? 우리와 환경으로 별에 닿아 새로운 RealityView가 별의 공간을 보여 줍니다 RealityView의 make 클로저 안에 Starfield 개체를 로딩하고 준비가 되면 RealityView의 콘텐츠에 추가할 거죠 그리고 Starfield 뷰를 SolarSystem의 내용에 Earth, Sun과 함께 추가할 겁니다 확대하여 더 자세히 보죠
이제 완전히 빠져든 기분입니다
저의 우주를 다음 단계로 높이려면 ARKit을 사용할 수 있죠 ARKit은 시스템과 통합된 강력한 프레임워크로 사람들의 실제 주변 환경을 실시간으로 이해하게 해 줍니다 세계 추적이나 장면 이해 같은 API를 통해 실제 세계의 표면에 콘텐츠를 배치할 수 있죠 또한 손 추적이라는 놀라운 새 도구로 커스텀 손 제스처를 구축하고 실감 나는 물리를 이용한 콘텐츠와 상호작용하고 더 많은 것을 가능케 합니다 ARKit을 공간에 이용하는 방법이 궁금하다면 '공간 컴퓨팅을 위한 ARKit'을 확인해 보세요 ARKit을 통해 제 앱에 적용할 새로운 가능성이 기대되어 제가 작업 중이던 기능을 하나 더 소개하려고 합니다 ARKit을 제 공간에 통합하여 지구를 소환하는 새로운 손 제스처를 추가할 수 있었죠 이제 세상을 손바닥에 쥘 수 있습니다 전체 공간을 활용하는 방법은 무궁무진하죠 여러분이 공간으로 향할 수 있는 다른 세션도 준비했습니다 'SwiftUI로 윈도우 너머로 이동하기'를 통해 공간의 기본 원리를 자세히 알아보고 주변 환경에 효과 추가하기 가상 손 표시하기 SharePlay에 통합하기 등 고급 기능을 배워 보세요 여러분의 공간에서 콘텐츠 렌더링을 완벽히 제어하고 싶다면 Metal과 함께 CompositorServices 프레임워크를 사용하면 됩니다 '몰입형 앱을 위한 Metal'에서 더 알아보세요 여러분이 SwiftUI로 만들 놀라운 앱을 어서 보고 싶습니다 아름다운 윈도우부터 전혀 새로운 차원의 볼륨과 전체 공간의 몰입형 경험까지 새로운 가능성이 여러분을 기다립니다 더 깊이 알아보고 싶다면 더 많은 세션들이 준비되어 있습니다 '공간 디자인의 원리'에서는 멋진 앱을 설계하는 방법을 배울 수 있고 기존의 UIKit 코드를 플랫폼으로 가져오고 싶다면 '공간 컴퓨팅을 위한 UIKit'을 확인해 보세요 시청해 주셔서 감사합니다 재미있는 공간 컴퓨팅 되세요 ♪
-
-
2:02 - Button
Button("Of course") { // perform action }
-
2:41 - Toggle Favorite
Toggle(isOn: $favorite) { Label("Favorite", systemImage: "star") }
-
2:48 - TabView
TabView { DogsTab() .tabItem { Label("Dogs", systemImage: "pawprint") } CatsTab() .tabItem { Label("Cats", image: "cat") } BirdsTab() .tabItem { Label("Birds", systemImage: "bird") } }
-
3:37 - World App
@main struct WorldApp: App { var body: some Scene { WindowGroup("Hello, world") { ContentView() } } }
-
7:03 - World TabView
@main struct WorldApp: App { var body: some Scene { WindowGroup("Hello, world") { TabView { Modules() .tag(Tabs.menu) .tabItem { Label("Experience", systemImage: "globe.americas") } FunFactsTab() .tag(Tabs.library) .tabItem { Label("Library", systemImage: "book") } } } } }
-
8:42 - Stats Grid Section
VStack(alignment: .leading, spacing: 12) { Text("Stats") .font(.title) StatsGrid(stats: stats) .padding() .background(.regularMaterial, in: .rect(cornerRadius: 12)) }
-
9:23 - Fun Fact Button
Button(action: { // perform button action }) { VStack(alignment: .leading, spacing: 12) { Text(fact.title) .font(.title2) .lineLimit(2) Text(fact.details) .font(.body) .lineLimit(4) Text("Learn more") .font(.caption) .foregroundStyle(.secondary) } .frame(width: 180, alignment: .leading) } .buttonStyle(.funFact)
-
13:15 - FunFactButtonStyle
struct FunFactButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .padding() .background(.regularMaterial, in: .rect(cornerRadius: 12)) .hoverEffect() .scaleEffect(configuration.isPressed ? 0.95 : 1) } }
-
14:17 - Globe Volume
@main struct WorldApp: App { var body: some Scene { WindowGroup { Globe() } .windowStyle(.volumetric) .defaultSize(width: 600, height: 600, depth: 600) } }
-
14:36 - Model3D
import SwiftUI import RealityKit struct Globe: View { var body: some View { Model3D(named: "Earth") } }
-
15:40 - Globe with rotation and controls
struct Globe: View { @State var rotation = Angle.zero var body: some View { ZStack(alignment: .bottom) { Model3D(named: "Earth") .rotation3DEffect(rotation, axis: .y) .onTapGesture { withAnimation(.bouncy) { rotation.degrees += randomRotation() } } .padding3D(.front, 200) GlobeControls() .glassBackgroundEffect(in: .capsule) } } func randomRotation() -> Double { Double.random(in: 360...720) } }
-
17:30 - RealityView
RealityView { content in if let earth = try? await ModelEntity(named: "Earth") { earth.addImageBasedLighting() content.add(earth) } }
-
18:57 - RealityView Gesture
struct Earth: View { @State private var pinLocation: GlobeLocation? var body: some View { RealityView { content in if let earth = try? await ModelEntity(named: "Earth") { earth.addImageBasedLighting() content.add(earth) } } .gesture( SpatialTapGesture() .targetedToAnyEntity() .onEnded { value in withAnimation(.bouncy) { rotation.degrees += randomRotation() animatingRotation = true } completion: { animatingRotation = false } pinLocation = lookUpLocation(at: value) } ) } }
-
19:34 - RealityView Attachments
struct Earth: View { @State private var pinLocation: GlobeLocation? var body: some View { RealityView { content in if let earth = try? await ModelEntity(named: "Earth") { earth.addImageBasedLighting() content.add(earth) } } update: { content, attachments in if let pin = attachments.entity(for: "pin") { content.add(pin) placePin(pin) } } attachments: { if let pinLocation { GlobePin(pinLocation: pinLocation) .tag("pin") } } .gesture( SpatialTapGesture() .targetedToAnyEntity() .onEnded { value in withAnimation(.bouncy) { rotation.degrees += randomRotation() animatingRotation = true } completion: { animatingRotation = false } pinLocation = lookUpLocation(at: value) } ) } }
-
21:11 - ImmersiveSpace
@main struct WorldApp: App { var body: some Scene { // (other WindowGroup scenes) ImmersiveSpace(id: "solar-system") { SolarSystem() } } }
-
21:25 - Open ImmersiveSpace Action
@Environment(\.openImmersiveSpace) private var openImmersiveSpace Button("View Outer Space") { openImmersiveSpace(id: "solar-system") }
-
22:50 - ImmersionStyle
@main struct WorldApp: App { @State private var selectedStyle: ImmersionStyle = .full var body: some Scene { // (other WindowGroup scenes) ImmersiveSpace(id: "solar-system") { SolarSystem() } .immersionStyle(selection: $selectedStyle, in: .full) } }
-
23:17 - Starfield
struct Starfield: View { var body: some View { RealityView { content in let starfield = await loadStarfield() content.add(starfield) } } }
-
23:28 - SolarSystem
struct SolarSystem: View { var body: some View { Earth() Sun() Starfield() } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.