نحتاج أحيانًا إلى إنشاء عرض SwiftUI ، في ضوء بعض الشروط. على سبيل المثال، في رمز أعلاه، نحدد HomeView ، والتي قد تحتوي على ProfileView ، إذا LogInManager ديه loggedInUser . نحن نحاول تنفيذ ذلك باستخدام معيار if:struct HomeView: View {
@ObservedObject var loginManager: LoginManager
var body: some View {
VStack {
if let user = loginManager.loggedInUser {
ProfileView(user: user)
}
...
}
}
}
للأسف ، سيؤدي هذا الرمز إلى حدوث خطأ عند الترجمة:لا يمكن استخدام الإغلاق الذي يحتوي على بيان تدفق التحكم مع منشئ الوظائف ViewBuilder.
نظرًا لأن أدوات إغلاق الدوال غير مستخدمة هنا ، ولكن أدوات إنشاء الدوال ، لا يمكننا وضع كود عشوائي فيها لتشكيل HStack أو VStack. فكيف نخرج من هذا الوضع؟إحدى الطرق هي تمرير معالجة هذه الاختيارات مباشرة إلى العرض الذي نقوم بإنشائه. على سبيل المثال ، يمكننا تمرير ProfileView ليس قيمة مستخدم محددة ، ولكن نجعلها اختيارية:struct ProfileView: View {
var user: User?
var body: some View {
guard let user = user else {
return AnyView(EmptyView())
}
return AnyView(VStack {
Text(user.name)
...
})
}
}
يعمل هذا الرمز ، ولكنه ليس جميلًا بشكل خاص. لا معنى لإنشاء ProfileView لا شيء للمستخدم. لنأخذ نهجًا مختلفًا: استخدم الخريطة لمستخدمنا الاختياري لتحويلها إلى ProfileView:struct HomeView: View {
@ObservedObject var loginManager: LoginManager
var body: some View {
VStack {
loginManager.loggedInUser.map { user in
ProfileView(user: user)
}
...
}
}
}
هذا بالفعل أجمل بكثير: لسنا بحاجة إلى إعطاء EmptyView يدويًا عندما لا يكون لدى المستخدم قيمة. يمكننا مرة أخرى تمرير قيمة معينة إلى ProfileView ، وليس اختياريًا. هل من الممكن القيام بعمل أفضل؟الخبر السار حول ViewBuilder هو أن هذا ليس نوعًا من التنفيذ الخاص في SwiftUI ، ولكنه سمة يمكن الوصول إليها يمكننا من خلالها إضافة تعليقات على وظائفنا وعمليات الإغلاق الخاصة بنا.باستخدام هذه السمة ، يمكننا تجميع عرض Unwrap ، والذي يأخذ قيمة اختيارية كمعلمة وإغلاقًا بعلامة ViewBuilder لتحويل قيمة غير صفرية إلى عرض:struct Unwrap<Value, Content: View>: View {
private let value: Value?
private let contentProvider: (Value) -> Content
init(_ value: Value?,
@ViewBuilder content: @escaping (Value) -> Content) {
self.value = value
self.contentProvider = content
}
var body: some View {
value.map(contentProvider)
}
}
باستخدام هذا التصميم ، يمكننا الآن إعادة تصميم هيكل HomeView بالكامل:struct HomeView: View {
@ObservedObject var loginManager: LoginManager
var body: some View {
VStack {
Unwrap(loginManager.loggedInUser) { user in
HStack {
Text("Logged in as:")
ProfileView(user: user)
}
}
...
}
}
}