在2019年的WWDC大会上,苹果宣布了一种新的模态演示的"卡片式"外观,它带来了内置的手势,可以通过向下滑动卡片来解除模态视图控制器.他们还在UIViewController上引入了新的isModalInPresentation属性,这样如果你愿意,你就可以不允许这种解雇行为.

不过,到目前为止,我还没有找到在SwiftUI中模仿这种行为的方法.据我所知,使用.presentation(_ modal: Modal?)不能让你以同样的方式禁用解雇手势.我还try 将模态视图控制器放入UIViewControllerRepresentable View中,但这似乎也没有帮助:

struct MyViewControllerView: UIViewControllerRepresentable {
    func makeUIViewController(context: UIViewControllerRepresentableContext<MyViewControllerView>) -> UIHostingController<MyView> {
        return UIHostingController(rootView: MyView())
    }

    func updateUIViewController(_ uiViewController: UIHostingController<MyView>, context: UIViewControllerRepresentableContext<MyViewControllerView>) {
        uiViewController.isModalInPresentation = true
    }
}

即使在展示了.presentation(Modal(MyViewControllerView()))张照片之后,我仍然能够向下滑动以消除这种看法.目前有没有办法用现有的SwiftUI struct 来实现这一点?

推荐答案

Update for iOS 15

根据下面的pawello2222's answer,新的interactiveDismissDisabled(_:) API现在支持这一点.

struct ContentView: View {
    @State private var showSheet = false

    var body: some View {
        Text("Content View")
            .sheet(isPresented: $showSheet) {
                Text("Sheet View")
                    .interactiveDismissDisabled(true)
            }
    }
}

Pre-iOS-15 answer

我也想这么做,但在任何地方都找不到解决方案.劫持拖动手势的答案是可行的,但当它通过滚动视图或窗体而被取消时,答案就不起作用了.问题中的方法也不太老套,所以我进一步研究了它.

对于我的用例,我有一个表格,理想情况下,当没有内容时,可以将其删除,但当有内容时,必须通过alert 进行确认.

我对这个问题的解决方案是:

struct ModalSheetTest: View {
    @State private var showModally = false
    @State private var showSheet = false
    
    var body: some View {
        Form {
            Toggle(isOn: self.$showModally) {
                Text("Modal")
            }
            Button(action: { self.showSheet = true}) {
                Text("Show sheet")
            }
        }
        .sheet(isPresented: $showSheet) {
            Form {
                Button(action: { self.showSheet = false }) {
                    Text("Hide me")
                }
            }
            .presentation(isModal: self.showModally) {
                print("Attempted to dismiss")
            }
        }
    }
}

状态值showModally确定是否必须以模式显示.如果是这样,将其向下拖动到Disclose只会触发关闭,该关闭只会在示例中打印"Attemed to Disclose",但可用于显示确认解除的alert .

struct ModalView<T: View>: UIViewControllerRepresentable {
    let view: T
    let isModal: Bool
    let onDismissalAttempt: (()->())?
    
    func makeUIViewController(context: Context) -> UIHostingController<T> {
        UIHostingController(rootView: view)
    }
    
    func updateUIViewController(_ uiViewController: UIHostingController<T>, context: Context) {
        context.coordinator.modalView = self
        uiViewController.rootView = view
        uiViewController.parent?.presentationController?.delegate = context.coordinator
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate {
        let modalView: ModalView
        
        init(_ modalView: ModalView) {
            self.modalView = modalView
        }
        
        func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
            !modalView.isModal
        }
        
        func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
            modalView.onDismissalAttempt?()
        }
    }
}

extension View {
    func presentation(isModal: Bool, onDismissalAttempt: (()->())? = nil) -> some View {
        ModalView(view: self, isModal: isModal, onDismissalAttempt: onDismissalAttempt)
    }
}

这非常适合我的用例,希望它也能帮助你或其他人.

Swift相关问答推荐

具有@Published属性的双向数据流动,能够忽略事件

字符串目录不适用于SWIFT包

如何在visionOS上删除PhotosPicker背景 colored颜色 ?

SWIFT异步/等待,多个监听程序

同时具有每个文本的标识符的文本组的可扩展性标识符

调度组是否阻止iOS中的主线程?

SwiftUI .task 视图修改器:运行在哪个线程中?

如何在 SwiftUI 中创建条件内容?

一组可散列的自定义元素插入相等的元素

有没有更快的方法来循环浏览 macOS 上已安装的应用程序?

类型 '()' 不能符合 View (除非它肯定是 View,这次没有恶作剧)

从 Swift 列表中的行中检索值

如何在 ZStack 中单独下移卡片?

在 xcode 13 中的构建之间保持可访问性权限

Xcode swift ui 错误KeyPathComparator仅在 macOS 12.0 或更高版本中可用

在 Swift 中返回实例类型

哪些版本的 Xcode 支持哪些版本的 Swift?

如何交换快速数组中的元素?

如何在 SwiftUI 中为删除和禁用配置 ContextMenu 按钮?

如何停止 NSTimer.scheduledTimerWithTimeInterval