SwiftUI - animacja przejścia

Sep 05 2020

Mam przycisk wylogowania w arkuszu modalnym, który przenosi użytkownika z powrotem do ekranu logowania. Aby to osiągnąć, najpierw zwalniam arkusz, a następnie za pomocą asyncAfter (deadline :) ustawiam zmienną środowiskową, która powoduje wyświetlenie strony logowania. Wszystko działa dobrze, ale po odrzuceniu arkusza przejście z widoku pod arkuszem do strony logowania jest dość irytujące. Głównie dlatego, że takiego nie ma. Widok z góry po prostu znika, odsłaniając widok logowania. Wiem, że mogę tworzyć niestandardowe przejścia, ale nie wiem, gdzie je dołączyć. Powiedzmy na przykład, że chcę wygasić widok pod arkuszem. (Chociaż jestem otwarty na wszelkie zmiany!)

Oto struktura kierująca ruchem:

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

A to jest przycisk wylogowania na arkuszu:

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

Odpowiedzi

1 Asperi Sep 05 2020 at 00:56

Oto uproszczone demo twojego zreplikowanego kodu (i zrobiłem trochę dłuższe opóźnienie, aby był widoczny). Oczywiście będziesz musiał dostosować go do swoich potrzeb, zmieniając rodzaj przejścia lub animacji itp. Testowane z 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")
                }
            }
    }
}