是否可以在 SwiftUI 中设置不可关闭的模态?

Is it possible to make a modal non-dismissible in SwiftUI?

我正在创建一个应用程序,其中登录/注册部分位于模式内,如果用户未登录则显示。

问题是,用户可以通过向下滑动来关闭模式...

是否可以避免这种情况?

var body: some View {
    TabView(selection: $selection) {
        App()
    }.sheet(isPresented: self.$showSheet) { // This needs to be non-dismissible
        LoginRegister()
    }
}

第二个例子:

我正在使用模式来询问信息。除非使用保存按钮关闭模态,否则用户不应该能够退出此过程。用户必须在按钮工作之前输入信息。不幸的是,可以通过向下滑动来关闭模态。

是否可以避免这种情况?

这是一个常见问题,"code smell"...好吧,这不是真正的代码,而是 "design pattern smell"。

问题是您将登录过程作为应用程序其余部分的一部分。

与其在 App 上显示 LoginRegister,不如显示 AppLoginRegister

即你应该有一些像 userLoggedIn: Bool 之类的状态对象,并且根据该值你应该显示 AppLoginRegister.

只是不要同时在视图层次结构中。这样您的用户将无法关闭视图。

iOS 15 岁及以后:

在sheet上使用.interactiveDismissDisabled(true),仅此而已。

上一页 iOS 15:

您可以尝试使用 highPriorityGesture 来做到这一点。当然,蓝色的矩形只是为了演示,但你必须使用覆盖整个屏幕的视图。

struct ModalViewNoClose : View {
    @Environment(\.presentationMode) var presentationMode
    
    let gesture = DragGesture()
    
    var body: some View {
        
        Rectangle()
            .fill(Color.blue)
            .frame(width: 300, height: 600)
            .highPriorityGesture(gesture)
            
            .overlay(
                VStack{
                    Button("Close") {
                        self.presentationMode.value.dismiss()
                    }.accentColor(.white)
                    Text("Modal")
                        .highPriorityGesture(gesture)
                    TextField("as", text: .constant("sdf"))
                        .highPriorityGesture(gesture)
                } .highPriorityGesture(gesture)
        )
            .border(Color.green)
    }
}

如果您不介意使用 Introspect:

import Introspect

@available(iOS 13, *)
extension View {
    /// A Boolean value indicating whether the view controller enforces a modal behavior.
    ///
    /// The default value of this property is `false`. When you set it to `true`, UIKit ignores events
    /// outside the view controller's bounds and prevents the interactive dismissal of the
    /// view controller while it is onscreen.
    public func isModalInPresentation(_ value: Bool) -> some View {
        introspectViewController {
            [=10=].isModalInPresentation = value
        }
    }
}

用法:

.sheet {
    VStack {
        ...
    }.isModalInPresentation(true)
}

理论上这可能对你有帮助(我没试过)

private var isDisplayedBind: Binding<Bool>{ Binding(get: { true }, set: { _ = [=10=] }) }

和用法:

content
    .sheet(isPresented: isDisplayedBind) { some sheet }

iOS 15

从 iOS 15 开始,您可以使用 interactiveDismissDisabled

您只需将其附加到 sheet:

var body: some View {
    TabView(selection: $selection) {
        App()
    }.sheet(isPresented: self.$showSheet) {
        LoginRegister()
            .interactiveDismissDisabled(true)
    }
}

关于您的第二个示例,您可以传递一个变量来控制何时禁用 sheet:

.interactiveDismissDisabled(!isAllInformationProvided)

您可以在 documentation 中找到更多信息。