在SwiftUI中呈现新视图


11

我想单击一个按钮,然后像present modally在UIKit中 一样呈现一个新视图在此处输入图片说明

我已经看过“ 如何使用图纸呈现新视图 ”,但是我不想将其作为模式图纸附加到主视图。

而且我不想使用NavigationLink,因为我不希望新视图和旧视图具有导航关系。

谢谢你的帮助...


为什么不想将其作为模式表附加到主视图?即使在中也是一种标准方法UIKit。您有什么特殊原因吗?
Mojtaba Hosseini

我试图解释我的想法...如果有什么问题,请纠正我。
CH翼

该应用程序具有3个视图:1:登录页面2:TableView页面3:TableDetail页面,TableView页面和TableDetail页面是导航关系。登录后将显示在TableView页面上,登录后TableView页面与登录页面没有任何关系
CH Wing

所以你需要它是fullscreen对的?
Mojtaba Hosseini

是的!我想要fullscreen
CH翼

Answers:


12

显示模态(iOS 13样式)

您只需要一个sheet具有自我解散能力的简单装置即可:

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

并呈现为:

struct ContentView: View {
    @State var presentingModal = false

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

请注意,我将传递presentingModal给了模态,因此您可以将其从模态本身中删除,但是您可以摆脱它。


使其真正呈现fullscreen(不仅在视觉上)

您需要访问ViewController。因此,您需要一些辅助容器和环境材料:

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 }
    }
}

然后,您应该使用实现此扩展:

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)
        )
        self.present(toPresent, animated: true, completion: nil)
    }
}

最后

您可以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
            }
        }
    }
}

大!感谢您的详细解决方案
CH Wing

我在环境属性包装器中收到此错误:无法将类型'Environment <UIViewController?>'的值转换为指定的类型'UIViewController'–
jsbeginnerNodeJS

默认情况下应该处理它,但是尝试?在该行的末尾添加。@jsbeginnerNodeJS
Mojtaba Hosseini

我在控制台中此错误:```警告:试图提出<_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_:0x7fafd2641d30>在<_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_:0x7fafd2611bd0>谁的观点是不是在窗口层次```!
jsbeginnerNodeJS

您如何解雇它?
gabrielapittari

0

这是一种简单的方法-向前看。非常简单。

        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)
                                }}
                            }
                }
                }
        }

-1

免责声明:以下内容并不是真正的“本机模式”,既不表现也不美观,但是如果任何人都需要自定义过渡一个视图而不是另一个视图,仅激活顶部视图,则以下方法可能会有所帮助。

因此,如果您期望以下内容

自定义SwiftUI模态

这是演示方法的简单代码(Corse动画和过渡参数可以根据需要更改)

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)
            if showingModal {
                ModalView(activeModal: $showingModal)
                    .transition(.move(edge: .bottom))
                    .zIndex(1)
            }
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.