Reading time: 1 min

This recipe show how to display a custom dialog in SwiftUI. You can customize the dialog content in any way you want.

Here's what the end result can look like:

This component is available as a Swift Package in this repo.

The custom dialog will be a ViewModifier that wraps the attached view in a ZStack, over which it renders the semi-transparent overlay and the dialog view itself.

struct CustomDialog<DialogContent: View>: ViewModifier {
  @Binding var isShowing: Bool // set this to show/hide the dialog
  let dialogContent: DialogContent

  init(isShowing: Binding<Bool>, 
        @ViewBuilder dialogContent: () -> DialogContent) {
    _isShowing = isShowing
     self.dialogContent = dialogContent()
  }

  func body(content: Content) -> some View {
   // wrap the view being modified in a ZStack and render dialog on top of it
    ZStack {
      content
      if isShowing {
        // the semi-transparent overlay
        Rectangle().foregroundColor(Color.black.opacity(0.6))
        // the dialog content is in a ZStack to pad it from the edges
        // of the screen
        ZStack {
          dialogContent
            .background(
              RoundedRectangle(cornerRadius: 8)
                .foregroundColor(.white))
        }.padding(40)
      }
    }
  }
}

extension View {
  func customDialog<DialogContent: View>(
    isShowing: Binding<Bool>,
    @ViewBuilder dialogContent: @escaping () -> DialogContent
  ) -> some View {
    self.modifier(CustomDialog(isShowing: isShowing, dialogContent: dialogContent))
  }
}

And here's how to use it:

struct DialogTest: View {
  @State private var showDialog = true
  var body: some View {
    List(1..<6) { index in
      Text("Item \(index)")
    }.customDialog(isShowing: $showDialog) { // HERE
      VStack {
        Text("Dialog title")
          .fontWeight(.bold)
        Divider()
        Text("Some longer description")
          .padding(.bottom, 10)
        Button(action: {
          showDialog = false            
        }) {
          Text("Close dialog")
            .autocapitalization(.allCharacters)
            .frame(minWidth: 0, maxWidth: .infinity)
            .padding()
        }.buttonStyle(MyButtonStyle())
      }.padding()
    }
  }
}

Button in the example is styled as in shown in this recipe.

Next Post Previous Post