스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
SwiftUI로 TVML 앱 마이그레이션하기
모든 Apple 플랫폼에서 멋진 앱을 빌드할 수 있게 해주는 SwiftUI는 tvOS 18을 사용하여 콘텐츠를 Apple TV에서 선보이기 위한 최적의 툴킷이기도 합니다. SwiftUI에서 TVMLKit을 사용하여 친숙한 레이아웃 및 제어 기능을 제공하는 방법과 팁 및 모범 사례를 알아보세요.
챕터
- 0:00 - Introduction
- 3:54 - Lockups
- 5:12 - Shelves
- 7:35 - Catalogs
- 11:07 - Search
리소스
관련 비디오
WWDC24
-
다운로드
안녕하세요, ’SwiftUI로 TVML 앱 마이그레이션하기’를 시작합니다 저는 tvOS용 SwiftUI 업무를 담당하는 엔지니어 Jim입니다 이 세션에서는 SwiftUI로 tvOS 앱을 제작할 수 있는 멋진 기능을 보여 드리고 이를 사용해 TVML로 빌드한 앱과 같은 앱을 빌드하는 방법을 설명하려고 합니다 tvOS 18에서는 SwiftUI에 여러분이 즐겨 사용하는 셋톱 플랫폼용으로 모든 기능을 갖춘 풍부한 미디어 카탈로그 및 스트리밍 앱을 빌드하는 데 필요한 모든 것이 포함되어 있습니다 이를 고려해 Apple은 공식적으로 TVMLKit 지원을 중단합니다 여전히 tvOS의 일부로 사용할 수 있지만 더 이상 플랫폼과 함께 성장하지 않습니다 Apple이 tvOS를 출시했던 2015년에는 스트리밍 미디어 환경이 현재의 형태와 크게 달랐습니다 스트리밍 앱은 웹페이지로 가장 자주 구현되었으며 콘텐츠 제공업체 개발 리소스는 웹에 초점을 맞췄습니다 Apple은 이 동일한 리소스를 위한 방법으로 TVMLKit을 지원했고 멋진 Apple TV용 앱을 생성하는 전문 지식을 제공했습니다
현재 우리는 iPhone이나 iPad 셋톱 박스를 통한 TV 컴퓨터, Apple Vision Pro 같은 기기에서 콘텐츠를 소비합니다 스트리밍 미디어 카탈로그는 이러한 플랫폼에 맞춤화된 도구를 사용해 빌드되는 네이티브 앱을 통해 액세스됩니다 이러한 앱은 고객에게 더 개인적이고 익숙한 경험을 제공하며, 기기의 자연스러운 상호작용 및 프레젠테이션 스타일을 유지하면서 고유한 스타일을 구현할 수 있게 합니다 SwiftUI는 이러한 각 플랫폼에 맞춤화된 네이티브 코드 및 UI를 생성하는 툴킷을 제공하며 이 코드 및 UI는 모두 동일한 언어로, 동일한 구성요소를 사용해 빌드됩니다 이들 중 하나에서 사용되는 개발 리소스와 전문 지식은 나머지 플랫폼으로 깔끔하게 변환됩니다
Apple TV에서는 네이티브 tvOS UI를 생성하며 특히 멋진 tvOS 앱 생성을 목표로 하는 디자인 요소를 사용합니다 TVMLKit으로 할 수 있던 모든 것은 이제 SwiftUI에서 가능하며 해마다 한층 더 큰 유연성과 멋진 새로운 기능이 추가됩니다 앱은 Swift로 작성되는데 이는 iOS, iPadOS, macOS, watchOS 그리고 이제 visionOS용 앱 빌드에 사용하는 것과 동일한 언어입니다
따라서 기존의 네이티브 개발 리소스를 사용하므로 iPad용 앱을 빌드하는 동일한 도구와 기법을 Apple TV용 앱을 빌드하는 데 사용할 수 있습니다 또 그 어느 때보다 많은 tvOS 앱을 다른 앱을 만드는 데 사용하는 정확히 동일한 코드로부터 빌드할 수 있습니다
SwiftUI로 콘텐츠 카탈로그 앱을 조합하는 기본 사항을 안내하겠습니다 Apple 플랫폼 중 하나를 위한 앱을 빌드하는 데 사용하는 동일한 구성요소를 어떻게 Apple TV 사용자에게 즉시 익숙한 인터페이스와 동작을 생성하는 데 사용할 수 있는지 알아봅니다 tvOS 미디어 앱의 일반적인 디자인 언어를 살펴보는 것으로 시작하죠
미디어 카탈로그를 처음 열면 해당 뷰어로 대상이 지정된 홈페이지가 표시될 가능성이 큽니다 페이지 상단 부분에는 일반적으로 홍보 대상 콘텐츠가 표시되어 화면 가장자리까지 흐릅니다
이 홍보 대상 콘텐츠는 빠른 동작을 제공하여 사용자의 빠른 액세스를 허용할 수 있습니다 홍보 대상 콘텐츠 영역 아래의 선반에는 추가 콘텐츠가 표시되죠
각 항목에는 해당 아트워크를 선보이는 생생하고 다채로운 락업이 표시됩니다 마지막으로, 화면 상단을 가로지르는 탭 막대를 사용하면 앱의 다양한 섹션에 곧바로 액세스할 수 있으며 여기에는 검색 기능이 포함됩니다
이 세션에서는 음악과 TV 같은 Apple 앱에서 사용하는 익숙한 락업을 빌드하는 방법을 알아봅니다 이러한 콘텐츠 락업의 선반 흐름을 조합하는 멋진 기법 몇 가지를 소개하겠습니다 앱의 랜딩 페이지를 위한 인상적인 레이아웃을 구성하는 쉬운 방법을 제시하고 코드 몇 줄만으로 매끄러운 검색 경험을 선사하는 방법을 공개합니다
먼저, 조금 전에 설명했던 락업 디자인을 빌드하는 방법을 알아보겠습니다
tvOS에서 가장 일반적인 락업 유형은 텍스트 위에 떠 있는 이미지로 구성됩니다 이미지는 부드럽게 둥근 모서리를 가지고 있으며 초점이 놓이면 들리고 기울어지면서 반사 조명 효과가 적용됩니다 근처의 텍스트는 떠오른 이미지에 가려지지 않게 미끄러져 움직이죠 이 모든 것을 SwiftUI를 사용해 수행할 수 있습니다 기본 레이아웃은 간편하게 완료되는데, 텍스트 위의 이미지죠 이미 원하는 디자인에 상당히 가깝네요 하지만 대화형으로 구현하기 위해 이를 버튼 안에 넣겠습니다
기본적으로 tvOS의 버튼은 테두리 있는 buttonStyle을 사용합니다 초점이 놓이면 들리고 색상이 변경되는 배경 플래터가 제공되죠 원하는 디자인을 위해 테두리 없는 buttonStyle을 사용합니다
이제 이미지와 제목의 방향이 올바르고 이미지에 둥근 모서리와 약간의 드롭 그림자가 적용되었습니다 초점이 놓이면 이미지가 들리고 텍스트가 아래로 이동하며 반사 하이라이트가 나타나죠 리모컨의 터치 표면에서 손가락을 움직이면 이미지가 기울어집니다 이 코드는 다른 플랫폼에서 그대로 작동하며 플랫폼별로 적절한 상호작용 역학이 적용됩니다
기본 콘텐츠 락업이 구현되었으니 이제 선반을 생성할 수 있습니다 콘텐츠 선반의 표준 디자인을 다시 살펴보고 구현할 내용을 결정하겠습니다
콘텐츠는 가로로 흘러야 하며 화면의 안전 영역 경계에 맞게 정렬되어야 합니다 이렇게 하면 주변과 멋지게 인라인 상태로 유지됩니다
또한 화면 가장자리에서 화면 밖 콘텐츠가 살짝 보이게 하여 그 방향에서 더 많은 콘텐츠를 사용할 수 있음을 나타내야 합니다
가로로 스크롤하는 스택을 사용해 이를 빌드하기 시작합니다 이상적으로는 ScrollView에 임베드된 LazyHStack이 됩니다 테두리 없는 buttonStyle을 사용해 락업을 구현하고 ScrollView에서 스크롤 클리핑을 비활성화해 초점이 놓인 락업이 커져서 ScrollView 자체의 경계 외부에 드롭 그림자를 만들 수 있도록 합니다 선반 내의 각 항목은 Button으로 표현됩니다 항목 포스터 이미지의 정확한 크기를 직접 지정할 수 있지만 그 크기에서 파생된 aspectRatio만 사용하는 것이 더 나을 수 있죠 이렇게 하면 SwiftUI가 조정하여 이상적인 레이아웃을 제공합니다 이 경우 aspectRatio는 영화 포스터 모양을 제공하고 실제 포인트 크기는 화면의 항목 수에 따라 결정됩니다
containerRelativeFrame 한정자에서 레이아웃이 결정됩니다 이렇게 하면 SwiftUI가 이 항목의 프레임을 결정해야 함을 지정하고 유용한 정보를 제공합니다 이렇게 하면 콘텐츠가 자동으로 조정되어 선행 및 후행 안전 영역 삽입에 맞게 정렬되고 현재 스크롤 경계 외부의 추가 항목이 화면 양쪽의 뷰에 살짝 보이도록 충분한 공간을 제공합니다
먼저, 항목의 프레임은 가장 가까운 상위 컨테이너 뷰의 가로 경계를 기준으로 결정되어야 하며 이 경우에는 해당 뷰가 ScrollView입니다 단독으로는 이 버튼의 이미지가 ScrollView 경계의 너비만큼 확장되지만 2개의 다른 속성으로 이 동작이 변경됩니다
여기서는 6개 항목 레이아웃을 컨테이너의 너비 내에 배치하도록 요청하고 항목 사이에 간격을 얼마나 적용할지 지정하여 약간의 지원을 제공합니다 이렇게 하면 가로 스택에 제공된 간격과 일치하며 이 두 값이 일치하는 것은 중요합니다 다시 한 번, 정확히 동일한 코드를 사용하고, 간격과 항목 수 등을 적절히 조정해 다른 플랫폼에서 비슷한 경험을 선사할 수 있습니다
짧은 코드 몇 줄만으로 tvOS 9에서 TVML을 다시 사용하듯 Apple 고유의 미디어 앱의 디자인과 느낌을 복제했습니다 약간만 변경하여 다양한 방식으로 활용할 수 있는 유연한 구성요소입니다
버튼 제목을 제거하고 예를 들어 containerRelativeFrame 한정자를 약간 조정하면 hero 크기의 락업 캐러셀이 생성됩니다
아트워크의 가로 세로 비율과 containerRelativeFrame당 항목 수를 변경하면 앨범 아트워크를 위한 완벽한 크기가 만들어집니다
더 큰 정보 밀도가 필요한 경우 .card buttonStyle을 사용하여 TV 앱의 검색 결과에서와 동일한 유형의 락업을 빌드할 수 있습니다
카드 buttonStyle은 콘텐츠를 뒷받침하기 위해 둥근 플래터를 제공하며, 이 플래터는 테두리 없는 buttonStyle보다 더 미묘한 방식으로 들리고 이동합니다 다시 한 번, .card buttonStyle만 추가하면 이 동작을 구현할 수 있습니다
이제 선반이 만들어졌으니 시작 페이지를 조합할 준비가 되었네요
섹션 뷰는 각 선반에 제목을 제공하며, 해당 제목은 락업에 초점이 맞춰지면 가려지지 않도록 자동으로 물러납니다 버튼 자체의 제목과 마찬가지로요
홈 뷰를 올바르게 도입하려면 넓은 배경 이미지가 필요하며 이는 확장되어 화면의 가장자리를 멋진 이미지로 채웁니다 제목과 이미지를 보여 주기 위해 약간의 간격으로 분리된 몇 개의 버튼이 포함된 간단한 뷰를 추가하는 것으로 시작할 수 있습니다
이제 여기에 배경을 추가하고 외부 ScrollView에 첨부할 수 있죠 이미지를 크기 조정 가능하게 설정하여 화면에 맞추되 화면 가장자리까지 확장되지 않도록 하겠습니다 안전 영역을 무시하여 빠르게 해결할 수 있습니다
이미 훨씬 더 나아 보이네요 콘텐츠가 디스플레이 가장자리까지 흐르고 있습니다 정확히 우리가 원하는 대로요 조금 산만하긴 하지만 아래 선반 뒤에 표시됩니다
간단한 그라디언트 마스크는 버튼 뒤로 페이드 아웃됩니다 마스크 한정자가 적용된 일반 선형 그라디언트만 있으면 됩니다 이제 이미지가 페이드 아웃되어 그 뒤의 배경을 드러냅니다
더 낫네요, 최종 애플리케이션에 훨씬 더 가까워졌습니다 이상적으로는 이미지가 관심의 중심에 있지 않다면 완전히 사라지는 것이 좋습니다
tvOS 18.0에는 ScrollView만의 뷰 한정자가 많이 있습니다 이러한 한정자를 사용하여 헤더 섹션이 화면 밖으로 스크롤될 때 배경 이미지를 제거할 수 있습니다 콘텐츠가 belowFold를 이동하는 경우죠
새로운 onScrollVisibilityChange 한정자를 헤더 뷰에 추가했고 화면 밖으로 이동할 때 상태를 변경하겠습니다
그런 다음 해당 상태에 따라 배경을 표시하도록 선택합니다
또한 scrollTargetBehavior를 viewAligned로 설정하여 해당 전환이 더 확실하게 실행되도록 합니다
이 모든 것을 종합하면 이제 랜딩 페이지가 완성됩니다 이 세션의 샘플 코드를 다운로드하여 더 고급 기법을 사용해 이 뷰 스타일을 구현하는 방법의 다른 예제를 확인할 수 있습니다 tvOS 18 및 관련 릴리스의 새로운 ScrollView 한정자에 대해서는 ’SwiftUI 컨테이너 쉽게 이해하기’를 참고하세요
지금까지 잘했지만, 멋진 콘텐츠 제공 서비스라면 꼭 갖춰야 할 한 가지 기능이 있습니다 바로 검색입니다 이미 빌드한 내용을 빠르게 사용해 멋진 검색 인터페이스를 제공하는 방법을 알아보죠 무엇보다, 검색 패널에 액세스할 수 있는 방법을 제공해야 합니다
일반적인 접근 방식은 TabView를 사용하는 것이며, tvOS 18에는 TabView 조합을 위한 표현적인 새 타입 세이프 구문이 있습니다 기존 콘텐츠 스택을 StackView라는 새로운 뷰에 배치하고 SearchView를 새 탭에 추가했습니다 SearchView에서 무엇을 구현해야 할지 생각해 보죠
검색 뷰에서는 일반적으로 한 번에 많은 양의 콘텐츠를 검색할 수 있으므로 선반 레이아웃은 적절하지 않은 것 같습니다 그 대신에, 격자 레이아웃을 사용해 결과를 표시합니다
다른 뷰와 동일한 기본 구조로 시작하되 내부에 LazyVGrid를 배치하겠습니다
40포인트의 간격과 유연한 크기로 열 4개를 생성하도록 설정했습니다 다시 한 번 aspect-ratio 한정자와 SwiftUI의 레이아웃 기계가 항목의 실제 크기를 계산해 내도록 하겠습니다
락업 자체는 이제는 익숙한 Button이지만 지금은 세로로 스크롤되므로 가로 방향의 이미지를 사용해 해당 축을 따라 더 많은 콘텐츠를 포함합니다 이 콘텐츠를 검색 가능하게 만들기 위해 두 항목을 추가합니다 먼저, 상태 속성을 뷰에 추가합니다 이는 검색어를 입력할 때 이를 포함하도록 업데이트되며 이를 사용해 for-each 뷰에 전달된 콘텐츠를 필터링하여 결과에 표시되는 내용의 범위를 좁힐 수 있습니다
다음으로, .searchable 한정자를 추가하고 여기에 searchTerm 상태 속성에 대한 바인딩을 전달합니다
이 한정자 하나가 검색 뷰를 제공하는 데 필요한 전부입니다 여기에서 앱의 소비자는 입력을 시작할 수 있으며 여러분은 search-term 상태 속성 값으로 비일치 항목을 걸러 낼 수 있죠
좋아하는 로봇 식물학자가 포함된 비디오를 검색하거나 자신만의 식물학적 열망을 직접 충족할 수 있습니다 소비자가 검색할 콘텐츠가 많이 있을 수 있지만 여러분의 콘텐츠 항목에 관한 잠재적 검색어와 키워드는 상당히 길 수 있습니다 가능성이 있는 검색어 완성을 제안하면 유용할 것입니다 또한 .searchSuggestions 한정자만 추가하면 쉽게 수행할 수 있습니다 여기에 현재 검색어를 포함하는 키워드 목록을 가져왔으며 이로부터 일련의 텍스트 뷰를 생성하고 있습니다 결과는 이렇습니다 이러한 검색어 완성 중 하나를 선택하면 해당 값이 검색 필드에 자동으로 입력됩니다 한 제안 사항에 초점을 가져가면 검색 필드 내에 화면상의 미리보기가 제공되어 해당 제안 사항이 수행할 작업과 이 용어가 현재 입력 내용에 매칭되는 방식을 모두 나타냅니다 다시 한 번, 이것은 모두 다른 플랫폼에서 사용하는 것과 동일한 코드이며 정확히 동일한 방식으로 iPad, Mac 또는 Apple Vision Pro용 앱에 검색 필드를 추가합니다
이제 얼마나 쉽게 SwiftUI로 멋진 디자인의 Apple TV용 앱을 완성할 수 있으며 우리가 빌드한 모든 것이 어떻게 실제로 변경되지 않고 Apple의 다른 플랫폼에서도 실행되는지 명확해지셨겠죠 그 어느 때보다 더 쉽게 네이티브 SwiftUI 앱을 거실에서 실행할 수 있습니다
Apple은 SwiftUI의 최신 플랫폼 기능을 도입하는 깔끔하고 쉬운 방법을 제공하기 위해 노력합니다 사실, 하나만 더 보여 드리고 싶은데요 이제 SwiftUI를 통해 tvOS 18에서 TV 앱의 플로팅 사이드바를 시스템 전체에서 사용할 수 있습니다 동일하고 멋진 반투명 디자인을 자랑하며 축소되어 TV 앱의 동일하고 콤팩트한 표시기를 표시합니다 나만의 사이드바를 빌드했다면 앱에 이 새로운 디자인을 도입하는 것을 고려해 보세요 기존의 탐색 분할 뷰 API를 사용해 빌드할 수 있습니다 하지만 앱에서 탭 막대를 현재 사용 중이라면 코드 한 줄만으로 이 디자인을 사용해 볼 수 있습니다 sidebarAdaptable의 tabViewStyle만 설정하면 탭 막대가 사이드바로 대신 표시됩니다 이것은 iPad에서 iPadOS 18의 새로운 탭 막대 디자인을 구현하기 위해 사용하는 것과 동일한 한정자이며 여기서 사용자는 탭 막대와 사이드바 표현 간에 전환할 수도 있습니다 tvOS에서는 사이드바 및 그와 연관된 모든 동작을 단 한 줄의 코드로 제공합니다
새로운 TabView API를 사용하면 다양한 유형의 모든 콘텐츠로 전체 기능을 갖춘 사이드바를 빌드하는 동시에 최소 탭 막대 디자인을 유지할 수 있습니다 즉, iPad 앱에서 단순하고 밀도 낮은 탭 막대 표현을 사용하고, 이를 상세하고 기능적인 사이드바로 확장할 수 있으며 그 동일한 사이드바 콘텐츠가 tvOS에서 멋지게 구현됩니다 자세한 내용은 ’iPad에서 탭 및 사이드바 경험 향상하기’에서 새로운 탭 막대 API에 대한 모든 내용을 확인하세요
앱을 Apple TV로 가져오는 것을 고려 중이라면 지금이 적기입니다 SwiftUI로 쉽게 멋진 디자인의 tvOS용 앱을 빌드하고 많은 코드를 iPhone, iPad, Mac, Apple Vision Pro용 앱과 공유할 수 있습니다 현재 TVML 앱이 있다면 이제 변경하는 것을 고려해야 할 때입니다
SwiftUI로 Apple TV용 멋진 앱을 얼마나 쉽게 만드는지 알아봤죠 테두리 없는 buttonStyle은 해당 플랫폼의 표준 콘텐츠 락업 디자인 및 상호작용 구현에 필요한 모든 것을 제공하며 컨테이너 기준 프레임을 사용하면 콘텐츠 선반 빌드에서 주먹구구식 작업이 모두 배제됩니다 자세한 내용은 ’SwiftUI의 새로운 기능’에서 tvOS 18 및 관련 릴리스에 도입된 멋진 새로운 기능들을 참고하세요 ’SwiftUI 컨테이너 쉽게 이해하기’에서 새 컨테이너 API를 알아보고 ’iPad에서 탭 및 사이드바 경험 향상하기’에서 멋진 크로스 플랫폼 최상위 앱 탐색 UI 빌드 방법을 알아보세요
이 세션의 샘플 프로젝트에서 오늘 살펴본 예제 코드가 몇 가지 추가 예제와 제안 사항을 사용해 완전히 구현된 모습을 확인해 보세요 Apple TV에서 멋지게 실행되는 모든 기능을 갖춘 크로스 플랫폼 앱 예제는 Destination Video 샘플 프로젝트를 살펴보세요 SwiftUI for tvOS 팀은 tvOS 18 기반의 Apple TV용으로 여러분이 빌드하실 앱을 정말 기대하고 있습니다
-
-
4:18 - Borderless button lockup
Button {} label: { Image("discovery_landscape") .resizable() .frame(width: 250, height: 375) Text("Borderless Portrait") } .buttonStyle(.borderless)
-
5:38 - Standard content shelf
ScrollView(.horizontal) { LazyHStack(spacing: 40) { ForEach(Asset.allCases) { asset in Button {} label: { asset.portraitImage .resizable() .aspectRatio(250/375, contentMode: .fit) .containerRelativeFrame(.horizontal, count: 6, spacing: 40) Text(asset.title) } } } } .scrollClipDisabled() .buttonStyle(.borderless)
-
8:19 - Card button
ScrollView(.horizontal) { LazyHStack(spacing: 48) { ForEach(Asset.allCases) { asset in Button {} label: { HStack(alignment: .top, spacing: 10) { asset.landscapeImage .resizable() .aspectRatio(contentMode: .fit) .clipShape(RoundedRectangle(cornerRadius: 12)) VStack(alignment: .leading) { Text(asset.title) .font(.body) Text("Subtitle text goes here, limited to two lines") .font(.caption2) .foregroundStyle(.secondary) .lineLimit(2) Spacer(minLength: 0) HStack(spacing: 4) { ForEach(1..<4) { _ in Image(systemName: "ellipsis.rectangle.fill") } } .foregroundStyle(.secondary) } } .padding([.leading, .top, .bottom], 12) .padding(.trailing, 20) .frame(maxWidth: .infinity) } .containerRelativeFrame(.horizontal, count: 3, spacing: 48) } } } .scrollClipDisabled() .buttonStyle(.card)
-
8:39 - Landing page
ScrollView(.vertical) { LazyVStack(alignment: .leading, spacing: 26) { VStack(alignment: .leading) { Text("tvOS with SwiftUI") .font(.largeTitle).bold() Spacer(minLength: 300) HStack { Button("Show") {} Button(“More Info…”) {} Spacer() } .padding(.bottom, 100) Spacer() } .onScrollVisibilityChange { visible in withAnimation { belowFold = !visible } } Section("Movie Shelf") { MovieShelf() } Section("TV and Music Shelf") { TVMusicShelf() } Section("Content Cards") { CardShelf() } } .scrollTargetLayout() } .scrollClipDisabled() .background(alignment: .top) { if !belowFold { Image("beach_landscape") .resizable() .aspectRatio(contentMode: .fill) .ignoresSafeArea() .mask { LinearGradient(stops: [ .init(color: .black, location: 0.0), .init(color: .black, location: 0.45), .init(color: .black.opacity(0), location: 0.8) ], startPoint: .top, endPoint: .bottom) } } } .scrollTargetBehavior(.viewAligned)
-
11:13 - Tab view
TabView { Tab("Stack", systemImage: "line.3.horizontal") { StackView() } // Other Tabs... Tab("Search", systemImage: "magnifyingglass") { SearchView() } }
-
11:50 - Search page
@State var searchTerm: String = "" let columns: [GridItem] = Array(repeating: .init(.flexible(), spacing: 40), count: 4) ScrollView(.vertical) { LazyVGrid(columns: columns) { ForEach(sortedMatchingAssets) { asset in Button {} label: { asset.landscapeImage .resizable() .aspectRatio(16 / 9, contentMode: .fit) Text(asset.title) } } } } .scrollClipDisabled() .buttonStyle(.borderless) .searchable(text: $searchTerm) .searchSuggestions { ForEach(suggestedSearchTerms, id: \.self) { suggestion in Text(suggestion) } }
-
14:59 - Sidebar adaptable tab view style
TabView { Tab("Stack", systemImage: "line.3.horizontal") { StackView() } // Other Tabs... Tab("Search", systemImage: "magnifyingglass") { SearchView() } } .tabViewStyle(.sidebarAdaptable)
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.