Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

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 ↗

Transcript all transcripts

Chapters

Code shown on screen · 42 snippets

SwiftUI Lists swift · at 0:20 ↗
List {
  Text("Scrolling in the Deep")
  Text("Born to Build & Run")
  Text("Some Body Like View")
}
SwiftUI Lists swift · at 0:36 ↗
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 swift · at 0:54 ↗
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 swift · at 1:00 ↗
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 swift · at 2:35 ↗
@State 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 swift · at 2:47 ↗
// Insert code snvar data: Data
@ViewBuilder var content: (Data.Element) -> Content

var body: some View {
  DisplayBoardCardLayout {
    ForEach(data) { item in
      CardView {
        content(item)
      }
    }
  }
  .background { BoardBackgroundView() }
}
Data-driven DisplayBoard swift · at 3:08 ↗
@State 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 swift · at 3:30 ↗
List(songsFromSam) { song in
  Text(song.title)
}
List composition swift · at 3:46 ↗
List {
  Text("Scrolling in the Deep")
  Text("Born to Build & Run")
  Text("Some Body Like View")
}
List composition swift · at 3:56 ↗
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 swift · at 4:05 ↗
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 swift · at 4:24 ↗
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 swift · at 4:59 ↗
var data: Data
@ViewBuilder var content: (Data.Element) -> Content

var body: some View {
  DisplayBoardCardLayout {
    ForEach(data) { item in
      CardView {
        content(item)
      }
    }
  }
  .background { BoardBackgroundView() }
}
DisplayBoard implementation swift · at 5:15 ↗
// DisplayBoard implementation

@ViewBuilder 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 swift · at 5:27 ↗
@ViewBuilder var content: Content

var body: some View {
  DisplayBoardCardLayout {
    ForEach(subviewOf: content) { subview in
      CardView {
        subview
      }
    }
  }
  .background { BoardBackgroundView() }
}
List composition swift · at 5:52 ↗
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 swift · at 5:57 ↗
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 swift · at 6:12 ↗
@ViewBuilder var content: Content

var body: some View {
  DisplayBoardCardLayout {
    ForEach(subviewOf: content) { subview in
      CardView {
        subview
      }
    }
  }
  .background { BoardBackgroundView() }
}
DisplayBoard subviews swift · at 6:23 ↗
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 swift · at 6:36 ↗
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 swift · at 7:11 ↗
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 swift · at 7:19 ↗
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 swift · at 8:00 ↗
// 1 declared view
ForEach(songsFromSam) { song in
  Text(song.title)
}

// 9 resolved subviews
Text("I Container Multitudes")

Text("Love Stack")
Resolved Group swift · at 8:16 ↗
// 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 swift · at 8:32 ↗
// 1 declared view
EmptyView()	

// Zero resolved subviews
Resolved if expression swift · at 8:39 ↗
// 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 swift · at 8:48 ↗
@ViewBuilder var content: Content

var body: some View {
  DisplayBoardCardLayout {
    ForEach(subviewOf: content) { subview in
      CardView {
        subview
      }
    }
  }
  .background { BoardBackgroundView() }
}
DisplayBoard composition swift · at 9:11 ↗
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 swift · at 9:17 ↗
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 swift · at 9:44 ↗
@ViewBuilder var content: Content

var body: some View {
  DisplayBoardCardLayout {
    ForEach(subviewOf: content) { subview in
      CardView {
        subview
      }
    }
  }
  .background { BoardBackgroundView() }
}
DisplayBoard implementation swift · at 9:55 ↗
@ViewBuilder var content: Content

var body: some View {
  DisplayBoardCardLayout {
    Group(subviewsOf: content) { subviews in
      ForEach(subviews) { subview in
        CardView {
          subview
        }
      }
    }
  }
  .background { BoardBackgroundView() }
}
DisplayBoard implementation swift · at 10:19 ↗
@ViewBuilder 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 swift · at 10:47 ↗
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 swift · at 11:03 ↗
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 swift · at 11:26 ↗
DisplayBoard sections
@ViewBuilder 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 swift · at 11:35 ↗
@ViewBuilder var content: Content

var body: some View {
  DisplayBoardSectionContent {
    content
  }
  .background { BoardBackgroundView() }
}

struct DisplayBoardSectionContent<Content: View>: View {
  @ViewBuilder var content: Content
  ...
}
Implementing DisplayBoard sections swift · at 11:42 ↗
@ViewBuilder var content: Content

var body: some View {
  HStack(spacing: 80) {
    ForEach(sectionOf: content) { section in
      DisplayBoardSectionContent {
        section.content
      }
    }
  }
  .background { BoardBackgroundView() }
}
Implementing DisplayBoard section headers swift · at 12:48 ↗
@ViewBuilder 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 swift · at 13:30 ↗
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 swift · at 14:46 ↗
extension ContainerValues {
  @Entry var isDisplayBoardCardRejected: Bool = false
}

extension View {
  func displayBoardCardRejected(_ isRejected: Bool) -> some View {
    containerValue(\.isDisplayBoardCardRejected, isRejected)
  }
}
Implementing DisplayBoard customization swift · at 15:42 ↗
struct DisplayBoardSectionContent<Content: View>: View {
  @ViewBuilder 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 swift · at 16:15 ↗
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