【问题标题】:Is it possible to make a modal non-dismissible in SwiftUI?是否可以在 SwiftUI 中使模态不可关闭?
【发布时间】:2021-08-23 23:05:07
【问题描述】:

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

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

是否可以防止这种情况发生?

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

第二个例子:

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

是否可以防止这种情况发生?

【问题讨论】:

  • 模态通常(大多数情况下)被关闭。为什么不改变你的应用程序设计?让您的登录成为完整的View?将它放在您想要的任何视图顶部的ZStack 中。现在您拥有 100% 的控制权(您没有模态框),并且可以 (a) 以动画方式呈现它,(b) 保持它可见,直到您的代码说要关闭它,以及 (c) 动画化关闭它。跨度>
  • 从 iOS 15 开始,您可以使用 interactiveDismissDisabled - 请参阅 this answer

标签: swiftui


【解决方案1】:

iOS 15 及更高版本:

在工作表上使用.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)
    }
}

【讨论】:

  • 我会将其标记为答案,因为它最接近我的要求。谢谢,效果很好!
  • 不幸的是,您仍然可以同时使用两个或多个手指来关闭模式。有什么想法吗?
  • @user1302387 你解决了吗,或者在stackoverflow.com/a/60939207/6433690试试我的方法?
【解决方案2】:

这是一个常见问题和“代码异味”...不是真正的代码,而是“设计模式异味”。

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

您应该真正显示AppLoginRegister,而不是在App 上显示LoginRegister

即你应该有一些像userLoggedIn: Bool 这样的状态对象,根据这个值你应该显示AppLoginRegister

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

【讨论】:

  • 好吧对不起我心情不好!我知道我问了一个 XY 问题,但我在谷歌上找不到任何东西,我想也许有人已经尝试过了。我只是在试验,试图找出新的东西。正如我所说,这是一个示例用例......你是对的......我不知道我在做什么......
  • @krjw 不用担心 ?? 一切都很好。当您觉得自己已经尝试了所有方法却一无所获时,这可能会令人沮丧。希望你能解决你的问题。
  • 我认为如果您在敏感应用程序(例如银行/医疗)中“超时”用户 - 如果用户5 分钟内不使用它,在成功登录后以模态方式显示身份验证屏幕。这使用户不会失去他们在应用程序中的位置。
【解决方案3】:

如果你不介意使用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 {
            $0.isModalInPresentation = value
        }
    }
}

用法:

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

【讨论】:

    【解决方案4】:

    iOS 15

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

    您只需将其附加到工作表:

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

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

    .interactiveDismissDisabled(!isAllInformationProvided)
    

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

    【讨论】:

      【解决方案5】:

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

      private var isDisplayedBind: Binding<Bool>{ Binding(get: { true }, set: { _ = $0 }) }
      

      及用法:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-04
        • 2020-03-30
        • 1970-01-01
        • 2021-10-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多