Sheet in swiftui


My notes on sheets in swiftUI

Simple sheet example:

// This view is displayed as a full screen modal
struct FullScreenPopupView: View {
    // Environment variable to dismiss the modal
    @Environment(\.dismiss) var close

    var body: some View {
        ZStack {
            // Fill the entire screen with a primary color
            Color.primary.edgesIgnoringSafeArea(.all)
            // Button to dismiss the modal
            Button("Close Popup") {
                close()
            }
        }
    }
}

// This is the main content view
struct MainView: View {
    // State variable to control the presentation of the modal
    @State private var showModal = false

    var body: some View {
        // Button to present the modal
        Button("Show!") {
            showModal.toggle()
        }
        // Present the modal when the state variable is true
        .fullScreenCover(isPresented: $showModal, content: FullScreenPopupView.init)
    }
}

Controlling sheet from a subview:

import SwiftUI

class Model: ObservableObject {
    @Published var show = false
}
struct SubView: View {
    @EnvironmentObject var model: Model
    var tag: Int
    var body: some View {
        VStack {
            NavigationLink(destination: SubView(tag: tag + 1).environmentObject(model)) {
                Text("subview \(tag)")
            }
            if tag == 2 {
                Toggle(isOn: $model.show) {// toggle sheet
                    Text("toggle")
                }.padding()
            }
        }.navigationBarTitle("subview \(tag)")
    }
}
struct ContentView: View {
    @ObservedObject var model = Model()
    var body: some View {
        NavigationView {
            SubView(tag: 0).environmentObject(model) // inject sheet binding
        }.sheet(isPresented: $model.show) {
            Text("sheet")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Presenting as sheet or fullCover (backward compatibility):

Ref: https://stackoverflow.com/a/63056850/5389500

struct DemoCompatibleFullScreen: View {
    @State private var activateFullScreen = false
    var body: some View {
        Button("Toggle") { self.activateFullScreen.toggle() }
            .compatibleFullScreen(isPresented: $activateFullScreen) {
                Text("I'm in Full Screen!")
            }
    }
}

extension View {
    func compatibleFullScreen<Content: View>(isPresented: Binding<Bool>, @ViewBuilder content: @escaping () -> Content) -> some View {
        self.modifier(FullScreenModifier(isPresented: isPresented, builder: content))
    }
}

struct FullScreenModifier<V: View>: ViewModifier {
    let isPresented: Binding<Bool>
    let builder: () -> V

    @ViewBuilder
    func body(content: Content) -> some View {
        if #available(iOS 14.0, *) {
            content.fullScreenCover(isPresented: isPresented, content: builder)
        } else {
            content.sheet(isPresented: isPresented, content: builder)
        }
    }
}

Sheet activated on struct variable change:

It’s also possible to pass item in the sheet (seems this doesnt need isShowingSheet etc) see: https://medium.com/@ganeshrajugalla/swiftui-how-to-use-sheets-sheet-38dc2d22b1d3

import SwiftUI

struct User:Identifiable{
    let id = UUID()
    var name:String
}

struct SheetView: View {
    
    // MARK: - Properties    
    @State private var user:User? = nil
   
    // MARK: - Body
    var body: some View {
        VStack(spacing: 20){
            Button("User1") {
                user = User(name: "Steave Jobs")
            }
            
            Button("User2"){
                user = User(name: "Tim Cook")
            }
        }
        
        .sheet(item: $user){ item in
            NextView(user: item)
        }
    }
}

struct NextView: View {
    
    // MARK: - Properties
    @Environment (\.dismiss) var dismiss
    let user:User
    
    // MARK: - Body
    var body: some View {
        VStack(spacing: 30){
            Text("\(user.name)")
                .font(.title)
                .fontWeight(.bold)
            Button("Dismiss") {
                dismiss()
            }
        }
    }
}

Resources:

  • Customizing sheet in SwiftUI: https://github.com/edudnyk/SheeKit
  • More nuances on sheets in SwiftUI; https://www.swiftyplace.com/blog/swiftui-sheets-modals-bottom-sheets-fullscreen-presentation-in-ios
  • here is a way to customize sheet popover: https://rudrank.blog/custom-modal-ipad-swiftui
  • hacky custom popover for legacy swiftui: https://github.com/piterwilson/SwiftUI-Modal-on-iPad