
هل حاولت إضافة أكثر من 10 طرق عرض إلى VStack؟
var body: some View { VStack { Text("Placeholder1") Text("Placeholder2")
حاولت - أنها لا تجمع. نعم ، لقد فوجئت في البداية ودخلت في دراسة منتدى سويفت وجيثب. كانت نتيجة دراستي - "لا تزال غير مجمّعة ¯\_(ツ)_/¯
". لكن مهلا ، دعنا نرى لماذا.
بناء وظيفة
للبدء ، يجدر فهم كيف أصبح بناء الجملة متاحًا بشكل عام. أساس إنشاء التعريفي لعناصر غير عادية بالنسبة لنا هو آلية منشئ الوظيفة .
على جيثب في التطور السريع ، هناك اقتراح من جون مكول ودوغ غريغور - مُنشئو الوظائف (الاقتراح: SE-XXXX) ، يصفون فيه بالتفصيل المشكلة التي يواجهونها ، ولماذا تقرر استخدام منشئ الوظائف وماذا كان أ.
إذن ما هذا؟
من الصعب وصف ذلك باختصار ، ولكن باختصار - هذه هي الآلية التي تتيح لك سرد الحجج ، وبعض المحتوى في نص الكنز ، وإعطاء نتيجة عامة من كل هذا.
اقتباس من الاقتراح: SE-XXXX :
الفكرة الأساسية هي أننا نأخذ نتيجة التعبير ، بما في ذلك التعبيرات المتداخلة مثل if والتبديل ، ونشكلها في نتيجة واحدة ، والتي تصبح القيمة المرجعة للدالة الحالية. يتم التحكم في "البنية" هذه بواسطة أداة إنشاء الوظائف ، وهي سمة مخصصة.
الأصليالفكرة الأساسية هي أننا نأخذ نتائج التعبير "المتجاهلة" لمجموعة من العبارات - بما في ذلك في المواضع المتداخلة مثل نصوص بيانات التبديل - وننشئها في قيمة نتيجة واحدة تصبح قيمة الإرجاع للدالة الحالية. يتم التحكم في طريقة تنفيذ هذه المجموعة بواسطة باني afunction ، وهو نوع جديد من أنواع السمة المخصصة فقط ؛
تسمح لك هذه الآلية بكتابة شفرة تشبه الشجرة ، بدون أحرف ترقيم غير ضرورية:
let myBody = body { let chapter = spellOutChapter ? "Chapter" : "" div { if useChapterTitles { h1(chapter + "1. Loomings.") } p { "Call me Ishmael. Some years ago" } p { "There is now your insular city" } } }
هذا متاح بفضل @_functionBuilder
الجديدة. هذه السمة علامات بعض البناء ، يمكن أن يكون هيكل. يقوم هذا المنشئ بتنفيذ عدد من الأساليب المحددة. علاوة على ذلك ، يتم استخدام هذا المنشئ في حد ذاته ، كسمة مستخدم في مواقف مختلفة.
أدناه سوف أعرض كيف يعمل وكيفية تنظيم هذا الرمز.
لماذا هذا؟
لذا تريد Apple تقديم الدعم لـ DSL بلغة المجال المحددة .
يشير John McCall و Doug Gregor إلى أن مثل هذا الرمز أسهل في القراءة والكتابة - وهذا يبسط بناء الجملة ، ويجعله أكثر إيجازًا ، ونتيجة لذلك ، يصبح الرمز أكثر دعمًا. في الوقت نفسه ، لاحظوا أن حلهم ليس DSL عالميًا.
يركز هذا الحل على مجموعة محددة من المشكلات ، بما في ذلك وصف الهياكل الخطية والأشجار مثل XML ، JSON ، عرض التسلسلات الهرمية ، وما إلى ذلك.
كيف تعمل معها؟
يمكنك إنشاء أداة إنشاء الوظائف الخاصة بك ، فقد كان من الأسهل بالنسبة لي أن أفهم كيف تعمل. النظر في مثال بدائي لباني أن يسلسل السلاسل.
تحت الغطاء ، هذا سوف ينجح مثل هذا:
let result = stringsReduce { return MyBuilder.build("1", "2") }
من المهم أن تكون الأساليب ثابتة تمامًا عند تنفيذ المنشئ ، مع أسماء محددة ونوع محدد من المعلمات من هذه القائمة . يمكنك فقط تغيير نوع واسم معلمة الإدخال.
static func buildBlock(_ <*atrs*>: <*String*>...) -> <*String*>
سيتم البحث عن أسماء طرق محددة في المنشئ واستبدالها في مرحلة التجميع. وإذا لم يتم العثور على الطريقة ، فسيحدث خطأ في الترجمة.
وهذا هو السحر. عندما تقوم بتنفيذ المنشئ ، فلن يخبرك المترجم بأي شيء على الإطلاق. لن يقول عن الطرق المتاحة ، ولن يساعد في الإكمال التلقائي. فقط عندما تكتب رمز العميل الذي لا يمكن معالجته بواسطة هذا المنشئ ، فإنك تحصل على خطأ غير واضح.
حتى الآن ، فإن الحل الوحيد الذي وجدته هو الاسترشاد بقائمة الطرق .
إذن لماذا نحتاج إلى طرق أخرى؟ حسنا ، على سبيل المثال ، لدعم هذا الرمز مع الشيكات
stringsReduce { if .random() {
لدعم بناء الجملة هذا ، يحتاج المنشئ إلى تطبيق buildEither(first:/second:)
static func buildEither(first: String) -> String { return first } static func buildEither(second: String) -> String { return second }
استجابة المجتمع
والشيء المضحك هو أن هذا لم يتم بعد في Swift 5.1 ، أي أن طلب السحب مع هذه الميزة لم يتم سكبه بعد ، ولكن مع ذلك ، أضافته Apple بالفعل إلى الإصدار التجريبي Xcode 11. وفي Function builders → Pitches → Swift Forums ، يمكنك رؤية رد فعل المجتمع على هذا الاقتراح.
ViewBuilder
عُد الآن إلى VStack
وانظر وثائق التهيئة الخاصة ببرنامج التهيئة الأولي (المحاذاة: التباعد: content :) .
يبدو مثل هذا:
init(alignment: HorizontalAlignment = .center, spacing: ? = nil, @ViewBuilder content: () -> Content)
وقبل جدول المحتويات ، هناك سمة مخصصة ViewBuilder
أعلن على النحو التالي:
@_functionBuilder public struct ViewBuilder {
سمة المستخدم هي @ _functionBuilder
، والتي يتم تسجيلها في بداية الإعلان الخاص بها.
وإذا نظرت إلى الوثائق أقل ، فيمكنك رؤية العديد من أساليب buildBlock
الثابتة التي تختلف في عدد الوسائط.
وهذا يعني أن رمز العرض
var body: some View { VStack { Text("Placeholder1") Text("Placeholder2") Text("Placeholder3") } }
تحت غطاء محرك السيارة يتم تحويلها إلى مثل هذا
var body: some View { VStack(alignment: .leading) { ViewBuilder.buildBlock(Text("Placeholder1"), Text("Placeholder2"), Text("Placeholder3")) } }
أي يلبي buildBlock (:: _ :) طريقة البناء.
من القائمة بأكملها ، تكون الطريقة ذات الحد الأقصى لعدد الوسائط هي guy buildBlock (:::::::::: :) :) (10 وسائط):
extension ViewBuilder { public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View, C5 : View, C6 : View, C7 : View, C8 : View, C9 : View }
وبناءً على ذلك ، عند العودة إلى المثال الأصلي ، عندما تحاول رفع VStack
وإحدى عشرة طريقة عرض داخلية ، يحاول المترجم العثور على طريقة ViewBuilder'a buildBlock
، التي تحتوي على 11 وسيطة إدخال. ولكن لا توجد مثل هذه الطريقة: ومن هنا جاء خطأ التحويل البرمجي.
هذا صحيح بالنسبة لجميع المجموعات التي تستخدم التهيئة مع السمةViewBuilder في أداة التهيئة: V | H | Z-Stack ، و List ، و Group ، وغيرها ، والتي يمكنك من خلالها إعلان أكثر من عرض كعد.
وهذا محزن.
MEM (آسف ، لم أجد ميمي لائق)
كيف تكون
يمكننا التحايل على هذا القيد باستخدام ForEach
struct TestView : View { var body: some View { VStack { ForEach(texts) { i in Text(«\(i)») } } } var texts: [Int] { var result: [Int] = [] for i in 0...150 { result.append(i) } return result } }
أو مجموعات التداخل:
var body: some View { VStack { VStack { Text("Placeholder_1") Text("Placeholder_2")
لكن مثل هذه القرارات تبدو وكأنها عكازات وليس هناك سوى أمل لمستقبل أكثر إشراقا. ولكن ما هو هذا المستقبل؟
سويفت بالفعل المعلمات المتغير . هذه هي قدرة طريقة على قبول الوسائط بالتعداد. على سبيل المثال ، تسمح لك طريقة print
المعروفة للجميع بكتابة كل من print(1, 2)
print(1, 2, 3, 4)
وهذا بدون حمولات زائدة غير ضرورية من الطريقة.
print(items: Any...)
لكن هذه الميزة من اللغة ليست كافية ، لأن أسلوب buildBlock
يقبل الوسائط العامة المختلفة كمدخلات.
مضيفا المتغيرات الوراثية من شأنه أن يحل هذه المشكلة. تتيح لك الوراثة المتغيرة الاختزال من العديد من الأنواع العامة ، على سبيل المثال ، شيء من هذا القبيل:
static func buildBlock<…Component>(Component...) -> TupleView<(Component...)> where Component: View
وأبل ملزمة ببساطة لإضافة هذا. كل شيء يقع ضد هذه الآلية الآن. ويبدو لي أنهم ببساطة لم يتمكنوا من الانتهاء منه بحلول WWDC 2019 (ولكن هذه مجرد تكهنات).