Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: Clear Modal state or Reinitialize

I have a SwiftUI modal that I would like to either clear the state of or reinitialize. Reinitalizing would be preferred considering the fact that this modal can open other modals that may have some state.

Here is a simple example:

import SwiftUI

struct OtherView: View {
    @State var otherViewState: String = ""

    var body: some View {
        TextField($otherViewState, placeholder: Text("Demo Text Input"))
    }
}

struct Demo: View {
    @State var showModal: Bool = false

    var modal: Modal {
        Modal(OtherView(), onDismiss: { self.showModal = false })
    }

    var body: some View {
        Button(action: { self.showModal = true }) {
            Text("Toggle Modal")
        }
        .presentation(self.showModal ? self.modal : nil)
    }
}

Regardless of how OtherView is dismissed, I would like to reopen it with its text state cleared, with the requirement that OtherView could open modals itself. Adding a clear method on the OtherView struct itself is always an option, but I don't find it to be a maintainable one.

Below is a video of the simplified problem: Demo

like image 652
michaelgmcd Avatar asked Jul 04 '19 05:07

michaelgmcd


2 Answers

Update September 11th: This appears to be fixed in iOS 13 GM.

I've been struggling with the same thing and I would like to think that this is a bug that will be resolve by September, I've already filed it on Feedback Assistant, make sure to do the same!

For now though you can just create a new UIHostingController that wraps the SwiftUI View that you want to show modally. I know it looks really hacky but at least it works:

import SwiftUI

struct OtherView: View {
    @State var otherViewState: String = ""

    var body: some View {
        TextField($otherViewState, placeholder: Text("Demo Text Input"))
    }
}

struct Demo: View {
    var body: some View {
        Button("Toggle Modal") {
            self.showModal()
        }
    }

    func showModal() {
        let window = UIApplication.shared.windows.first
        window?.rootViewController?.present(UIHostingController(rootView: OtherView()), animated: true)
    }
}

You might want to improve how you get the window, specially if you support multiple windows but I think you get the idea.

like image 158
GuiyeC Avatar answered Nov 07 '22 07:11

GuiyeC


You can reinitialize your Modal in .onAppear(). This example works on Beta 3.

import SwiftUI

struct ModalView : View {

    @Environment(\.isPresented) var isPresented: Binding<Bool>?

    @State var textName: String = ""

    var body: some View {

        NavigationView {

            Form {
                Section() {
                     TextField("Name", text: self.$textName)
                        .textFieldStyle(.roundedBorder)
                }
            }
            .listStyle(.grouped)
                .navigationBarTitle(Text("Add Name"), displayMode: .large)
                .navigationBarItems(leading: Button(action:{ self.dismiss() })
                { Text("Cancel") },
                                    trailing: Button(action:{ self.dismiss() })
                                    { Text("Save") } )
                .onAppear(perform: {
                    self.textName = ""
                })
        }
    }

    func dismiss() {
        self.isPresented?.value = false
    }

}

struct DetailView : View {

    var body: some View {

        PresentationLink(destination: ModalView())
        { Text("Present") }
    }

}

struct ContentView : View {

    var body: some View {

        NavigationView {
            NavigationLink(destination: DetailView())
            { Text("Navigate") }
        }
    }
}
like image 1
Chuck H Avatar answered Nov 07 '22 08:11

Chuck H