Menavigasi antara tampilan menggunakan @EnvironmentObject di SwiftUI

Terjemahan artikel ini disiapkan pada malam peluncuran kursus lanjutan "Pengembang iOS" .




Halo dan selamat datang di tutorial kami! Dalam seri ini, kita berbicara tentang cara menavigasi antara tampilan di SwiftUI (tanpa menggunakan tampilan navigasi!). Meskipun ide ini mungkin tampak sepele, tetapi dengan memahaminya sedikit lebih dalam, kita dapat belajar banyak tentang konsep aliran data yang digunakan dalam SwiftUI.

Pada bagian sebelumnya, kami belajar cara menerapkan ini menggunakan @ObservableObject. Pada bagian ini, kita akan melihat bagaimana melakukan hal yang sama, tetapi lebih efisien menggunakan @EnvironmentObject. Kami juga akan menambahkan animasi transisi kecil.

Inilah yang akan kita capai:


Apa yang kita miliki


Jadi, kami baru tahu cara menavigasi antara tampilan yang berbeda menggunakan ObservableObject. Singkatnya, kami membuat ViewRouter dan mengaitkannya dengan Tampilan Ibu dan Tampilan Konten kami. Selanjutnya, kita cukup memanipulasi properti CurrentRage ViewRouter dengan mengklik tombol Content View. Setelah itu, MotherView diperbarui untuk menampilkan Tampilan Konten yang sesuai!

Tetapi ada cara kedua yang lebih efisien untuk mencapai fungsi ini: gunakan @EnvironmentObject!

Petunjuk: Anda dapat mengunduh perkembangan terbaru di sini (ini adalah folder "NavigateInSwiftUIComplete") : GitHub

Mengapa menggunakan ObservableObjectbukanlah solusi terbaik


Anda mungkin bertanya-tanya: mengapa kita harus menerapkan ini dengan cara lain, ketika kita sudah memiliki solusi yang berfungsi? Nah, jika Anda melihat logika hierarki aplikasi kami, maka itu akan menjadi jelas bagi Anda. MotherView kami adalah tampilan root yang menginisialisasi instance ViewRouter. Dalam MotherView, kami juga menginisialisasi ContentViewA dan ContentViewB, melewati mereka contoh ViewRouter sebagai BindableObject.

Seperti yang Anda lihat, kita harus mengikuti hierarki ketat yang melewati hilir ObservableOb yang diinisialisasi ke semua subview. Sekarang ini tidak begitu penting, tetapi bayangkan aplikasi yang lebih kompleks dengan banyak pandangan. Kita selalu perlu melacak transmisi tampilan root yang dapat diobservasi yang diinisialisasi ke semua subview dan semua subview subview, dll., Yang pada akhirnya bisa menjadi tugas yang agak membosankan.



Untuk meringkas: menggunakan clean ObservableObjectbisa menjadi masalah ketika datang ke hierarki aplikasi yang lebih kompleks.

Sebagai gantinya, kita dapat menginisialisasi ViewRouter ketika aplikasi dimulai sehingga semua tampilan dapat terhubung langsung ke instance ini, atau, lebih tepatnya, menontonnya, terlepas dari hierarki aplikasi. Dalam hal ini, instance ViewRouter akan terlihat seperti awan yang terbang di atas kode aplikasi kita, yang semua akses secara otomatis mengakses tanpa khawatir tentang rantai inisialisasi yang benar di hierarki tampilan.
Ini hanya pekerjaan EnvironmentObject!

Apa EnvironmentObject?


EnvironmentObjectAdalah model data yang, setelah inisialisasi, dapat bertukar data dengan semua representasi aplikasi Anda. Apa yang sangat baik adalah apa yang EnvironmentObjectdiciptakan dengan menyediakan ObservableObject, sehingga kita dapat menggunakan milik kita ViewRouteruntuk membuat EnvironmentObject!

Segera setelah kami menyatakan milik kami ViewRoutersebagai EnvironmentObject, semua representasi dapat dilampirkan padanya dengan cara yang sama seperti yang biasa ObservableObject, tetapi tanpa perlu rantai inisialisasi ke hierarki aplikasi!

Seperti yang telah disebutkan, ini EnvironmentObjectharus sudah diinisialisasi saat pertama kali diakses. Karena properti kita MotherView, sebagai tampilan root, akan melihat properti CurrentPage ViewRouter, kita harus menginisialisasi EnvironmentObjectketika aplikasi dimulai. Kemudian kita dapat secara otomatis mengubah Halaman saat iniEnvironmentObjectdari ContentView, yang kemudian memanggil MotherViewrendering ulang.



Implementasi ViewRoutersebagaiEnvironmentObject


Jadi, mari perbarui kode aplikasi kita!

Pertama, ubah pembungkus properti viewRouterdi dalamnya MotherViewdari @ObservableObjectmenjadi @EnvironmentObject.

import SwiftUI

struct MotherView : View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
//...
    }
}

Properti viewRoutersekarang mencari ViewRouter-EnvironmentObject. Karena itu, kita perlu menyediakan struktur kita MotherView_Previewsdengan contoh yang sesuai:

#if DEBUG
struct MotherView_Previews : PreviewProvider {
    static var previews: some View {
        MotherView().environmentObject(ViewRouter())
    }
}
#endif

