فهم أغلفة الممتلكات في SwiftUI

تم إعداد ترجمة المقال خصيصًا لطلاب الدورة التدريبية "iOS Developer. الدورة المتقدمة v 2.0. "




في الأسبوع الماضي ، بدأنا سلسلة جديدة من المشاركات حول إطار SwiftUI. اليوم أريد أن أستمر في هذا الموضوع من خلال الحديث عن Wrappers Property في SwiftUI. يزودنا @State @Binding @ObservedObject @EnvironmentObject و @Environment و @Environment و @Environment و @Environment . لذلك ، دعونا نحاول أن نفهم الفرق بينهم وبين متى ولماذا وأي نوع يجب أن نستخدمه.

مغلفة الممتلكات


يتم وصف أغلفة الممتلكات (يشار إليها فيما يلي باسم "أغلفة الممتلكات") في SE-0258 . الفكرة الرئيسية هي لف الخصائص مع المنطق ، والتي يمكن استخراجها في هيكل منفصل لإعادة استخدامها في قاعدة الكود.

دولة


@State عبارة عن غلاف يمكن استخدامه للإشارة إلى حالة View . سيقوم SwiftUI بتخزينه في ذاكرة داخلية خاصة خارج هيكل View . فقط View المرتبطة يمكن الوصول إليها. عندما @State قيمة الخاصية @State ، يعيد SwiftUI إنشاء View لحساب التغييرات في الحالة. هنا مثال بسيط.

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

في المثال أعلاه ، لدينا شاشة بسيطة مع زر وقائمة المنتجات. بمجرد النقر فوق الزر ، فإنه يغير قيمة خاصية الحالة ، ويقوم SwiftUI بإعادة إنشاء View .

Binding


يوفر @Binding الوصول المرجعي لنوع القيمة. نحتاج في بعض الأحيان إلى جعل حالة عرضنا متاحة لأطفاله. لكن لا يمكننا أن نأخذ هذه القيمة ونمررها ، لأنها نوع من القيم ، وسيقوم Swift بتمرير نسخة من هذه القيمة. هذا هو المكان الذي يتم فيه التفاف خاصية @Binding .

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

نحن نستخدم @Binding لتحديد خاصية showFavorited داخل FilterView . نستخدم أيضًا الحرف $ الخاص لتمرير رابط الربط ، لأنه بدون $ Swift سيمر بنسخة من القيمة بدلاً من تمرير رابط الارتساء نفسه. يمكن لـ FilterView قراءة وكتابة قيمة الخاصية showFavorited في ProductsView ، لكن لا يمكنه تتبع التغييرات باستخدام هذا الربط. بمجرد أن يغير FilterView قيمة الخاصية showFavorited ، تقوم SwiftUI بإعادة إنشاء ProductsView و FilterView كطفل لها.

ObservedObject


يعمل @ObservedObject بشكل مشابه لـ @State ، لكن الفارق الرئيسي هو أنه يمكننا تقسيمه بين عدة View مستقلة يمكنها الاشتراك ومشاهدة التغييرات في هذا الكائن ، وبمجرد ظهور التغييرات ، تقوم SwiftUI بإعادة إنشاء كل طرق العرض المرتبطة بهذا الكائن . لنلقِ نظرة على مثال.

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

هنا لدينا فئة PodcastPlayer ، التي PodcastPlayer فيها شاشات تطبيقنا فيما بينها. يجب أن تعرض كل شاشة زر إيقاف مؤقت عائم عندما يلعب التطبيق حلقة بودكاست. SwiftUI بتتبع التغييرات على @Published باستخدام برنامج المجمّع @Published ، وبمجرد أن يتم وضع علامة على التغييرات على SwiftUI ، يعيد SwiftUI جميع SwiftUI المرتبطة PodcastPlayer . نحن هنا نستخدم برنامج @ObservedObject لربط EpisodesView 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


بدلاً من اجتياز ObservableObject خلال طريقة init الخاصة برؤيتنا ، يمكننا تضمينها ضمنيا في التسلسل الهرمي لطريقة View Environment بنا. من خلال القيام بذلك ، نجعل من الممكن لجميع المشاهدات الفرعية Environment الحالية الوصول إلى هذا ObservableObject .

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

كما ترون ، يجب أن نمرر PodcastPlayer خلال معدّل environmentObject PodcastPlayer العرض الخاصة بنا. من خلال القيام بذلك ، يمكننا الوصول بسهولة إلى PodcastPlayer عن طريق تعريفه باستخدام برنامج تضمين @EnvironmentObject . يستخدم @EnvironmentObject وظيفة البحث عن الأعضاء الديناميكية للعثور على مثيل لفئة PodcastPlayer في Environment ، لذلك لا تحتاج إلى تمريرها عبر طريقة EpisodesView init. البيئة هي الطريقة الصحيحة لحقن التبعيات في SwiftUI .

Environment


كما قلنا في الفصل السابق ، يمكننا نقل الكائنات المخصصة إلى التسلسل الهرمي " View Environment داخل SwiftUI . لكن SwiftUI لديه بالفعل Environment مليئة بالإعدادات على مستوى النظام. يمكننا الوصول إليهم بسهولة باستخدام غلاف @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) } } 

من خلال وضع علامة على @Environment باستخدام برنامج تضمين @Environment ، نتمكن من الوصول إلى التغييرات على الإعدادات على مستوى النظام والاشتراك فيها. بمجرد تغيير أنظمة الإعدادات المحلية أو التقويم أو ColorScheme ، تقوم SwiftUI بإعادة إنشاء CalendarView بنا.

استنتاج


تحدثنا اليوم عن أغلفة الممتلكات التي تقدمها SwiftUI . @State @Binding و @EnvironmentObject و @EnvironmentObject و @ObservedObject دورًا كبيرًا في تطوير SwiftUI . شكرا لاهتمامكم!

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


All Articles