iosswiftswiftuiswiftui-sheet

SwiftUI - open sheet from various views


So I have a sheet like this

      .sheet(item: $logMyPractice, content: { item in
          AssesmentRecorderView(
            assessment: DrillModel(
              questions: [
                .init(prompt: "Question 1", resultValue: .integer),
              ]
            ),
            completion: { date, answers in
              print("response", date, answers)
              // custom logic, save in the db
            })
          .presentationDetents([.fraction(0.85), .large ])
        }

What is happening is that I need to open that sheet from a different views. I could copy/paste it to the separate views but it's not following a DRY concept.

I could create an ObservableObject in the main view that holds information about the sheet and attach it as a environmentObject, but I wonder if there's a better approach?


Solution

  • Here comes the power of the ViewModifier

    You can do something like this

        struct ContentView: View {
        
        @State var isPresented: Bool = false
        
        var body: some View {
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                Button {
                    isPresented = true
                } label: {
                    Text("Hello, world!")
                }
    
            }
            .customSheet(isPresented: $isPresented) { singleString, arrayString in
                print(singleString)
                print(arrayString)
            }
        }
     }
        
        struct CustomSheetViewModifier: ViewModifier {
            
            @Binding var isPresented: Bool
            
            let completionHandler: ((String, [String]) -> Void)?
            
            func body(content: Content) -> some View {
                content
                    .sheet(isPresented: $isPresented) {
                        Button {
                            completionHandler?("Done", ["Button 1", "Button 2"])
                            isPresented = false
                        } label: {
                            Text("Click me")
                        }
        
                    }
            }
        }
    
    extension View {
        func customSheet(isPresented: Binding<Bool>, completionHandler: ((String, [String]) -> Void)?) -> some View {
            modifier(CustomSheetViewModifier(isPresented: isPresented, completionHandler: completionHandler))
        }
    }
    

    and in your case you will replace the body of the CustomeSheetViewModifier with this

    func body(content: Content) -> some View {
            content
                .sheet(isPresented: $isPresented) {
                    AssesmentRecorderView(
                        assessment: DrillModel(
                            questions: [
                                .init(prompt: "Question 1", resultValue: .integer),
                            ]
                        ),
                        completion: { date, answers in
                            completionHandler(date, answers)
                        })
                    .presentationDetents([.fraction(0.85), .large ])
                }
        }
    

    assuming that the date is a string and the answers is an array of Strings you of course can replace the completionHandler parameters with whatever you like