Seperti disebutkan di atas - ketika memulai aplikasi kita, itu harus segera disediakan dengan contoh ViewRouterdalam kualitas EnvironmentObject, karena MotherViewsekarang disebut sebagai representasi root EnvironmentObject. Oleh karena itu, perbarui fungsi adegan di dalam file SceneDelegage.swiftsebagai berikut:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: MotherView().environmentObject(ViewRouter()))
            self.window = window
            window.makeKeyAndVisible()
        }
    }

Hebat, sekarang ketika Anda memulai aplikasi, SwiftUI membuat instance ViewRouterdalam kualitas EnvironmentObject, yang mana semua tampilan aplikasi kita sekarang dapat dilampirkan.

Selanjutnya, mari perbarui kami ContentViewA. Ubah viewRouterpropertinya ke EnvironmentObjectdan juga perbarui struktur ContentViewA_Previews.

import SwiftUI

struct ContentViewA : View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    
    var body: some View {
       //...
    }
}
#if DEBUG
struct ContentViewA_Previews : PreviewProvider {
    static var previews: some View {
        ContentViewA().environmentObject(ViewRouter())
    }
}
#endif

Petunjuk: Sekali lagi, struktur ContentViewsA_Previewsmemiliki turunannya sendiri ViewRouter, tetapi ContentViewAdikaitkan dengan turunan yang dibuat ketika aplikasi diluncurkan!

Mari kita ulangi ini untuk ContentViewB:

import SwiftUI

struct ContentViewB : View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    
    var body: some View {
        //...
    }
}
#if DEBUG
struct ContentViewB_Previews : PreviewProvider {
    static var previews: some View {
        ContentViewB().environmentObject(ViewRouter())
    }
}
#endif

Karena properti viewRouterkami ContentViewsekarang terkait langsung dengan / mengamati instance awal ViewRoutersebagai EnvironmentObject, kita tidak perlu lagi menginisialisasi mereka di properti kita MotherView. Jadi, mari perbarui MotherView:

struct MotherView : View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    
    var body: some View { 
        VStack {
            if viewRouter.currentPage == "page1" {
                ContentViewA()
            } else if viewRouter.currentPage == "page2" {
                ContentViewB()
            }
        }
    }
}

Ini luar biasa: kita tidak perlu lagi menginisialisasi ViewRouterdi dalam milik kita MotherViewdan meneruskannya ke ContentView, yang bisa sangat efisien, terutama untuk hierarki yang lebih kompleks.

Luar biasa, mari kita jalankan aplikasi kita dan melihat cara kerjanya.


Hebat, kita masih bisa bergerak di antara pandangan kita!

Menambahkan animasi transisi


Sebagai bonus, mari kita lihat bagaimana menambahkan animasi transisi ketika beralih dari "page1" ke "page2".

Di SwiftUI, ini cukup sederhana.
Lihatlah willChangemetode yang kami sebut file ViewRouter.swiftketika CurrentPagediperbarui. Seperti yang sudah Anda ketahui, ini menyebabkan pengikatan MotherViewuntuk menampilkan kembali tubuh Anda, akhirnya menunjukkan yang lain ContentView, yang berarti pindah ke yang lain ContentView. Kami dapat menambahkan animasi dengan hanya membungkus metode willChangedalam suatu fungsi withAnimation:

var currentPage: String = "page1" {
        didSet {
            withAnimation() {
                willChange.send(self)
            }
        }
    }

Sekarang kita dapat menambahkan animasi transisi saat menampilkan yang lain Content View.
“WithAnimation (_: _ :) - mengembalikan hasil penghitungan ulang tampilan tubuh dengan animasi yang disediakan”
Apple
Kami ingin memberikan transisi pop-up aplikasi saat menavigasi dari ContentViewAke ContentViewB. Untuk melakukan ini, buka file MotherView.swiftdan tambahkan pengubah transisi saat dipanggil ContentViewB. Anda dapat memilih salah satu dari beberapa jenis transisi yang telah ditentukan atau bahkan membuat sendiri (tetapi ini adalah topik untuk artikel lain). Untuk menambahkan transisi pop-up, kami memilih jenis .scale.

var body: some View {
        VStack {
            if viewRouter.currentPage == "page1" {
                ContentViewA()
            } else if viewRouter.currentPage == "page2" {
                ContentViewB()
                    .transition(.scale)
            }
        }
    }

Untuk melihat cara kerjanya, jalankan aplikasi Anda dalam simulator biasa:


Keren, kami menambahkan animasi transisi yang bagus ke aplikasi kami hanya dalam beberapa baris kode!

Anda dapat mengunduh semua kode sumber di sini !

Kesimpulan


Itu saja! Kami belajar mengapa lebih baik digunakan EnvironmentObjectuntuk menavigasi antara tampilan di SwiftUI, dan bagaimana menerapkannya. Kami juga belajar cara menambahkan animasi transisi ke navigasi. Jika Anda ingin tahu lebih banyak, ikuti kami di Instagram dan berlangganan buletin kami agar tidak ketinggalan pembaruan, tutorial, dan tips tentang SwiftUI dan banyak lagi!



Pelajaran gratis: “Mempercepat aplikasi iOS dengan instrumen”



All Articles