Memahami Pembungkus Properti di SwiftUI

Terjemahan artikel disiapkan khusus untuk siswa kursus “Pengembang iOS. Kursus Lanjutan v 2.0. "




Minggu lalu kami memulai serangkaian posting baru tentang kerangka kerja SwiftUI. Hari ini saya ingin melanjutkan topik ini dengan berbicara tentang Pembungkus Properti di SwiftUI. SwiftUI menyediakan pembungkus untuk @State , @Binding , @ObservedObject , @EnvironmentObject , dan @Environment . Jadi, mari kita coba memahami perbedaan antara mereka dan kapan, mengapa dan mana yang harus kita gunakan.

Pembungkus properti


Pembungkus Properti (selanjutnya disebut "pembungkus properti") dijelaskan dalam SE-0258 . Ide utamanya adalah untuk membungkus properti dengan logika, yang dapat diekstraksi ke dalam struktur terpisah untuk digunakan kembali dalam basis kode.

Negara


@State adalah pembungkus yang bisa kita gunakan untuk menunjukkan status View . SwiftUI akan menyimpannya dalam memori internal khusus di luar struktur View . Hanya View ditautkan yang dapat mengaksesnya. Ketika nilai properti @State berubah, SwiftUI membangun kembali View to account untuk perubahan status. Ini adalah contoh sederhana.

 struct ProductsView: View { let products: [Product] @State private var showFavorited: Bool = false var body: some View { List { Button( action: { self.showFavorited.toggle() }, label: { Text("Change filter") } ) ForEach(products) { product in if !self.showFavorited || product.isFavorited { Text(product.title) } } } } } 

Pada contoh di atas, kami memiliki layar sederhana dengan tombol dan daftar produk. Segera setelah kami mengklik tombol, itu mengubah nilai properti negara, dan SwiftUI membangun kembali View .

@Binding


@Binding menyediakan akses referensi untuk tipe nilai. Terkadang kita perlu membuat keadaan View kita dapat diakses oleh anak-anaknya. Tetapi kita tidak bisa hanya mengambil dan meneruskan nilai ini, karena ini adalah tipe nilai, dan Swift akan meneruskan salinan nilai ini. Di sinilah pembungkus properti @Binding datang untuk menyelamatkan.

 struct FilterView: View { @Binding var showFavorited: Bool var body: some View { Toggle(isOn: $showFavorited) { Text("Change filter") } } } struct ProductsView: View { let products: [Product] @State private var showFavorited: Bool = false var body: some View { List { FilterView(showFavorited: $showFavorited) ForEach(products) { product in if !self.showFavorited || product.isFavorited { Text(product.title) } } } } } 

Kami menggunakan @Binding untuk menandai properti showFavorited di dalam FilterView . Kami juga menggunakan $ karakter khusus untuk melewati tautan jangkar, karena tanpa $ Swift itu akan melewati salinan nilai alih-alih melewati tautan jangkar itu sendiri. FilterView dapat membaca dan menulis nilai properti showFavorited di ProductsView , tetapi tidak dapat melacak perubahan menggunakan pengikatan ini. Segera setelah FilterView mengubah nilai properti showFavorited, SwiftUI membuat ulang ProductsView dan FilterView sebagai anaknya.

@ObservedObject


@ObservedObject bekerja mirip dengan @State , tetapi perbedaan utamanya adalah bahwa kita dapat membaginya di antara beberapa View independen yang dapat berlangganan dan menonton perubahan pada objek ini, dan segera setelah perubahan muncul, SwiftUI membangun kembali semua tampilan yang terkait dengan objek ini. . Mari kita lihat sebuah contoh.

 import Combine final class PodcastPlayer: ObservableObject { @Published private(set) var isPlaying: Bool = false func play() { isPlaying = true } func pause() { isPlaying = false } } 

Di sini kita memiliki kelas PodcastPlayer , di mana layar aplikasi kita berbagi di antara mereka sendiri. Setiap layar harus menampilkan tombol jeda mengambang ketika aplikasi memutar episode podcast. SwiftUI melacak perubahan pada ObservableObject menggunakan pembungkus @Published , dan segera setelah properti ditandai sebagai perubahan SwiftUI , SwiftUI membangun kembali semua SwiftUI terkait dengan PodcastPlayer ini. Di sini kita menggunakan pembungkus @ObservedObject untuk mengikat EpisodesView kita ke kelas PodcastPlayer

 struct EpisodesView: View { @ObservedObject var player: PodcastPlayer let episodes: [Episode] var body: some View { List { Button( action: { if self.player.isPlaying { self.player.pause() } else { self.player.play() } }, label: { Text(player.isPlaying ? "Pause": "Play") } ) ForEach(episodes) { episode in Text(episode.title) } } } } 

@EnvironmentObject


Alih-alih meneruskan ObservableObject melalui metode init dari View kita, kita dapat secara implisit menanamkannya dalam Environment hierarki View kita. Dengan melakukan ini, kami memungkinkan semua pandangan anak-anak dari Environment saat ini untuk mengakses objek yang ObservableObject ini.

 class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let window = UIWindow(frame: UIScreen.main.bounds) let episodes = [ Episode(id: 1, title: "First episode"), Episode(id: 2, title: "Second episode") ] let player = PodcastPlayer() window.rootViewController = UIHostingController( rootView: EpisodesView(episodes: episodes) .environmentObject(player) ) self.window = window window.makeKeyAndVisible() } } struct EpisodesView: View { @EnvironmentObject var player: PodcastPlayer let episodes: [Episode] var body: some View { List { Button( action: { if self.player.isPlaying { self.player.pause() } else { self.player.play() } }, label: { Text(player.isPlaying ? "Pause": "Play") } ) ForEach(episodes) { episode in Text(episode.title) } } } } 

Seperti yang Anda lihat, kami harus melewati PodcastPlayer melalui pengubah environmentObject dari View kami. Dengan melakukan ini, kita dapat dengan mudah mengakses PodcastPlayer dengan mendefinisikannya menggunakan pembungkus @EnvironmentObject . @EnvironmentObject menggunakan fungsi pencarian anggota dinamis untuk menemukan instance kelas PodcastPlayer di Environment , sehingga Anda tidak perlu meneruskannya melalui metode PodcastPlayer EpisodesView . Lingkungan adalah cara yang tepat untuk menyuntikkan dependensi ke dalam SwiftUI .

@ Lingkungan


Seperti yang kami katakan di bab sebelumnya, kami dapat mentransfer objek khusus ke hierarki View Environment di dalam SwiftUI . Tetapi SwiftUI sudah memiliki Environment dipenuhi dengan pengaturan seluruh sistem. Kita dapat dengan mudah mengaksesnya menggunakan pembungkus @Environment .

 struct CalendarView: View { @Environment(\.calendar) var calendar: Calendar @Environment(\.locale) var locale: Locale @Environment(\.colorScheme) var colorScheme: ColorScheme var body: some View { return Text(locale.identifier) } } 

Dengan menandai properti kami dengan pembungkus @Environment , kami mendapatkan akses dan berlangganan perubahan ke pengaturan seluruh sistem. Segera setelah Lokal , Kalender atau sistem ColorScheme berubah, SwiftUI menciptakan CalendarView kami.

Kesimpulan


Hari ini kita berbicara tentang Pembungkus Properti yang disediakan oleh SwiftUI . @State , @State , @Binding , dan @ObservedObject memainkan peran besar dalam pengembangan SwiftUI . Terima kasih atas perhatian anda!

Source: https://habr.com/ru/post/id475078/


All Articles