简体   繁体   English

在 SwiftUI 中呈现新视图

[英]Present a new view in SwiftUI

I want to click a button and then present a new view like present modally in UIKit我想单击一个按钮,然后在 UIKit 中以present modally方式呈现一个新视图在此处输入图像描述

I have already seen " How to present a new view using sheets ", but I don't want to attach it to the main view as a modal sheet.我已经看过“ 如何使用工作表呈现新视图”,但我不想将其作为模式工作表附加到主视图。

And I don't want to use NavigationLink , because I don't want a new view and old view have a navigation relationship.而且我不想使用NavigationLink ,因为我不希望新视图和旧视图有导航关系。

Thanks for your help...谢谢你的帮助...

To show a modal (iOS 13 style)显示模式(iOS 13 风格)

You just need a simple sheet with the ability to dismiss itself:您只需要一张能够自行关闭的简单sheet

struct ModalView: View {
    @Binding var presentedAsModal: Bool
    var body: some View {
        Button("dismiss") { self.presentedAsModal = false }
    }
}

And present it like:并将其呈现为:

struct ContentView: View {
    @State var presentingModal = false
    
    var body: some View {
        Button("Present") { self.presentingModal = true }
        .sheet(isPresented: $presentingModal) { ModalView(presentedAsModal: self.$presentingModal) }
    }
}

Note that I passed the presentingModal to the modal so you can dismiss it from the modal itself, but you can get rid of it.请注意,我将presentingModal传递给模态,因此您可以将其从模态本身中消除,但您可以摆脱它。


To make it REALLY present fullscreen (Not just visually)让它真正呈现fullscreen (不仅仅是视觉上)

You need to access to the ViewController .您需要访问ViewController So you need some helper containers and environment stuff:所以你需要一些辅助容器和环境的东西:

struct ViewControllerHolder {
    weak var value: UIViewController?
}

struct ViewControllerKey: EnvironmentKey {
    static var defaultValue: ViewControllerHolder {
        return ViewControllerHolder(value: UIApplication.shared.windows.first?.rootViewController)

    }
}

extension EnvironmentValues {
    var viewController: UIViewController? {
        get { return self[ViewControllerKey.self].value }
        set { self[ViewControllerKey.self].value = newValue }
    }
}

Then you should use implement this extension:然后你应该使用实现这个扩展:

extension UIViewController {
    func present<Content: View>(style: UIModalPresentationStyle = .automatic, @ViewBuilder builder: () -> Content) {
        let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
        toPresent.modalPresentationStyle = style
        toPresent.rootView = AnyView(
            builder()
                .environment(\.viewController, toPresent)
        )
        NotificationCenter.default.addObserver(forName: Notification.Name(rawValue: "dismissModal"), object: nil, queue: nil) { [weak toPresent] _ in
            toPresent?.dismiss(animated: true, completion: nil)
        }
        self.present(toPresent, animated: true, completion: nil)
    }
}

Finally最后

you can make it fullscreen like:你可以让它fullscreen像:

struct ContentView: View {
    @Environment(\.viewController) private var viewControllerHolder: UIViewController?
    
    var body: some View {
        Button("Login") {
            self.viewControllerHolder?.present(style: .fullScreen) {
                Text("Main") // Or any other view you like
// uncomment and add the below button for dismissing the modal
            // Button("Cancel") {
            //       NotificationCenter.default.post(name: Notification.Name(rawValue: "dismissModal"), object: nil)
            //        }
            }
        }
    }
}

For iOS 14 and Xcode 12:对于 iOS 14 和 Xcode 12:

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

var body: some View {
    Button("Show Modal with full screen") {
        self.isPresented.toggle()
    }
    .fullScreenCover(isPresented: $isPresented, content: FullScreenModalView.init)
    }
}
struct FullScreenModalView: View {
     @Environment(\.presentationMode) var presentationMode

var body: some View {
    VStack {
        Text("This is a modal view")
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
    .background(Color.red)
    .edgesIgnoringSafeArea(.all)
    .onTapGesture {
        presentationMode.wrappedValue.dismiss()
    }
}
}

Hope this answer can help you all.希望这个答案可以帮助到大家。 Comment below about your result.在下面评论您的结果。

Ref: This Link参考: 此链接

Disclaimer: Below is not really like a "native modal", neither behave nor look&feel, but if anyone would need a custom transition of one view over other, making active only top one, the following approach might be helpful.免责声明:下面并不是真正的“本机模式”,既不是行为也不是外观和感觉,但如果有人需要一个视图的自定义转换,只激活顶部视图,以下方法可能会有所帮助。

So, if you expect something like the following因此,如果您期望以下内容

自定义 SwiftUI 模态

Here is a simple code for demo the approach (of corse animation & transition parameters can be changed by wish)这是演示方法的简单代码(可根据需要更改 animation 和转换参数)

struct ModalView : View {
    @Binding var activeModal: Bool
    var body : some View {
        VStack {
            Button(action: {
                withAnimation(.easeInOut(duration: 0.3)) {
                    self.activeModal = false
                }
            }) {
                Text("Hide modal")
            }
            Text("Modal View")
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
        .background(Color.green)
    }
}

struct MainView : View {
    @Binding var activeModal: Bool
    var body : some View {
        VStack {
            Button(action: {
                withAnimation(.easeInOut(duration: 0.3)) {
                    self.activeModal = true
                }
            }) {
                Text("Show modal")
            }
            Text("Main View")
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
        .background(Color.yellow)
    }
}

struct ModalContainer: View {
    @State var showingModal = false
    var body: some View {
        ZStack {
            MainView(activeModal: $showingModal)
                .allowsHitTesting(!showingModal)
                .disabled(showingModal)
            if showingModal {
                ModalView(activeModal: $showingModal)
                    .transition(.move(edge: .bottom))
                    .zIndex(1)
            }
        }
    }
}

backup 备份

Here is a simple one way - forward views.这是一种简单的方法——前瞻观点。 It's very straight forward.这是非常直接的。

        struct ChildView: View{
           private  let colors: [Color] = [.red, .yellow,.green,.white]
           @Binding var index : Int
           var body: some View {
           let next = (self.index+1)  % MyContainer.totalChildren
             return   ZStack{
                    colors[self.index  % colors.count]
                     Button("myNextView \(next)   ", action: {
                    withAnimation{
                        self.index = next
                    }
                    }
                )}.transition(.asymmetric(insertion: .move(edge: .trailing)  , removal:  .move(edge: .leading)  ))
            }
        }

        struct MyContainer: View {
            static var totalChildren = 10
            @State private var value: Int = 0
            var body: some View {
                    HStack{
                        ForEach(0..<(Self.totalChildren) ) { index in
                            Group{
                            if    index == self.value {
                                ChildView(index:  self.$value)
                                }}
                            }
                }
                }
        }

then presents it from ContentView when a button is tapped:然后在点击按钮时从 ContentView 呈现它:

struct SheetView: View {
        @Environment(\.dismiss) var dismiss
    
        var body: some View {
            Button("Press to dismiss") {
                dismiss()
            }
            .font(.title)
            .padding()
            .background(Color.black)
        }
    }
    
    struct ContentView: View {
        @State private var showingSheet = false
    
        var body: some View {
            Button("Show Sheet") {
                showingSheet.toggle()
            }
            .sheet(isPresented: $showingSheet) {
                SheetView()
            }
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM