SwiftUI dismiss modal

前端 未结 13 2131
闹比i
闹比i 2020-11-29 23:23

Since SwiftUI is declarative there is no dismiss methode. How can is add a dismiss/close button to the DetailView?



        
相关标签:
13条回答
  • 2020-11-29 23:59

    In Xcode Beta 5, another way to do this is to use @State in the view that launches the modal, and add a binding in the modal view to control visibility of the modal. This doesn't require you to reach into the @Environment presentationMode variable.

    struct MyView : View {
        @State var modalIsPresented = false
    
        var body: some View {
            Button(action: {self.modalIsPresented = true})  {
                Text("Launch modal view")
            }
            .sheet(isPresented: $modalIsPresented, content: {
                MyModalView(isPresented: self.$modalIsPresented)
            })
        }
    }
    
    
    struct MyModalView : View {
        @Binding var isPresented: Bool
    
        var body: some View {
            Button(action: {self.isPresented = false})  {
                Text("Close modal view")
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-29 23:59

    SwiftUI 2 code sample (works with mobiles also)

    (sample code doesnt work with swift 1, but you still can try it without @main block)

    Full app sample for using sheets:

    @main
    struct TestAppApp: App {
        var body: some Scene {
            WindowGroup {
                SheetLink(text: "click me!", content: ChildView() )
                    .padding(.all, 100)
            }
        }
    }
    
    struct ChildView: View {
        var body: some View {
            Text("this is subView!")
        }
    }
    

    and when subview is larger than main view:

    And code behind this:

    struct SheetLink<Content> : View where Content: View {
        @State var text: String
        @State var displaySheet = false
        @State var content: Content
    
    
        var body: some View {
            HStack {
                Button(text, action: { self.displaySheet = true } ).buttonStyle(PlainButtonStyle()).foregroundColor(.blue)
            }
            .sheet(isPresented: $displaySheet) {
                SheetTemplateView(isPresented: self.$displaySheet, content: content)
            }
        }
    }
    
    struct SheetTemplateView<Content> : View where Content: View {
        @Binding var isPresented: Bool
        @State var content: Content
        
        var body: some View {
            VStack{
                HStack{
                    Button("Back!", action: { isPresented.toggle() } ).buttonStyle(PlainButtonStyle()).foregroundColor(.blue)
                    Spacer()
                }
                Spacer()
                content
                Spacer()
            }
            .padding()
        }
    }
    
    0 讨论(0)
  • 2020-11-30 00:00

    Using @State property wrapper (recommended)

    struct ContentView: View {
        @State private var showModal = false
        
        var body: some View {
           Button("Show Modal") {
              self.showModal.toggle()
           }.sheet(isPresented: $showModal) {
                ModalView(showModal: self.$showModal)
            }
        }
    }
    
    struct ModalView: View {
        @Binding var showModal: Bool
        
        var body: some View {
            Text("Modal view")
            Button("Dismiss") {
                self.showModal.toggle()
            }
        }
    }
    

    Using presentationMode

    You can use presentationMode environment variable in your modal view and calling self.presentaionMode.wrappedValue.dismiss() to dismiss the modal:

    struct ContentView: View {
    
      @State private var showModal = false
    
      // If you are getting the "can only present once" issue, add this here.
      // Fixes the problem, but not sure why; feel free to edit/explain below.
      @Environment(\.presentationMode) var presentationMode
    
    
      var body: some View {
        Button(action: {
            self.showModal = true
        }) {
            Text("Show modal")
        }.sheet(isPresented: self.$showModal) {
            ModalView()
        }
      }
    }
    
    
    struct ModalView: View {
    
      @Environment(\.presentationMode) private var presentationMode
    
      var body: some View {
        Group {
          Text("Modal view")
          Button(action: {
             self.presentationMode.wrappedValue.dismiss()
          }) {
            Text("Dismiss")
          }
        }
      }
    }
    

    0 讨论(0)
  • 2020-11-30 00:02

    There is now a pretty clean way to do this in Beta 5.

    import SwiftUI
    
    struct ModalView : View {
        // In Xcode 11 beta 5, 'isPresented' is deprecated use 'presentationMode' instead
        @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
        var body: some View {
            Group {
                Text("Modal view")
                Button(action: { self.presentationMode.wrappedValue.dismiss() }) { Text("Dismiss") }
            }
        }
    }
    
    struct ContentView : View {
        @State var showModal: Bool = false
        var body: some View {
            Group {
                Button(action: { self.showModal = true }) { Text("Show modal via .sheet modifier") }
                    .sheet(isPresented: $showModal, onDismiss: { print("In DetailView onDismiss.") }) { ModalView() }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-30 00:05

    You can implement this.

    struct view: View {
        @Environment(\.isPresented) private var isPresented
    
        private func dismiss() {
            isPresented?.value = false
        }
    }
    
    0 讨论(0)
  • 2020-11-30 00:07

    Since PresentationButton is easy to use but hiding the state wich is undermining the predictive character of SwiftUI I have implemented it with an accessible Binding.

    public struct BindedPresentationButton<Label, Destination>: View where Label: View, Destination: View {
        /// The state of the modal presentation, either `visibile` or `off`.
        private var showModal: Binding<Bool>
    
        /// A `View` to use as the label of the button.
        public var label: Label
    
        /// A `View` to present.
        public var destination: Destination
    
        /// A closure to be invoked when the button is tapped.
        public var onTrigger: (() -> Void)?
    
        public init(
            showModal: Binding<Bool>,
            label: Label,
            destination: Destination,
            onTrigger: (() -> Void)? = nil
        ) {
            self.showModal = showModal
            self.label = label
            self.destination = destination
            self.onTrigger = onTrigger
        }
    
        public var body: some View {
            Button(action: toggleModal) {
                label
            }
            .presentation(
                !showModal.value ? nil :
                    Modal(
                        destination, onDismiss: {
                            self.toggleModal()
                        }
                    )
            )
        }
    
        private func toggleModal() {
            showModal.value.toggle()
            onTrigger?()
        }
    }
    

    This is how it is used:

    struct DetailView: View {
        @Binding var showModal: Bool
    
        var body: some View {
            Group {
                Text("Detail")
                Button(action: {
                    self.showModal = false
                }) {
                    Text("Dismiss")
                }
            }
        }
    }
    
    struct ContentView: View {
        @State var showModal = false
    
        var body: some View {
            BindedPresentationButton(
                showModal: $showModal,
                label: Text("Show"),
                destination: DetailView(showModal: $showModal)
            ) {
                print("dismissed")
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题