SwiftUI - Animasi transisi

Sep 05 2020

Saya memiliki tombol keluar pada lembar modal yang membawa pengguna kembali ke layar masuk. Untuk mencapai ini saya pertama menutup sheet dan kemudian, menggunakan asyncAfter (deadline :) saya menetapkan variabel lingkungan yang menyebabkan halaman login muncul. Semuanya berfungsi dengan baik, tetapi setelah sheet ditutup, transisi dari tampilan di bawah sheet ke halaman login cukup mengejutkan. Sebagian besar karena tidak ada. Tampilan atas menghilang begitu saja, menampilkan tampilan login. Saya tahu saya dapat membuat transisi khusus, tetapi saya tidak tahu di mana harus memasangnya. Katakanlah, misalnya, saya ingin memudarkan tampilan di bawah lembar. (Meskipun, saya terbuka untuk segala jenis transisi!)

Ini adalah struct yang mengarahkan lalu lintas:

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

Dan ini adalah tombol keluar di lembar:

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

Jawaban

1 Asperi Sep 05 2020 at 00:56

Berikut adalah demo yang disederhanakan pada kode yang direplikasi Anda (dan saya membuat penundaan lebih lama untuk membuatnya terlihat mode). Tentu saja Anda perlu menyesuaikannya dengan kebutuhan Anda dengan mengubah jenis transisi atau animasi, dll. Diuji dengan 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")
                }
            }
    }
}