SwiftUI关闭模态

时间:2019-06-09 18:41:21

标签: swift modal-dialog swiftui

由于SwiftUI是声明性的,因此没有dismiss方法。 如何为DetailView添加一个关闭/关闭按钮?

struct DetailView: View {
  var body: some View {
  Text("Detail")
  }
}

struct ContentView : View {
  var body: some View {
  PresentationButton(Text("Click to show"), destination: DetailView())
  }
}

16 个答案:

答案 0 :(得分:17)

消除模态的另一种方法是使用isPresented环境变量,即:

  

绑定到一个布尔值,该值指示此实例是否为   当前呈现的层次结构的一部分。

您需要做的就是在模式视图中将此变量的值设置为false

struct DetailView : View {

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

  var body: some View {
    Group {
      Text("Detail view")
      Button(action: {
        self.isPresented?.value = false
      }) {
        Text("Dismiss")
      }
    }
  }
}

struct ContentView : View {
  var body: some View {
   // PresentationButton(Text("Show modal"), destination: DetailView())
   // In Xcode 11 beta 3, 'PresentationButton' is deprecated and renamed to 'PresentationLink'
    PresentationLink(destination: DetailView()) {
      Text("Show modal")
    }
  }
}

enter image description here

答案 1 :(得分:12)

这是一种消除显示视图的方法。

struct DetailView: View {
    @Binding
    var dismissFlag: Bool

    var body: some View {
        Group {
            Text("Detail")
            Button(action: {
                self.dismissFlag.toggle()
            }) {
                Text("Dismiss")
            }
        }

    }
}

struct ContentView : View {
    @State var dismissFlag = false

    var body: some View {
        Button(action: {
            self.dismissFlag.toggle()
        })
        { Text("Show") }
            .presentation(!dismissFlag ? nil :
                Modal(DetailView(dismissFlag: $dismissFlag)) {
                print("dismissed")
            })
    }
}

enter image description here

答案 2 :(得分:8)

对于Xcode 11 Beta 7(这是在Xcode的版本11M392r上),似乎有些不同。

@Environment(\.presentationMode) var presentation


Button(action: { self.presentation.wrappedValue.dismiss() }) { Text("Dismiss") }

答案 3 :(得分:6)

您可以实现这一点。

$ cat file
this/that/maybe/more/and/more
or/even/this/could/be/it/and/maybe/more
short/more

答案 4 :(得分:6)

现在在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() }
        }
    }
}

答案 5 :(得分:6)

在xcode beta5中,另​​一种方法是在启动模式的视图中使用@State,并在模式视图中添加绑定以控制模式的可见性。这不需要您进入@EnvironmentpresentationMode变量。

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")
        }
    }
}

答案 6 :(得分:3)

iOS 15

我们现在可以使用 presentationMode 代替 DismissAction

以下是来自 documentation 的示例:

struct SheetView: View {
    @Environment(\.dismiss) var dismiss

    var body: some View {
        NavigationView {
            SheetContents()
                .toolbar {
                    Button("Done") {
                        dismiss()
                    }
                }
        }
    }
}

答案 7 :(得分:2)

由于PresentationButton易于使用,但是隐藏状态到SwiftUI的可预测性,我已经使用可访问的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?()
    }
}

这是它的用法:

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")
        }
    }
}

答案 8 :(得分:2)

在Xcode 11.0 beta 7中,该值现已包装,以下功能对我有用:

func dismiss() {
    self.presentationMode.wrappedValue.dismiss()
}

答案 9 :(得分:1)

在您开始在ListForm视图中使用它们之前,SwiftUI中的模式视图似乎很简单。我创建了一个小型库,该库包装了所有边缘情况,并使模态视图的使用与NavigationView-NavigationLink对相同。

该库在以下位置开放源代码:https://github.com/diniska/modal-view。您可以使用Swift Package Manager将其包含在项目中,也可以仅复制库中包含的单个文件。

您的代码的解决方案是:

struct DetailView: View {
    var dismiss: () -> ()
    var body: some View {
        Text("Detail")
        Button(action: dismiss) {
            Text("Click to dismiss")
        }
    }
}

struct ContentView : View {
    var body: some View {
        ModalPresenter {
            ModalLink(destination: DetailView.init(dismiss:)) {
                Text("Click to show")
            }
        }
    }
}

此外,还有一篇文章提供了完整的描述和示例:How to present modal view in SwiftUI

答案 10 :(得分:1)

Navigation中自动弹出,在Modal中自动关闭


只需从目标视图中的环境中获取presentationMode,并从其中获取dismiss的{​​{1}}:

wrappedValue

演示(弹出/关闭)

Pop Dismiss

答案 11 :(得分:1)

Swift 5.5 和 SwiftUI 3 中的新功能:

@Environment(\.dismiss) var dismiss

然后在函数或正文代码中的某处,只需调用:

self.dismiss()

答案 12 :(得分:0)

在PresentationMode上使用环境变量。 该GitHub链接可能会帮助您解决问题https://github.com/MannaICT13/Sheet-in-SwiftUI

这是简单的解决方案:

struct ContentView2 : View {

    @Environment (\.presentationMode) var presentationMode

    var body : some View {
        VStack {
            Text("This is ContentView2")
            Button(action: {
                self.presentationMode.wrappedValue.dismiss()
            }, label: {
                Text("Back")    
            })    
        }
    }
}


struct ContentView: View {

    @State var isShowingSheet : Bool = false

    var body: some View {
        Button(action: {
            self.isShowingSheet.toggle()
        }, label: {
            Text("Click Here")
        }).sheet(isPresented: $isShowingSheet, content: {  
            ContentView2()
        })
    }
}

答案 13 :(得分:0)

执行此操作的一种方法可能是声明您自己的修饰符以用于模式表示和解雇。

extension View {

  func showModal<T>(_ binding: Binding<Bool>, _ view: @escaping () -> T) -> some View where T: View {

    let windowHeightOffset = (UIApplication.shared.windows.first?.frame.height ?? 600) * -1

    return ZStack {

      self

      view().frame(maxWidth: .infinity, maxHeight: .infinity).edgesIgnoringSafeArea(.all).offset(x: 0, y: binding.wrappedValue ? 0 : windowHeightOffset)

    }

  }
}

然后,您可以在想要告知如何显示视图和关闭该视图的任何视图上使用修饰符。就像弹出框或工作表修改器一样。

struct ContentView: View {

  @State var showModal = false

  var body: some View {

    Text("Show").foregroundColor(.blue).onTapGesture {
      withAnimation(.easeIn(duration: 0.75)) {
        self.showModal = true
      }
    }.showModal($showModal, {

      Text("Dismiss").foregroundColor(.blue).onTapGesture {
        withAnimation(.easeIn(duration: 0.75)) {
          self.showModal = false
        }
      }

    })


  }
}    

该演示文稿从顶部开始全屏显示,如果您希望它来自侧面,请将修改器内的过渡更改为前导或尾随。其他转换也可以使用,例如不透明度或缩放。

enter image description here

答案 14 :(得分:0)

SwiftUI 2代码示例(也适用于手机)

(示例代码不适用于swift 1,但您仍可以在没有@main块的情况下尝试使用它)

使用表格的完整应用示例:

@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!")
    }
}

enter image description here

并且子视图大于主视图时:

enter image description here

以及此后的代码:

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()
    }
}

答案 15 :(得分:0)

您可以使用演示模式关闭。 声明

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

然后在需要时将其关闭

self.presentationMode.wrappedValue.dismiss()