Presentar una nueva vista en SwiftUI

11

Quiero hacer clic en un botón y luego presentar una nueva vista como present modally en UIKit ingrese la descripción de la imagen aquí

Ya he visto " Cómo presentar una nueva vista usando hojas ", pero no quiero adjuntarla a la vista principal como una hoja modal.

Y no quiero usar NavigationLink , porque no quiero una nueva vista y la vista anterior tiene una relación de navegación.

Gracias por tu ayuda...

Ala CH
fuente
¿Por qué no desea adjuntarlo a la vista principal como una hoja modal? Es un método estándar incluso en UIKit. ¿Tienes alguna razón especial?
Mojtaba Hosseini
Trato de explicar mis pensamientos ... Si hay algo mal, corrígeme.
Ala CH
Las aplicaciones tienen 3 vistas, 1: Página de inicio de sesión 2: Página TableView 3: Página TableDetail, página TableView y página TableDetail es la relación de navegación. Después de que el inicio de sesión se presente en la página TableView, la página TableView no tiene ninguna relación con la página de inicio de sesión después del inicio de sesión
CH Wing
¿Entonces necesitas que sea fullscreencorrecto?
Mojtaba Hosseini
ys! Quierofullscreen
CH Wing

Respuestas:

12

Para mostrar un modal (estilo iOS 13)

Solo necesita un simple sheetcon la capacidad de despedirse a sí mismo:

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

Y presentarlo como:

struct ContentView: View {
    @State var presentingModal = false

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

Tenga en cuenta que pasé el presentingModalal modal para que pueda descartarlo del modal en sí, pero puede deshacerse de él.


Para hacerlo REALMENTE presente fullscreen (no solo visualmente)

Necesita acceder a la ViewController. Entonces necesita algunos contenedores auxiliares y cosas del entorno:

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

Entonces deberías usar implementar esta extensión:

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

Finalmente

puedes hacerlo fullscreenasí:

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
            }
        }
    }
}
Mojtaba Hosseini
fuente
¡estupendo! gracias por su solución detallada
CH Wing
Recibo este error en el contenedor de propiedades del entorno: no se puede convertir el valor del tipo 'Entorno <UIViewController?>' Al tipo especificado 'UIViewController'
jsbeginnerNodeJS
Debe manejarse de manera predeterminada, pero intente agregar allí ?al final de la línea. @jsbeginnerNodeJS
Mojtaba Hosseini
Me sale este error en la consola: `` `Advertencia: intento de presentar <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7fafd2641d30> en <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7fafd2611bd0> cuya visión no está en la jerarquía de la ventana` ``!
jsbeginnerNodeJS
¿Cómo lo descartas?
gabrielapittari
0

Aquí hay una manera simple: vistas hacia adelante. Es muy sencillo.

        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)
                                }}
                            }
                }
                }
        }
E.Coms
fuente
-1

Descargo de responsabilidad: a continuación no se parece realmente a un "modo nativo", ni se comporta ni se ve y se siente, pero si alguien necesita una transición personalizada de una vista sobre otra, activando solo la primera, el siguiente enfoque podría ser útil.

Entonces, si espera algo como lo siguiente

SwiftUI modal personalizado

Aquí hay un código simple para demostrar el enfoque (los parámetros de transición y animación de corse se pueden cambiar por deseo)

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