Reading time: 1 min

This recipe shows how to present multiple sheets or full screen covers on a single view in SwiftUI. It saves you from having to have multiple modifiers with isPresented, each holding their own view. Also, it automatically takes care of switching between different sheets / covers with just a single binding.

The end result looks like this:

preview

The key here is to use the item variant instead of isPresented. Instead of binding to a boolean, you bind to an Identifiable optional. If the value is nil, the sheet / cover isn't presented. When the value changes, the sheet / cover goes up, and you can decide on its content based on the value of the binding.

The best way of going about it is to declare the sheet / cover view variants in an enum. It's also dead easy to make it conform to Identifiable, since enums without associated values can use themselves as ObjectIdentifier:

enum Screen: Identifiable, CaseIterable {
  case red, blue, green

  var id: Self {
    self
  }

  var color: Color {
    switch self {
    case .red:
      return .red
    case .blue:
      return .blue
    case .green:
      return .green
    }
  }
}

Then, use the enum in an optional Binding to control when and what to show:

struct PopoverTest: View {
  @State private var screen: Screen?

  var body: some View {
    Button("Random color") {
      screen = Screen.allCases.randomElement() // show the popover
    }
    .fullScreenCover(item: $screen, onDismiss: nil) { currentScreen in
      ZStack {
        Rectangle()
          .foregroundColor(currentScreen.color)
        Button("Reset") {
          self.screen = nil // hide the popover again
        }
        .foregroundColor(.white)
      }
    }
  }
}

fullScreenCover is only available from SwiftUI 2 (iOS 14, macOS 11), so can check out drop-in replacement that works on any version!

Next Post Previous Post