2024 SwiftUI & UI Frameworks
WWDC24 · 18 min · SwiftUI & UI Frameworks
Demystify SwiftUI containers
Learn about the capabilities of SwiftUI container views and build a mental model for how subviews are managed by their containers. Leverage new APIs to build your own custom containers, create modifiers to customize container content, and give your containers that extra polish that helps your apps stand out.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 42 snippets
SwiftUI Lists
List {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
} SwiftUI Lists
List {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(otherSongs) { song in
Text(song.title)
}
} SwiftUI Lists
List {
Section("Favorite Songs") {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
Section("Other Songs") {
ForEach(otherSongs) { song in
Text(song.title)
}
}
} SwiftUI Lists
List {
Section("Favorite Songs") {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
Section("Other Songs") {
ForEach(otherSongs) { song in
Text(song.title)
.listRowSeparator(.hidden)
}
}
} Data-driven DisplayBoard
private var songs: [Song] = [
Song("Scrolling in the Deep"),
Song("Born to Build & Run"),
Song("Some Body Like View"),
]
var body: some View {
DisplayBoard(songs) { song in
Text(song.title)
}
} DisplayBoard implementation
// Insert code snvar data: Data
var content: (Data.Element) -> Content
var body: some View {
DisplayBoardCardLayout {
ForEach(data) { item in
CardView {
content(item)
}
}
}
.background { BoardBackgroundView() }
} Data-driven DisplayBoard
private var songs: [Song] = [
Song("Scrolling in the Deep"),
Song("Born to Build & Run"),
Song("Some Body Like View"),
]
var body: some View {
DisplayBoard(songs) { song in
Text(song.title)
}
} List composition
List(songsFromSam) { song in
Text(song.title)
} List composition
List {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
} List composition
List {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
List(songsFromSam) { song in
Text(song.title)
} List composition
List {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
List {
ForEach(songsFromSam) { song in
Text(song.title)
}
} List composition
List {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(songsFromSam) { song in
Text(song.title)
}
} DisplayBoard implementation
var data: Data
var content: (Data.Element) -> Content
var body: some View {
DisplayBoardCardLayout {
ForEach(data) { item in
CardView {
content(item)
}
}
}
.background { BoardBackgroundView() }
} DisplayBoard implementation
// DisplayBoard implementation
var content: Content
var body: some View {
DisplayBoardCardLayout {
ForEach(data) { item in
CardView {
content(item)
}
}
}
.background { BoardBackgroundView() }
}
DisplayBoard {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
DisplayBoard {
ForEach(songsFromSam) { song in
Text(song.title)
}
} DisplayBoard implementation
var content: Content
var body: some View {
DisplayBoardCardLayout {
ForEach(subviewOf: content) { subview in
CardView {
subview
}
}
}
.background { BoardBackgroundView() }
} List composition
List {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(songsFromSam) { song in
Text(song.title)
}
} DisplayBoard composition
DisplayBoard {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(songsFromSam) { song in
Text(song.title)
}
} DisplayBoard implementation
var content: Content
var body: some View {
DisplayBoardCardLayout {
ForEach(subviewOf: content) { subview in
CardView {
subview
}
}
}
.background { BoardBackgroundView() }
} DisplayBoard subviews
DisplayBoard {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(songsFromSam) { song in
Text(song.title)
}
} Declared vs. resolved views
DisplayBoard {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(songsFromSam) { song in
Text(song.title)
}
}
// 3 resolved subviews
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
// 9 resolved subviews
Text("I Container Multitudes")
…
Text("Love Stack") List subviews
List {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(songsFromSam) { song in
Text(song.title)
}
} Declared vs. resolved views
DisplayBoard {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(songsFromSam) { song in
Text(song.title)
}
}
// 3 resolved subviews
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
// 9 resolved subviews
Text("I Container Multitudes")
…
Text("Love Stack") Resolved ForEach
// 1 declared view
ForEach(songsFromSam) { song in
Text(song.title)
}
// 9 resolved subviews
Text("I Container Multitudes")
…
Text("Love Stack") Resolved Group
// 1 declared view
Group {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
// 3 resolved subviews
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View") Resolved EmptyView
// 1 declared view
EmptyView()
// Zero resolved subviews Resolved if expression
// 1 declared view
if showFavoriteSongs {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
// true → 3 resolved subviews
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
// false → Zero resolved subviews DisplayBoard implementation
var content: Content
var body: some View {
DisplayBoardCardLayout {
ForEach(subviewOf: content) { subview in
CardView {
subview
}
}
}
.background { BoardBackgroundView() }
} DisplayBoard composition
DisplayBoard {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(songsFromSam) { song in
Text(song.title)
}
} DisplayBoard composition
DisplayBoard {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
ForEach(songsFromSam) { song in
Text(song.title)
}
ForEach(songsFromSommer) { song in
Text(song.title)
}
} DisplayBoard implementation
var content: Content
var body: some View {
DisplayBoardCardLayout {
ForEach(subviewOf: content) { subview in
CardView {
subview
}
}
}
.background { BoardBackgroundView() }
} DisplayBoard implementation
var content: Content
var body: some View {
DisplayBoardCardLayout {
Group(subviewsOf: content) { subviews in
ForEach(subviews) { subview in
CardView {
subview
}
}
}
}
.background { BoardBackgroundView() }
} DisplayBoard implementation
var content: Content
var body: some View {
DisplayBoardCardLayout {
Group(subviewsOf: content) { subviews in
ForEach(subviews) { subview in
CardView(
scale: subviews.count > 15 ? .small : .normal
) {
subview
}
}
}
}
.background { BoardBackgroundView() }
} List sections
List {
Section("Favorite Songs") {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
Section("Other Songs") {
ForEach(otherSongs) { song in
Text(song.title)
}
}
} DisplayBoard sections
DisplayBoard {
Section("Matt's Favorites") {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
Section("Sam's Favorites") {
ForEach(songsFromSam) { song in
Text(song.title)
}
}
Section("Sommer's Favorites") {
ForEach(songsFromSommer) { song in
Text(song.title)
}
}
} Implementing DisplayBoard sections
DisplayBoard sections
var content: Content
var body: some View {
DisplayBoardCardLayout {
Group(subviewsOf: content) { subviews in
ForEach(subviews) { subview in
CardView(
scale: subviews.count > 15 ? .small : .normal
) {
subview
}
}
}
}
.background { BoardBackgroundView() }
} Implementing DisplayBoard sections
var content: Content
var body: some View {
DisplayBoardSectionContent {
content
}
.background { BoardBackgroundView() }
}
struct DisplayBoardSectionContent<Content: View>: View {
var content: Content
...
} Implementing DisplayBoard sections
var content: Content
var body: some View {
HStack(spacing: 80) {
ForEach(sectionOf: content) { section in
DisplayBoardSectionContent {
section.content
}
}
}
.background { BoardBackgroundView() }
} Implementing DisplayBoard section headers
var content: Content
var body: some View {
HStack(spacing: 80) {
ForEach(sectionOf: content) { section in
VStack(spacing: 20) {
if !section.header.isEmpty {
DisplayBoardSectionHeaderCard { section.header }
}
DisplayBoardSectionContent {
section.content
}
.background { BoardSectionBackgroundView() }
}
}
}
.background { BoardBackgroundView() }
} List customization
List {
Section("Favorite Songs") {
Text("Scrolling in the Deep")
Text("Born to Build & Run")
Text("Some Body Like View")
}
Section("Other Songs") {
ForEach(otherSongs) { song in
Text(song.title)
.listRowSeparator(.hidden)
}
}
} Custom container values
extension ContainerValues {
var isDisplayBoardCardRejected: Bool = false
}
extension View {
func displayBoardCardRejected(_ isRejected: Bool) -> some View {
containerValue(\.isDisplayBoardCardRejected, isRejected)
}
} Implementing DisplayBoard customization
struct DisplayBoardSectionContent<Content: View>: View {
var content: Content
var body: some View {
DisplayBoardCardLayout {
Group(subviewsOf: content) { subviews in
ForEach(subviews) { subview in
let values = subview.containerValues
CardView(
scale: (subviews.count > 15) ? .small : .normal,
isRejected: values.isDisplayBoardCardRejected
) {
subview
}
}
}
}
}
} DisplayBoard customization
DisplayBoard {
Section("Matt's Favorites") {
Text("Scrolling in the Deep")
.displayBoardCardRejected(true)
Text("Born to Build & Run")
Text("Some Body Like View")
}
Section("Sam's Favorites") {
ForEach(songsFromSam) { song in
Text(song.title)
.displayBoardCardRejected(song.samHasDibs)
}
}
Section("Sommer's Favorites") {
ForEach(songsFromSommer) { Text($0.title) }}}
}
.displayBoardCardRejected(true)
} Resources
Related sessions
-
40 min -
22 min