Apresentar uma nova visualização no SwiftUI

11

Quero clicar em um botão e apresentar uma nova exibição, como present modallyno UIKit insira a descrição da imagem aqui

Eu já vi " Como apresentar uma nova exibição usando planilhas ", mas não quero anexá-la à exibição principal como uma planilha modal.

E não quero usar NavigationLink, porque não quero uma nova exibição e a exibição antiga tenha um relacionamento de navegação.

Obrigado pela ajuda...

CH Wing
fonte
Por que você não deseja anexá-lo à vista principal como uma folha modal? É um método padrão, mesmo em UIKit. Você tem algum motivo especial?
Mojtaba Hosseini
Eu tento explicar meus pensamentos ... Se houver algo errado, por favor me corrija.
CH Wing
Os aplicativos têm 3 visualizações, 1: Página de login 2: TabelaView Página 3: Página TableDetail, página TableView e página TableDetail é a relação de navegação. Depois que o login for apresentado na página TableView, a página TableView não terá qualquer relação com a página de login após o login
CH Wing
Então você precisa estar fullscreencerto?
Mojtaba Hosseini
ys! eu querofullscreen
CH Ala

Respostas:

12

Para mostrar um modal (estilo iOS 13)

Você só precisa de um simples sheetcom a capacidade de se demitir:

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

E apresente-a como:

struct ContentView: View {
    @State var presentingModal = false

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

Observe que eu passei o presentingModalpara o modal para que você possa descartá-lo do próprio modal, mas você pode se livrar dele.


Para torná-lo realmente presente fullscreen(não apenas visualmente)

Você precisa acessar o ViewController. Então, você precisa de alguns contêineres auxiliares e coisas do ambiente:

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

Então você deve usar implementar esta extensão:

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

você pode fazer fullscreenassim:

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
fonte
ótimo! obrigado pela sua solução detalhada
CH Wing
Eu recebo este erro no invólucro da propriedade de ambiente: Não é possível converter o valor do tipo 'Ambiente <UIViewController?>' Para o tipo especificado 'UIViewController'
jsbeginnerNodeJS
Ele deve ser tratado por padrão, mas tente adicionar ?no final da linha. @jsbeginnerNodeJS
Mojtaba Hosseini
Eu recebo este erro no console: `` `Aviso: Tentativa de presente <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7fafd2641d30> em <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7fafd2611bd0> cuja visão não é na hierarquia janela` ``!
jsbeginnerNodeJS
como você descarta isso?
gabrielapittari
0

Aqui está uma maneira simples - visualizações avançadas. É muito direto.

        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
fonte
-1

Isenção de responsabilidade: abaixo não é realmente como um "modal nativo", não se comporta nem parece, mas se alguém precisar de uma transição personalizada de uma visualização sobre outra, ativando apenas a primeira, a abordagem a seguir pode ser útil.

Então, se você espera algo como o seguinte

modal SwiftUI personalizado

Aqui está um código simples para demonstração da abordagem (os parâmetros de animação e transição da corse podem ser alterados por desejo)

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
fonte