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