스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
iPadOS에서 탭 및 사이드바 경험 향상하기
iPadOS 18에는 사용자들이 탭 막대 또는 사이드바 중에 유연하게 선택할 수 있도록 해주는 새로운 탐색 시스템이 도입되었습니다. 새롭게 다시 설계된 탭 막대는 콘텐츠 및 기타 기능을 추가할 수 있는 넓은 공간이 있습니다. SwiftUI 및 UIKit을 활용하여 탭 추가, 탭 제거, 탭 순서 변경 등 맞춤화 기능을 활성화하고 앱에 개성을 더하는 방법을 알아보세요.
챕터
- 0:00 - Introduction
- 0:52 - Tab bar and sidebar refresh
- 3:56 - Tab bar and sidebar features
- 4:28 - Tab bar SwiftUI updates
- 5:00 - Tab bar UIKit updates
- 5:58 - Search tab
- 6:41 - Enable sidebar with TabView in SwiftUI
- 7:16 - Enable sidebar with UITabBarController in UIKit
- 7:46 - Sidebar actions
- 8:13 - Drop destinations on Tabs in SwiftUI
- 8:25 - Drop destinations on UITabs in UIKit
- 9:15 - User customization
- 10:45 - Enable customization in SwiftUI
- 12:38 - Enable customization in UIKit
- 13:52 - Platform considerations
리소스
- Destination Video
- Elevating your iPad app with a tab bar and sidebar
- Enhancing your app’s content with tab navigation
- Forum: UI Frameworks
관련 비디오
WWDC22
WWDC20
-
다운로드
’iPadOS에서 탭 및 사이드바 경험 향상하기’입니다 저는 Andy이고 UIKit 엔지니어입니다 이 비디오에서는 앱을 향상하고 콘텐츠를 전면에 배치하는 데 도움이 되는 탭 막대와 사이드바의 개선 사항을 알아보겠습니다
탭 막대는 iPhone 및 iPad가 출시된 이후부터 핵심 탐색 패턴이었습니다 탭은 앱의 콘텐츠를 여러 섹션으로 분류합니다
탭 막대를 통해 각 탭 내의 현재 상태를 유지하며 탭 간에 빠르게 전환할 수 있습니다 예를 들어, 시계 앱에서 탭 막대에는 세계 시계 알람, 스톱워치 타이머의 네 가지 탭이 있습니다
iPadOS 18에서 탭 막대는 사용하지 않는 수직 및 수평 공간을 줄여 더 많은 콘텐츠를 전면에 표시하는 새 콤팩트한 모습을 선보입니다
이제 막대는 앱 상단에 위치하며 다른 탐색 제어기와 더 가까워서 더 쉽게 접근할 수 있습니다 또 상단 막대와 공간을 공유해 더 많은 앱 콘텐츠가 표시되게 할 수 있습니다
사람들이 어떤 기기를 사용하든 쉽게 탐색할 수 있도록 탭은 iPhone과 iPad 간에 일관적이어야 합니다 탭을 너무 많이 추가하지 마세요 선택이 많으면 앱에서 정보를 찾기 더 어렵습니다
탭 막대처럼 사이드바는 최상위 대상에 빨리 접근하도록 해 사람들이 앱을 탐색하는 데 도움을 줍니다 폴더나 플레이리스트와 같은 최상위 콘텐츠 컬렉션을 표시하여 더 효율적으로 탐색할 수 있습니다 iPadOS 17 및 이전 버전에는 선행 열에 윤곽선 보기가 있는 Split View를 사용하여 사이드바가 구성되었습니다
iPadOS 18의 새로운 기능으로 이제 선택적으로 탭 막대를 사이드바로 표시할 수 있으며 여러 개선 사항을 통해 사이드바를 가장 효과적으로 만들 수 있습니다
사이드바를 숨기면 탭 막대에 다시 애니메이션 효과가 적용되어 탭으로 표시되는 콘텐츠에 다시 초점을 맞춥니다
사이드바를 열지 않고 탭 막대로 탐색 가능합니다
탭 막대는 앱의 최상위 섹션을 표시하며 사이드바는 같은 앱 계층 구조 섹션을 표시 가능합니다 탭 막대와 사이드바를 적용하는건 다양한 계층 구조의 콘텐츠 중심 앱에 적합합니다
탭 막대 탐색에 대한 모범 사례는 ’iOS를 위한 탐색 디자인 살펴보기’ 사이드바는 ’iPad를 위한 새로운 디자인’을 시청하세요
콘텐츠가 풍부한 앱은 강력할 뿐만 아니라 개인 맞춤이 가능해야 합니다 새로운 사이드바는 개별 탭을 표시하거나 숨기거나 재정렬할 수 있는 사용자 정의 기능을 지원합니다
탭 막대의 드래그 앤 드롭으로 즐겨찾는 탭을 추가할 수 있어 앱 경험을 개인화할 수 있습니다
기존 사이드바에 탭 막대를 적용하면 둘 모두의 장점을 결합하고 앱을 맞춤화하며 개인화할 수 있는 새로운 방법을 사용할 수 있습니다 먼저 새 탭 막대 및 사이드바의 기능과 이를 적용할 때 고려할 사항을 살펴보겠습니다
다음으로, 앱에 개성을 더하는 사용자 맞춤화 지원에 대해 이야기하겠습니다
마지막으로, 이 기능이 여러 플랫폼에서 어떻게 보이는지 다중 플랫폼 환경 빌드 시 고려할 사항을 설명하겠습니다
탭 막대와 사이드바로 시작합니다 iPadOS 18 SDK로 컴파일하면 기존 탭 막대 앱은 코드 변경 없이 새 모습으로 업데이트됩니다 이 업데이트된 모습에서 탭 막대는 더 통합된 모양을 위해 탐색 막대와 안전 영역을 공유합니다
탭 막대 옆에 표시할 공간이 충분하지 않으면 탐색 막대의 도구 막대 항목이 자동으로 오버플로로 이동합니다
iOS 18의 새로운 기능으로 TabView의 SwiftUI에 새 구문이 추가되어 빌드 시 일반 오류를 쉽게 포착할 수 있습니다 제목, 이미지, 해당 콘텐츠 보기 포함 Tab 구조를 선언합니다
필요에 따라 Tab에 선택 값 포함 프로그래밍 방식 선택을 활성화합니다 이 새 구문은 모든 탭의 선택 유형이 동일하고 해당 유형이 TabView 자체와 일치하는지 확인합니다
iOS 18의 새로운 사항에 UIKit에 UITabBarController의 새 앱 계층 구조 설명 API가 있습니다
앱의 각 최상위 섹션을 나타내는 UITab을 생성하고 이를 tabBarController의 tabs 속성에 할당합니다 UITab 인스턴스에 대한 변경은 탭이 표시되는 위치에 즉시 반영됩니다
탭 막대는 채워진 글리프이고 사이드바는 윤곽선 글리프가 기본입니다 탭에 기호 이미지를 사용하면 윤곽선 변형을 사용하세요
탭 막대에 표시될 때 시스템에서 채워진 변형을 자동으로 선택합니다 예를 들어, 음악 앱에서 둘러보기 탭은 윤곽선이 있는 글리프인 square.grid.2x2를 사용합니다 탭 막대에서 채워진 변형은 다른 이미지 지정 없이 표시됩니다
검색은 모든 앱의 공통 기능이며 돋보기 기호로 즉시 알아볼 수 있습니다
SwiftUI의 search role Tab 또는 UIKit의 UISearchTab을 사용하면 시스템은 기본 제목, 이미지 및 고정된 배치로 탭을 구성합니다 고정된 배치를 통해 탭 막대 후행 가장자리에서 탭을 항상 사용할 수 있습니다
사이드바는 여러 계층 구조의 iPad 앱에서 콘텐츠의 추가 컬렉션을 표시하는 데 유용합니다
새로운 Tab 및 UITab API를 사용하면 앱이 탭 막대와 사이드바 모두에 앱의 구조를 나타낼 수 있습니다
SwiftUI에서 TabView로 사이드바를 활성화하려면 먼저 tabViewStyle을 sidebarAdaptable로 설정합니다
그런 다음 TabSection으로 사이드바에 그룹을 나타냅니다
탭 막대에 선언된 순서대로 탭이 나타납니다 사이드바에서 섹션은 개별 탭 다음에 정렬됩니다 이 예에서는 검색은 사이드바의 두 TabSection 앞에 표시됩니다
UIKit에서 UITabBarController를 사용하여 사이드바를 활성화하려면 tabBarController를 tabSidebar로 설정합니다
TabSection과 유사하게 UITabGroup을 사용하여 단일 그룹에 속하는 하위 탭 모음을 나타냅니다
동적 콘텐츠 그룹은 하위 항목을 직접 변경하여 업데이트됩니다
사이드바의 섹션에 작업을 추가하여 팟캐스트에서 새 스테이션 생성 같은 일반 작업의 편의를 제공할 수도 있습니다
탭은 드롭 대상이기도 합니다 이로써 모음에 사진 추가처럼 드래그 앤 드롭으로 사이드바 또는 탭 막대의 탭에 직접 추가할 수 있습니다
SwiftUI의 Tab에 drop destinations 기능을 지원하려면 Tab에 수신자 유형 dropDestination 한정자를 사용하고
UIKit의 UITab에 drop destinations 기능을 지원하려면 UITabBarControllerDelegate에서 두 가지 메서드를 구현합니다 먼저, 드롭을 수신할 수 있다면 operationForAcceptingItemsFromDropSession에서 유효 연산을 반환하고
그 다음, 데이터를 수신하려면 acceptItemsFromDropSession 드롭 세션에서 로드합니다
여러 동작 및 drop destinations 기능 외에도 사이드바를 맞춤화할 수 있는 새 API가 많이 있습니다 사이드바의 머리글과 바닥글을 사용자 지정하거나 탭에 스와이프 동작이나 빠른 메뉴를 추가할 수 있습니다 또한 탭에서 팝오버를 표시할 수 있는데 탭이 표시되는 위치에 고정됩니다 탭으로 앱을 설명하는 건 첫 단계에 불과합니다 다음, 사용자 맞춤화를 추가해 앱에 개성을 더하는 방법을 알아보겠습니다
맞춤화를 활성화하여 특정 요구에 맞게 사이드바를 구성할 수 있게 합니다 사이드바에서 필수 아닌 탭을 숨기거나 그룹을 재배치하려는데 순서, 표시 관련 사용자 맞춤화는 자동으로 유지됩니다
탭 막대 맞춤화는 iPadOS 16의 도구 막대 맞춤화 기능과 유사합니다
탭 막대 항목은 사이드바에서 추가 또는 드래그해서 놓는 등 드래그 앤 드롭을 통해 맞춤화할 수 있습니다
탭에는 맞춤화 및 표시 환경설정을 결정할 수 있는 기본 배치가 있습니다
탭 막대는 3개 섹션이 있습니다
고정 섹션은 앱의 중요한 대상을 위해 설계되었습니다 다른 항목보다 먼저 표시되며 맞춤화가 허용되지 않습니다
맞춤화 가능한 섹션의 항목은 재배치할 수 있습니다 사이드바에서 드래그 앤 드롭으로 항목을 추가할 수 있습니다
검색과 같이 고정된 섹션의 항목은 탭 막대의 후행 가장자리에서 항상 사용할 수 있습니다
sidebarOnly 배치를 사용하여 탭이 탭 막대에 추가되지 않도록 하고 사이드바를 통해서만 접근할 수 있게 할 수 있습니다
SwiftUI의 TabView에서 사용자 맞춤화를 활성화하려면 TabView에 TabViewCustomization을 덧붙이면 TabView 내 탭에 여러 맞춤화 기능이 가능합니다
사이드바 맞춤화를 반영할 다른 UI가 있는 경우 TabViewCustomization을 읽어 맞춤화된 데이터를 추적합니다
맞춤화 내용이 계속 유지되도록 하려면 TabViewCustomization에 식별자로 AppStorage를 추가합니다
customizationID를 연결하여 탭에 맞춤화가 적용되도록 합니다
이제 맞춤화를 활성화했으니 ’지금 보기’나 ’보관함’처럼 앱에서 가장 중요한 탭이 고정되어 항상 사용 가능한지 확인하고 싶습니다
개별 탭에 대해 맞춤화를 비활성화하려면 customizationBehavior 한정자를 사용합니다 이 한정자를 사용하면 사이드바와 탭 막대에서 탭의 작동 방식을 제어할 수 있습니다 ’지금 보기’ 탭은 앱의 기능에 중요하므로 사이드바와 탭 막대 모두에 대해 맞춤화를 비활성화하겠습니다
맞춤화할 수 없는 탭엔 customizationID가 불필요합니다
마찬가지로 탭을 숨길 수 있으므로 앱에서 어떤 기본 대상을 표시할지 유연하게 설정할 수 있습니다 사이드바나 탭 막대에서 defaultVisibility 한정자로 탭을 숨깁니다
’지금 보기’, ’보관함’에 맞춤화를 비활성화했으므로 앱에서 중요한 탭과 맞춤화할 수 있는 탭을 명확하게 알 수 있습니다
UIKit에 탭 맞춤화를 활성화하려면 allowsHiding을 true로 설정해서 필수적이지 않은 탭을 숨깁니다
표시 여부는 UITab의 isHidden 속성으로 결정할 수 있습니다
탭이 tabBarController에 할당되면 저장된 맞춤화가 적용됩니다
preferredPlacement 속성으로 탭의 맞춤화 동작과 탭 막대의 표시 여부를 제어할 수 있습니다
그룹 내에서 탭을 재정렬하려면 allowsReordering을 true 설정해요 맞춤화 순서는 displayOrderIdentifiers 속성으로 결정됩니다
맞춤화가 완료되면 UIKit은 두 UITabBarControllerDelegate 메서드를 호출해 탭 표시, 순서 맞춤화 변화를 알립니다
맞춤화를 통해 좋아하는 콘텐츠에 대한 단축어를 쉽게 만들고 사이드바와 탭 막대를 필요에 맞게 조정할 수 있습니다 이 API로 Apple 플랫폼에 탭 보기 앱을 빌드 가능합니다 각 플랫폼에서 최상의 환경을 빌드하기 위해 고려 가능한 플랫폼 사항과 조정 사항을 살펴보겠습니다 macOS Sequoia에서 TabView나 TabBarController가 사이드바를 지원하면 표준 Mac 사이드바를 채택합니다 사이드바 탭은 iPad처럼 드래그 앤 드롭으로 재정렬합니다
visionOS 2에서는 탭 막대가 루트 탭의 윈도우 앞쪽 가장자리에 오너먼트로 표시됩니다 새로운 Tab 및 UITab API를 통해 iOS에서처럼 탭 막대에 기호가 표시될 때 시스템이 자동으로 채워진 변형을 선택합니다
TabSection이나 UITabGroup으로 그룹 내 보조 탐색을 위해 그룹 콘텐츠와 함께 표시되는 사이드바도 추가됩니다
tvOS 18에서 SwiftUI 앱은 TabView, TabSection API를 통해 새 축소 가능 사이드바를 적용할 수 있습니다
Tab 및 UITab을 통해 Apple 플랫폼에서 쉽게 탐색하고 콘텐츠가 풍부한 앱을 가장 쉽게 빌드할 수 있습니다 다음은 뭘까요? 새로운 탭 막대로 앱이 멋지게 보이게 하는 겁니다 새로운 사이드바가 앱을 향상하는 방법을 살펴보세요 앱에 사용자 맞춤화를 지원하여 새 수준의 맞춤화를 실현할 수도 있습니다 시청해 주셔서 감사합니다 탭 또는 공간을 좋아한다면 아래에 댓글을 남겨 주세요
-
-
4:27 - TabView updates in SwiftUI
TabView { Tab("Watch Now", systemImage: "play") { WatchNowView() } Tab("Library", systemImage: "books.vertical") { LibraryView() } // ... }
-
4:58 - UITabBarController updates in UIKIt
tabBarController.tabs = [ UITab(title: "Watch Now", image: UIImage(systemName: "play"), identifier: "Tabs.watchNow") { _ in WatchNowViewController() }, UITab(title: "Library", image: UIImage(systemName: "books.vertical"), identifier: "Tabs.library") { _ in LibraryViewController() }, // ... ]
-
5:58 - Search tab
// SwiftUI Tab(role: .search) { SearchView() } // UIKit let searchTab = UISearchTab { SearchViewController() }
-
6:41 - Adding a sidebar in SwiftUI
TabView { Tab("Watch Now", systemImage: "play") { // ... } Tab("Library", systemImage: "books.vertical") { // ... } // ... TabSection("Collections") { Tab("Cinematic Shots", systemImage: "list.and.film") { // ... } Tab("Forest Life", systemImage: "list.and.film") { // ... } // ... } TabSection("Animations") { // ... } Tab(role: .search) { // ... } } .tabViewStyle(.sidebarAdaptable)
-
7:16 - Adding a sidebar in UIKit
let collectionsGroup = UITabGroup( title: "Collections", image: UIImage(systemName: "folder"), identifier: "Tabs.CollectionsGroup" children: self.collectionsTabs()) { _ in // ... } tabBarController.mode = .tabSidebar tabBarController.tabs = [ UITab(title: "Watch Now", ...) { _ in // ... }, UITab(title: "Library", ...) { _ in // ... }, // ... collectionsGroup, UITabGroup(title: "Animations", ...) { _ in // ... }, UISearchTab { _ in // ... }, ]
-
7:35 - Updating a tab group in UIKit
let collectionsGroup = UITabGroup( title: "Collections", image: UIImage(systemName: "folder"), identifier: "Tabs.CollectionsGroup" children: self.collectionsTabs()) { _ in // ... } let newCollection = UITab(...) collectionsGroup.children.append(newCollection)
-
7:45 - Sidebar actions
TabSection(...) { // ... } .sectionActions { Button("New Station", ...) { // action } } // UIKit let tabGroup = UITabGroup(...) tabGroup.sidebarActions = [ UIAction(title: "New Station", ...) { _ in // action }, ]
-
8:12 - Drop destinations in SwiftUI
Tab(collection.name, image: collection.image) { CollectionDetailView(collection) } .dropDestination(for: Photo.self) { photos in // Add 'photos' to the specified collection }
-
8:24 - Drop destinations in UIKit
func tabBarController( _ tabBarController: UITabBarController, tab: UITab, operationForAcceptingItemsFrom dropSession: any UIDropSession ) -> UIDropOperation { session.canLoadObjects(ofClass: Photo.self) ? .copy : .cancel } func tabBarController( _ tabBarController: UITabBarController, tab: UITab, acceptItemsFrom dropSession: any UIDropSession) { session.loadObjects(ofClass: Photo.self) { photos in // Add 'photos' to the specified collection } }
-
10:45 - TabView customization in SwiftUI
@AppStorage("MyTabViewCustomization") private var customization: TabViewCustomization TabView { Tab("Watch Now", systemImage: "play", value: .watchNow) { // ... } .customizationID("Tab.watchNow") // ... TabSection("Collections") { ForEach(MyCollectionsTab.allCases) { tab in Tab(...) { // ... } .customizationID(tab.customizationID) } } .customizationID("Tab.collections") // ... } .tabViewCustomization($customization)
-
11:40 - Customization behavior and visibility in SwiftUI
Tab("Watch Now", systemImage: "play", value: .watchNow) { // ... } .customizationBehavior(.disabled, for: .sidebar, .tabBar) Tab("Optional Tab", ...) { // ... } .customizationID("Tab.example.optional") .defaultVisibility(.hidden, for: .tabBar)
-
12:38 - Tab customization in UIKit
let myTab = UITab(...) myTab.allowsHiding = true print(myTab.isHidden) // .default, .optional, .movable, .pinned, .fixed, .sidebarOnly myTab.preferredPlacement = .fixed let myTabGroup = UITabGroup(...) myTabGroup.allowsReordering = true myTabGroup.displayOrderIdentifiers = [...]
-
12:39 - Observing customization changes in UIKit
func tabBarController(_ tabBarController: UITabBarController, visibilityDidChangeFor tabs: [UITab]) { // Read 'tab.isHidden' for the updated visibility. } func tabBarController(_ tabBarController: UITabBarController, displayOrderDidChangeFor group: UITabGroup) { // Read 'group.displayOrderIdentifiers' for the updated order. }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.