Ios 防止在SwiftUI中取消模态视图控制器
在2019年WWDC大会上,苹果宣布了一种新的模态演示“卡片式”外观,该外观附带了内置手势,可通过向下滑动卡片来解除模态视图控制器。他们还在Ios 防止在SwiftUI中取消模态视图控制器,ios,swift,swiftui,Ios,Swift,Swiftui,在2019年WWDC大会上,苹果宣布了一种新的模态演示“卡片式”外观,该外观附带了内置手势,可通过向下滑动卡片来解除模态视图控制器。他们还在UIViewController上引入了新的isModalInPresentation属性,以便您可以在选择的情况下禁止这种解雇行为 不过,到目前为止,我还没有找到在SwiftUI中模仿这种行为的方法。据我所知,使用.presentation(u.modal:modal?)不允许您以相同的方式禁用解雇手势。我还尝试将模态视图控制器放入UIViewContro
UIViewController
上引入了新的isModalInPresentation
属性,以便您可以在选择的情况下禁止这种解雇行为
不过,到目前为止,我还没有找到在SwiftUI中模仿这种行为的方法。据我所知,使用.presentation(u.modal:modal?
)不允许您以相同的方式禁用解雇手势。我还尝试将模态视图控制器放入UIViewControllerRepresentable
视图
,但这似乎也没有帮助:
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
}
}
struct MyViewControllerView:UIViewControllerRepresentable{
func makeUIViewController(上下文:UIViewControllerRepresentableContext)->UIHostingController{
返回UIHostingController(rootView:MyView())
}
func updateUIViewController(uViewController:UIHostingController,上下文:UIViewControllerRepresentableContext){
uiViewController.isModalInPresentation=true
}
}
即使在使用
.presentation(Modal(MyViewControllerView())
演示之后,我仍然能够向下滑动以关闭视图。当前是否有任何方法可以使用现有的SwiftUI构造实现这一点?通过更改不希望拖动的任何视图的手势优先级
,可以阻止任何视图上的拖动。例如,对于Modal,可按以下方式进行:
也许这不是一个最佳实践,但它非常有效
struct ContentView: View {
@State var showModal = true
var body: some View {
Button(action: {
self.showModal.toggle()
}) {
Text("Show Modal")
}.sheet(isPresented: self.$showModal) {
ModalView()
}
}
}
注意:为了清晰和简洁,对该代码进行了编辑
使用从中获取当前窗口场景的方法,可以通过此扩展从中获取俯视图控制器
并将其转换为视图修改器,如下所示:
struct DisableModalDismiss: ViewModifier {
let disabled: Bool
func body(content: Content) -> some View {
disableModalDismiss()
return AnyView(content)
}
func disableModalDismiss() {
guard let visibleController = UIApplication.shared.visibleViewController() else { return }
visibleController.isModalInPresentation = disabled
}
}
并使用类似于:
struct ShowSheetView: View {
@State private var showSheet = true
var body: some View {
Text("Hello, World!")
.sheet(isPresented: $showSheet) {
TestView()
.modifier(DisableModalDismiss(disabled: true))
}
}
}
iOS 15的更新
根据下面的,新的interactiveDismissDisabled(:)
API现在支持这一点
struct ContentView:View{
@国家私有var显示表=false
var body:一些观点{
文本(“内容视图”)
.sheet(显示:$showSheet){
文本(“图纸视图”)
.interactiveDismissDisabled(真)
}
}
}
iOS-15之前的答案
我也想这样做,但在任何地方都找不到解决方案。劫持拖动手势的答案是可行的,但当它通过滚动滚动视图或表单而被取消时就不行了。问题中的方法也不太老套,所以我进一步研究了它
对于我的用例,我有一个表单,理想情况下,当没有内容时可以将其删除,但当有内容时必须通过警报进行确认
我对此问题的解决方案:
struct ModalSheetTest:视图{
@State private var showModally=false
@国家私有var显示表=false
var body:一些观点{
形式{
切换(isOn:self.$showmodly){
文本(“模态”)
}
按钮(操作:{self.showSheet=true}){
文本(“显示页”)
}
}
.sheet(显示:$showSheet){
形式{
按钮(操作:{self.showSheet=false}){
文本(“隐藏我”)
}
}
.presentation(isModal:self.showmodly){
打印(“试图驳回”)
}
}
}
}
状态值showmodly
确定是否必须以模式显示。如果是这样,将其向下拖动到Disclose只会触发关闭,该关闭在示例中仅打印“试图解除”,但可用于显示确认解除的警报
struct ModalView:UIViewControllerRepresentable{
让我们来看一看:T
让伊斯莫达尔:布尔
让我们试一下:(()->())?
func makeUIViewController(上下文:context)->UIHostingController{
UIHostingController(根视图:视图)
}
func updateUIViewController(uViewController:UIHostingController,上下文:上下文){
context.coordinator.modalView=self
uiViewController.rootView=视图
uiViewController.parent?.presentationController?.delegate=context.coordinator
}
func makeCoordinator()->Coordinator{
协调员(自我)
}
类协调器:NSObject,UIAdaptivePresentationControllerDelegate{
让莫达维:莫达维
init(modalView:modalView){
self.modalView=modalView
}
func presentationController应关闭(presentationController:UIPresentationController)->Bool{
!modalView.isModal
}
func presentationController DidAttemptToDismiss(presentationController:UIPresentationController){
modalView.Ondismissalatter?()
}
}
}
扩展视图{
函数表示(isModal:Bool,ONDISMISSALATTENT:(()->())?=nil)->一些视图{
ModalView(视图:self、isModal:isModal、ondisIsSalattest:ondisIsSalattest)
}
}
这非常适合我的用例,希望它也能帮助您或其他人。我们已经创建了一个扩展,可以轻松地控制模式解除,至少
可以使用此方法传递模态视图的内容以供重用
使用具有手势优先级的NavigationView来禁用拖动
import SwiftUI
struct ModalView<Content: View>: View
{
@Environment(\.presentationMode) var presentationMode
let content: Content
let title: String
let dg = DragGesture()
init(title: String, @ViewBuilder content: @escaping () -> Content) {
self.content = content()
self.title = title
}
var body: some View
{
NavigationView
{
ZStack (alignment: .top)
{
self.content
}
.navigationBarTitleDisplayMode(.inline)
.toolbar(content: {
ToolbarItem(placement: .principal, content: {
Text(title)
})
ToolbarItem(placement: .navigationBarTrailing, content: {
Button("Done") {
self.presentationMode.wrappedValue.dismiss()
}
})
})
}
.highPriorityGesture(dg)
}
}
结果
这个解决方案在iPhone和iPad上对我很有效。它使用isModalInPresentation
。发件人:
此属性的默认值为false。将其设置为true时,UIKit将忽略视图控制器边界之外的事件,并防止视图控制器在屏幕上时被交互式取消
你的尝试接近于我的成功。诀窍在于设置
struct ShowSheetView: View {
@State private var showSheet = true
var body: some View {
Text("Hello, World!")
.sheet(isPresented: $showSheet) {
TestView()
.modifier(DisableModalDismiss(disabled: true))
}
}
}
/// Example:
struct ContentView: View {
@State private var presenting = false
var body: some View {
VStack {
Button {
presenting = true
} label: {
Text("Present")
}
}
.sheet(isPresented: $presenting) {
ModalContent()
.allowAutoDismiss { false }
// or
// .allowAutoDismiss(false)
}
}
}
import SwiftUI
struct ModalView<Content: View>: View
{
@Environment(\.presentationMode) var presentationMode
let content: Content
let title: String
let dg = DragGesture()
init(title: String, @ViewBuilder content: @escaping () -> Content) {
self.content = content()
self.title = title
}
var body: some View
{
NavigationView
{
ZStack (alignment: .top)
{
self.content
}
.navigationBarTitleDisplayMode(.inline)
.toolbar(content: {
ToolbarItem(placement: .principal, content: {
Text(title)
})
ToolbarItem(placement: .navigationBarTrailing, content: {
Button("Done") {
self.presentationMode.wrappedValue.dismiss()
}
})
})
}
.highPriorityGesture(dg)
}
}
struct ContentView: View {
@State var showModal = true
var body: some View {
Button(action: {
self.showModal.toggle()
}) {
Text("Show Modal")
}.sheet(isPresented: self.$showModal) {
ModalView (title: "Title") {
Text("Prevent dismissal of modal view.")
}
}
}
}
class MyHostingController<Content: View>: UIHostingController<Content> {
var canDismissSheet = true
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
parent?.isModalInPresentation = !canDismissSheet
}
}
struct MyViewControllerView<Content: View>: UIViewControllerRepresentable {
let content: Content
let canDismissSheet: Bool
func makeUIViewController(context: Context) -> UIHostingController<Content> {
let viewController = MyHostingController(rootView: content)
viewController.canDismissSheet = canDismissSheet
return viewController
}
func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: Context) {
uiViewController.parent?.isModalInPresentation = !canDismissSheet
}
}
struct FullScreenCoverPresenterView: View {
@State private var isPresenting = false
var body: some View {
Button("Present Full-Screen Cover") {
isPresenting.toggle()
}
.fullScreenCover(isPresented: $isPresenting) {
Text("Tap to Dismiss")
.onTapGesture {
isPresenting.toggle()
}
}
}
}
func interactiveDismissDisabled(_ isDisabled: Bool = true) -> some View
struct ContentView: View {
@State private var showSheet = false
var body: some View {
Text("Content View")
.sheet(isPresented: $showSheet) {
Text("Sheet View")
.interactiveDismissDisabled(true)
}
}
}
.interactiveDismissDisabled(!userAcceptedTermsOfUse)