如何在SwiftUI中以模态方式将下一个屏幕推到全屏显示

4
如何在不使用下滑手势(像Xcode 10模态展示)的情况下将下一个SwiftUI视图推入并全屏呈现。
我当前的实现,但它没有推入全屏(启用了下拉拖动手势并在顶部留有间隙):
btn
.presentation(
      !showModal.value ?
           nil :
           Popover(content: destination, dismissHandler: onTrigger ?? {})
)

我认为这是一个缺失的功能,因为SwiftUI(iOS13)仍处于测试版阶段。 - Ugo Arangino
@UgoArangino Popover是什么意思? - SwiftiSwift
1
https://dev59.com/RFMH5IYBdhLWcg3wnAWa#58970681 - Ernist Isabekov
3个回答

3

我认为目前唯一的方法是使用overlay()ZStack。当使用overlay()时,似乎无法使过渡效果正常工作,但是当使用ZStack时,就可以实现过渡效果。

只需确保您的模态视图填充整个屏幕,例如使用List或使用Spacer(),否则您仍将看到其他视图在其后面。

struct ContentView: View {
    
    @State var showModal = false
    
    let transition = AnyTransition.move(edge: .bottom)
    
    var body: some View {
        ZStack {
            VStack {
                Button(action: {
                    withAnimation {
                        self.showModal = true
                    }
                }) {
                    Text("Show Modal")
                }
            }
            
            if self.showModal {
                ModalView()
                    .background(Color.white)
                    .transition(transition)
            }
        }
    }
}

3

fullScreenCover()修饰符

iOS 14引入了一个名为fullScreenCover()的新SwiftUI修饰符。它是一种全屏模态呈现样式,与常规sheet几乎完全相同。例如,当按钮被按下时,将呈现一个全屏模态视图:

struct ContentView: View {
 Button("Present Full Screen Modal!") {
            self.isPresented.toggle()
        }
        .fullScreenCover(isPresented: $isPresented) {
            NavigationView {
                FullScreenModalView()
                    .toolbar {
                        ToolbarItem(placement: .navigationBarTrailing) {
                            Button(action: {
                                isPresented = false
                            }, label: {
                                Text("Dismiss")
                            })
                        }
                    }
            }
        }
    }
}

目标视图可以通过向下滑动或更好的方式来解除,如苹果建议,添加一个解除按钮(已在上面的导航工具栏中添加):

struct FullScreenModalView: View {
    var body: some View {
        ZStack {
            Color.pink
            Text("This my full screen modal view")
                .foregroundColor(.white)
        }
        .ignoresSafeArea()
        .onTapGesture {
            presentationMode.wrappedValue.dismiss()
        }
    }
}

0

简短的回答是目前没有好的方法来做到这一点。

以下是几个替代方案:

使用UIKit + Swift混合编程

请参阅在SwiftUI中呈现新视图

附加条件视图

第一个答案涵盖了此解决方案。基本上,您可以使用Bool在“基础”视图的顶部或代替它来呈现您的“模态”视图。

类似于这样:


struct ModalView: View {
    var closeAction: (() -> Void) = {}
    var body: some View {
        ZStack {
            Color.blue.edgesIgnoringSafeArea(.all)
            VStack {
                Text("I am a modal.")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                    .foregroundColor(.white)
                    .padding()
                Button(action: {
                    self.closeAction()
                }, label: {
                    Text("OK, BYE!")
                        .foregroundColor(.white)
                        .padding()
                        .overlay(
                            RoundedRectangle(cornerRadius: 5)
                                .stroke(Color.white, lineWidth: 1)
                    )
                })
            }
        }
    }
}

struct BaseView: View {
    @State private var showModal = false
    var body: some View {
        ZStack {
            if showModal {
                ModalView(closeAction: {
                    withAnimation(.easeOut(duration: 0.25)) { self.showModal = false }
                }).transition(.slideBottom)
            } else {
                VStack {
                    Button(action: {
                        withAnimation(.easeOut(duration: 0.25)) {
                            self.showModal = true
                        }
                    }, label: {
                        Text("Open Modal")
                            .padding()
                            .overlay(
                                RoundedRectangle(cornerRadius: 5)
                                    .stroke(Color.blue, lineWidth: 1)
                        )
                    })
                }
            }
        }.statusBar(hidden: true)
    }
}

它有自己的一套问题:

  • 不是真正的模态呈现(?)。 “基础”视图及其所有视图层次仍然存在于“模态”下。如果您想支持可访问性,这可能会很麻烦。当然,您可以呈现条件模态而不是在基础视图之上。此设置有效,但需要编写得相当聪明才能很好地扩展。
  • 您将不得不添加自己的动画过渡。
  • 除非隐藏其导航栏,否则如果基础视图嵌入到NavigationView根中,则无法工作
  • 除非隐藏其导航栏和返回按钮,否则如果基础视图嵌入到使用NavigationLink呈现的NavigationView子级中,则无法工作
  • 通常情况下,当将修饰符添加到给定布局中不是根视图的视图时,它将不再是全屏模态。

完整探索请参见https://github.com/piterwilson/SwiftUI-Modal-on-iPad/tree/master/iPadConditionalViewModal和我创建的这个ViewModifier,使代码更加简洁https://github.com/piterwilson/SwiftUI-FullscreenModalViewModifier

使用NavigationView+NavigationLink

您也可以使用NavigationView+NavigationLink来全屏显示,但最大的问题是您无法自定义动画。它看起来像这样:

struct ModalView: DismissableView {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)
            VStack {
                Text("I am a modal.")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                    .foregroundColor(.white)
                    .padding()
                Button(action: {
                    self.dismiss()
                }, label: {
                    Text("OK, BYE!")
                        .foregroundColor(.white)
                        .padding()
                        .overlay(
                            RoundedRectangle(cornerRadius: 5)
                                .stroke(Color.white, lineWidth: 1)
                    )
                })
            }
        }.navigationBarBackButtonHidden(true)
    }
}

struct BaseView: View {
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: ModalView()) {
                    Text("Open Modal")
                    .padding()
                    .overlay(
                        RoundedRectangle(cornerRadius: 5).stroke(Color.blue, lineWidth: 1)
                    )
                }
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

它也存在一些问题:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接