نشكل عرض في SwiftUI ، انطلاقا من الشروط

نحتاج أحيانًا إلى إنشاء عرض 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 {
            // We have to use 'AnyView' to perform type erasure here,
            // in order to give our 'body' a single return type:
            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)
                }
            }
            ...
        }
    }
} 

All Articles