تم إعداد ترجمة المقال خصيصًا لطلاب الدورة التدريبية "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 . شكرا لاهتمامكم!