0

I've a main view that displays a modal sheet. As I've multiple modals, I use enum and State to control which sheet is presented.

@State var activeSheet: Sheet?

Inside my MainView:

    Button(action: {
        activeSheet = .settings
    }, label: {
        Text(“Button”)
    })

   .sheet(item: $activeSheet) { sheet in
        switch sheet {
        case .info:
          InfoView()
        case .settings:
          SettingsView()
        }
      }

SettingsView:

struct SettingsView: View {
    @Environment(\.presentationMode) private var presentationMode
    @EnvironmentObject var model: MainModel
    
    var body: some View {
        Button("Action") {
            model.myFunction()
        }
    }
}

In my InfoView-sheet I have a button that calls a function inside an EnvironmentObject. How do I dismiss the sheet once the function has completed in the EnvironmentObject?

Every view is linked to the same EnvironmentObject btw.

Thanks!

Mallon
  • 13
  • 4

1 Answers1

1

Depending for which min iOS version you are deploying, you have two options:

If your minimum iOS version is <= iOS 14, in both InfoView and SettingsView use system environment \.presentationMode.

struct SettingsView: View {
    @EnvironmentObject var model: MainModel
    @Environment(\.presentationMode) private var presentationMode

    var body: some View {
        Button("Dismiss") {

            /// ... Do something here prior to dismissing the sheet.
            model.MyFunction(email: email, password: password) {
                presentationMode.wrappedValue.dismiss()
            }
        }
    }
}

On your minimum iOS version is iOS 15, in both InfoView and SettingsView use system environment \.dismiss.

struct SettingsView: View {
    @EnvironmentObject var model: MainModel
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        Button("Dismiss") {

            /// ... Do something here prior to dismissing the sheet.
            model.MyFunction(email: email, password: password) {
                dismiss()
            }
        }
    }
}

And MainModel class:

class MainModel: ObservableObject {
    func myFunction(email: String, password: String, onSuccess: (() -> Void)? = nil) {         
        auth.signIn(withEmail: email, password: password) { [weak self] result, error in
            if result == nil, error != nil {
                 self?.showAlert = true
            } else {
                 guard result != nil, error == nil else { return }
                 /* Success */
                 self?.signedIn = true
                 onSuccess?()
            }
        }
    }
}

Alternative solution:

If your minimum deployment target is >= iOS 14, you can listen to changes of your environment object properties like this:

struct SettingsView: View {
    @EnvironmentObject var model: MainModel
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        Button("Dismiss") {
            model.MyFunction(email: email, password: password)
        }
        .onChange(of: model.signedIn) { signedIn in
            if signedIn {
                dismiss()
            }
        }
    }
}
Eugene Dudnyk
  • 5,553
  • 1
  • 23
  • 48
  • Thanks for your reply! However, in my case I would like to dismiss the sheet only once the function from the EnvironmentObject has been completed. struct SettingsView: View { @EnvironmentObject var model: MainModel var body: some View { Button("Action") { model.MyFunction() } } } How would I do this? – Mallon Oct 09 '21 at 14:08
  • What is `model.MyFunction()`? If it is synchronous function, then just call `dismiss()` after you call `model.MyFunction()`. If it is asynchronous function, then you MUST have the POSSIBILITY TO KNOW when it completes. I.e. you must have a completion handler as a parameter. Do you have it? – Eugene Dudnyk Oct 09 '21 at 14:10
  • Here is the function. If the function returns an error, the modal view should stay open, if it succeeds then the modal view should be closed. func myFunction(email: String, password: String) { auth.signIn(withEmail: email, password: password) { [weak self] result, error in if result == nil, error != nil { self?.showAlert = true } else { guard result != nil, error == nil else { return } /* Success */ self?.signedIn = true } } } – Mallon Oct 09 '21 at 14:43
  • Add `onSuccess` parameter to MyFunction as shown in the answer and use `MyFunction` as shown in the answer. – Eugene Dudnyk Oct 09 '21 at 14:50
  • Added an alternative solution now, having more information about your model. Check it out. – Eugene Dudnyk Oct 09 '21 at 15:03