2024 DesignSwiftUI & UI Frameworks
WWDC24 · 16 min · Design / SwiftUI & UI Frameworks
Elevate your tab and sidebar experience in iPadOS
iPadOS 18 introduces a new navigation system that gives people the flexibility to choose between using a tab bar or sidebar. The newly redesigned tab bar provides more space for content and other functionality. Learn how to use SwiftUI and UIKit to enable customization features – like adding, removing and reordering tabs – to enable a more personal touch in your app.
Watch at developer.apple.com ↗Chapters
- 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
Code shown on screen · 13 snippets
TabView updates in SwiftUI
TabView {
Tab("Watch Now", systemImage: "play") {
WatchNowView()
}
Tab("Library", systemImage: "books.vertical") {
LibraryView()
}
// ...
} 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()
},
// ...
] Search tab
// SwiftUI
Tab(role: .search) {
SearchView()
}
// UIKit
let searchTab = UISearchTab {
SearchViewController()
} 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) 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
// ...
},
] 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) Sidebar actions
TabSection(...) {
// ...
}
.sectionActions {
Button("New Station", ...) {
// action
}
}
// UIKit
let tabGroup = UITabGroup(...)
tabGroup.sidebarActions = [
UIAction(title: "New Station", ...) { _ in
// action
},
] Drop destinations in SwiftUI
Tab(collection.name, image: collection.image) {
CollectionDetailView(collection)
}
.dropDestination(for: Photo.self) { photos in
// Add 'photos' to the specified collection
} 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
}
} TabView customization in SwiftUI
("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) 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) 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 = [...] 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.
} Resources
Related sessions
-
26 min -
29 min