[英]SwiftUI dismiss modal
由於 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())
}
}
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()
}
}
}
您可以在模態視圖中使用presentationMode
環境變量並調用self.presentaionMode.wrappedValue.dismiss()
來關閉模態:
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.
@SwiftUI.Environment(\.presentationMode) private var presentationMode: Binding<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")
}
}
}
}
在 Xcode Beta 5 中,另一種方法是在啟動模態的視圖中使用@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")
}
}
}
這是一種關閉所呈現視圖的方法。
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")
})
}
}
似乎對於 Xcode 11 Beta 7(這是在 Xcode 的 build 11M392r 上),它略有不同。
@Environment(\.presentationMode) var presentation
Button(action: { self.presentation.wrappedValue.dismiss() }) { Text("Dismiss") }
你可以實現這個。
struct view: View {
@Environment(\.isPresented) private var isPresented
private func dismiss() {
isPresented?.value = false
}
}
現在在 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() }
}
}
}
由於PresentationButton
易於使用,但隱藏狀態會破壞SwiftUI
的預測特性, 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")
}
}
}
我們現在可以使用DismissAction
代替presentationMode
。
這是文檔中的一個示例:
struct SheetView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
NavigationView {
SheetContents()
.toolbar {
Button("Done") {
dismiss()
}
}
}
}
}
在 Xcode 11.0 beta 7 中,該值現在已包裝,以下功能對我有用:
func dismiss() {
self.presentationMode.wrappedValue.dismiss()
}
SwiftUI 中的模態視圖似乎很簡單,直到您開始在List
或Form
視圖中使用它們。 我創建了一個包含所有邊緣情況的小型庫,並使模態視圖的使用與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
在 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()
})
}
}
執行此操作的一種方法可能是為模態呈現和解除聲明您自己的修飾符。
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
}
}
})
}
}
演示文稿從頂部全屏顯示,如果您希望它來自側面,請將修改器內部的過渡更改為前導或尾隨。 其他過渡也可以工作,例如不透明度或比例。
(示例代碼@main
用於 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!")
}
}
當子視圖大於主視圖時:
以及這背后的代碼:
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()
}
}
您可以使用演示模式關閉。 宣布
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
然后在需要時,通過
self.presentationMode.wrappedValue.dismiss()
Swift 5.5 和 SwiftUI 3 中的新功能:
@Environment(\.dismiss) var dismiss
然后在函數或正文代碼中的某處,只需調用:
self.dismiss()
您可以使用SheetKit關閉所有工作表
SheetKit().dismissAllSheets()
或呈現新的 UISheetPresentationController
sheetKit.present(with: .bottomSheet){
Text("Hello world")
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.