SwiftUI - Transición animada

Sep 05 2020

Tengo un botón de cierre de sesión en una hoja modal que lleva al usuario a la pantalla de inicio de sesión. Para lograr esto, primero descarto la hoja y luego, usando asyncAfter (fecha límite :) establezco una variable de entorno que hace que aparezca la página de inicio de sesión. Todo funciona bien, pero una vez que se descarta la hoja, la transición de la vista debajo de la hoja a la página de inicio de sesión es bastante discordante. Sobre todo porque no hay uno. La vista superior simplemente desaparece, revelando la vista de inicio de sesión. Sé que puedo crear transiciones personalizadas, pero no sé dónde adjuntarlo. Digamos, por ejemplo, que quiero atenuar la vista debajo de la hoja. (¡Aunque estoy abierto a cualquier tipo de transición!)

Esta es la estructura que dirige el tráfico:

struct ConductorView: View {
   @EnvironmentObject var tower: Tower
   let onboardingCompleted = UserDefaults.standard.bool(forKey: "FirstVisit")
    
   var body: some View {
      VStack {
         if tower.currentPage == .onboarding {
            Onboarding1View()
         } else if tower.currentPage == .login {
            LoginView()
         } else if tower.currentPage == .idle {
            LoginView()
         }
      }.onAppear{
         if self.onboardingCompleted {
            self.tower.currentPage = .login
         } else {
            self.tower.currentPage = .onboarding
         }
      }
   }
}

Y este es el botón de cerrar sesión en la hoja:

Button(action: {
   self.presentationMode.wrappedValue.dismiss()
   DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
      self.tower.currentPage = .login
   }
}) {
   Text("Sign Out")
}

Respuestas

1 Asperi Sep 05 2020 at 00:56

Aquí hay una demostración simplificada de su código replicado (e hice un retraso más largo para hacerlo visible). Por supuesto, necesitará ajustarlo a sus necesidades cambiando el tipo de transición o animación, etc. Probado con Xcode 12 / iOS 14

class Tower: ObservableObject {
    enum PageType {
        case onboarding, login, idle
    }
    @Published var currentPage: PageType = .onboarding
}

struct ConductorView: View {
   @EnvironmentObject var tower: Tower
   let onboardingCompleted = false

   var body: some View {
      VStack {
         if tower.currentPage == .onboarding {
            Onboarding1View()
         } else if tower.currentPage == .login {
            Text("LoginView")
                .transition(.move(edge: .trailing))    // << here !!
         } else if tower.currentPage == .idle {
            Text("IdleView")
         }
      }
      .animation(.default, value: tower.currentPage)   // << here !!
      .onAppear{
         if self.onboardingCompleted {
            self.tower.currentPage = .login
         } else {
            self.tower.currentPage = .onboarding
         }
      }
   }
}


struct Onboarding1View: View {
   @EnvironmentObject var tower: Tower
    @Environment(\.presentationMode) var presentationMode
    @State private var isPresented = true
    var body: some View {
        Text("Login")
            .sheet(isPresented: $isPresented) {
                Button(action: {
                   self.presentationMode.wrappedValue.dismiss()
                   DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                      self.tower.currentPage = .login
                   }
                }) {
                   Text("Sign Out")
                }
            }
    }
}