
تتمثل المهمة الشائعة في تطوير iOS في الأقسام القابلة للتوسيع / القابلة للطي في UITableView. اليوم نحن ندرك هذه المهمة باستخدام SwiftUI. كتحريف صغير ، أضف مثلثًا متحركًا في رأس القسم واجعل الخلايا تتوسع أيضًا.
تم التطوير على Xcode 11.2 لنظام التشغيل macOS Catalina 10.15.1
ابدأ المشروع
إطلاق Xcode ، ملف - مشروع جديد - تطبيق عرض واحد. في مربع الحوار ، حدد لغة تطوير Swift ، وسوف نقوم بتكوين واجهة المستخدم باستخدام SwiftUI.

معطيات
كبيانات توضيحية ، سوف نستخدم بعض التعبيرات المجنحة المضحكة باللغة اللاتينية مع ترجمتها إلى الروسية.
أضف ملف Swift جديدًا إلى المشروع ،
واسمه Data.swift واكتب ما يلي هناك:
struct QuoteDataModel : Identifiable { var id: String { return latin } var latin : String var russian : String var expanded = false } struct SectionDataModel : Identifiable { var id: Character { return letter } var letter : Character var quotes : [QuoteDataModel] var expanded = false }
QuoteDataModel هو نموذج لتعبير واحد ، في المستقبل سوف يصبح محتوى كل خلية على حدة. فيه نقوم بتخزين النص الأصلي للتعبير وترجمته وعلامة الخلية "الموسعة" (افتراضيًا ، يتم "تصغيرها")
SectionDataModel هو نموذج لكل قسم منفصل ، وهنا نقوم بتخزين "حرف" القسم ، ومجموعة من علامات الاقتباس التي تبدأ بهذا الحرف وأيضًا علامة على القسم "الموسع" (بشكل افتراضي "إنهار" أيضًا)
في المستقبل ، سوف نعرض كل هذا في عرض قائمة ، مما يتطلب أن تتوافق البيانات الخاصة به مع بروتوكول
التعريف . للقيام بذلك ، نقوم بتعريف خاصية
المعرف ، والتي يجب أن تكون فريدة لكل عنصر في القائمة.
علاوة على ذلك ، في نفس ملف Data.swift ، نقوم بتكوين بياناتنا:
var latinities : [SectionDataModel] = [ SectionDataModel(letter: "C", quotes: [ QuoteDataModel(latin: "Calvitium non est vitium, sed prudentiae indicium.", russian: " , ."), QuoteDataModel(latin: "Conjecturalem artem esse medicinam.", russian: " ."), QuoteDataModel(latin: "Crede firmiter et pecca fortiter!", russian: " !")]), SectionDataModel(letter: "H", quotes: [ QuoteDataModel(latin: "Homo sine religione sicut equus sine freno.", russian: " ."), QuoteDataModel(latin: "Habet et musca splenem.", russian: " .")]), SectionDataModel(letter: "M", quotes: [ QuoteDataModel(latin: "Malum est mulier, sed necessarium malum.", russian: " , ."), QuoteDataModel(latin: "Mulierem ornat silentium.", russian: " .")])]
دعونا نتعامل مع الواجهة
الآن سوف نحدد كيف سيكون عنوان القسم وكل خلية ستبدو.
اختر ملف - جديد - ملف - عرض SwiftUI من القائمة. اسم الملف
HeaderView.swift واستبدال محتوياته بما يلي:
import SwiftUI struct HeaderView : View { var section : SectionDataModel var body: some View { HStack() { Spacer() Text(String(section.letter)) .font(.largeTitle) .foregroundColor(Color.black) Spacer() } .background(Color.yellow) } } struct HeaderView_Previews: PreviewProvider { static var previews: some View { HeaderView(section: latinities[0]) } }

الآن مرة أخرى ملف - جديد - ملف - عرض SwiftUI. اسم الملف
QuoteView.swift واستبدال محتوياته بما يلي:
import SwiftUI struct QuoteView: View { var quote : QuoteDataModel var body: some View { VStack(alignment: .leading, spacing: 5) { Text(quote.latin) .font(.title) if quote.expanded { Group() { Divider() Text(quote.russian).font(.body) }.transition(.move(edge: .top)).animation(.default) } } } } struct QuoteView_Previews: PreviewProvider { static var previews: some View { QuoteView(quote: latinities[0].quotes[0]) } }

الآن افتح الملف ContentView.swift وقم بتغيير بنية ContentView كما يلي:
struct ContentView: View { var body: some View { List { ForEach(latinities) { section in Section(header: HeaderView(section: section), footer: EmptyView()) { if section.expanded { ForEach(section.quotes) { quote in QuoteView(quote: quote) } } } } } .listStyle(GroupedListStyle()) } }
تهانينا ، لقد ملأت القائمة للتو ببيانات حديثة! لكل عنصر من عناصر مجموعة
العلامات ، نقوم بإنشاء قسم برأس يستند إلى
HeaderView وتذييل فارغ. إذا تم "توسيع" القسم ، فكل تعبير في مجموعة علامات الاقتباس ، نقوم بتشكيل خلية بناءً على عرض
الأسعار . في بياناتنا ، يتم "طي" جميع الأقسام وجميع الخلايا ، لذلك إذا قمت بإظهار Canvas ، سترى فقط رؤوس الأقسام:

كما فهمت ، فإن التطبيق الآن "ميت" تمامًا ولا يزال بعيدًا عن هدفنا النهائي. ولكن سرعان ما سوف نصلحها!
تعديل رأس القسم قليلا
العودة إلى ملف HeaderView.swift. داخل هيكل HeaderView ، مباشرة بعد النص ، أضف هذا:
struct Triangle : Shape { func path(in rect: CGRect) -> Path { var path = Path() path.move(to: CGPoint(x: 0, y: 0)) path.addLine(to: CGPoint(x: 0, y: rect.height - 1)) path.addLine(to: CGPoint(x: sqrt(3)*(rect.height)/2, y: rect.height/2)) path.closeSubpath() return path } }
ترجع هذه البنية مثلث متساوي الأضلاع. أضف الآن المثلث الخاص بنا إلى الرأس. داخل
HStack ، قبل إضافة
الفاصل الأول هذا:
Triangle() .fill(Color.black) .overlay( Triangle() .stroke(Color.red, lineWidth: 5) ) .frame(width : 50, height : 50) .padding() .rotationEffect(.degrees(section.expanded ? 90 : 0), anchor: .init(x: 0.5, y: 0.5)).animation(.default))

تعديل البيانات
العودة إلى البيانات الخاصة بنا. افتح Data.swift و
Wrap صفيفنا اللاتينية في فئة UserData جديدة ، مثل هذا:
class UserData : ObservableObject { @Published var latinities : [SectionDataModel] = [ SectionDataModel(letter: "C", quotes: [ QuoteDataModel(latin: "Calvitium non est vitium, sed prudentiae indicium.", russian: " , ."), QuoteDataModel(latin: "Conjecturalem artem esse medicinam.", russian: " ."), QuoteDataModel(latin: "Crede firmiter et pecca fortiter!", russian: " !")]), SectionDataModel(letter: "H", quotes: [ QuoteDataModel(latin: "Homo sine religione sicut equus sine freno.", russian: " ."), QuoteDataModel(latin: "Habet et musca splenem.", russian: " .")]), SectionDataModel(letter: "M", quotes: [ QuoteDataModel(latin: "Malum est mulier, sed necessarium malum.", russian: " , ."), QuoteDataModel(latin: "Mulierem ornat silentium.", russian: " .")])] }
تذكر أيضًا تحديد علامات اللاتين مثل
Published .
ماذا فعلنا؟ObservableObject هو كائن خاص لبياناتنا التي يمكن "ربطها" ببعض طريقة العرض. يقوم SwiftUI "بمراقبة" جميع التغييرات التي قد تؤثر على طريقة العرض ، وبعد تغيير البيانات ، يقوم بتغيير طريقة العرض.
بعد "التفاف" اللاتينيات ، كان لدينا الكثير من الأخطاء ، وسوف نقوم بتصحيحها. افتح
HeaderView.swift وقم بتصحيح بنية
HeaderView_Previews كما يلي:
struct HeaderView_Previews: PreviewProvider { static var previews: some View { HeaderView(section: UserData().latinities[0]) } }
الآن قم بإجراء تغييرات مماثلة على
QuoteView.swift :
struct QuoteView_Previews: PreviewProvider { static var previews: some View { QuoteView(quote: UserData().latinities[0].quotes[0]) } }
افتح الملف ContentView.swift وأضف هذا قبل إعلان النص
@ObservedObject var userData = UserData()
إحياء المشهد
العودة إلى الملف ContentView.swift. داخل بنية ContenView ، مباشرة بعد تعريف userData ، أضف وظيفتين:
func sectionIndex(section : SectionDataModel) -> Int { userData.latinities.firstIndex(where: {$0.letter == section.letter})! } func quoteIndex(section : Int, quote : QuoteDataModel) -> Int { return userData.latinities[section].quotes.firstIndex(where: {$0.latin == quote.latin})! }
إضافة المعدلات
onTapGesture إلى رأس القسم والخلية التي نقوم
بإنشائها . العرض النهائي لمحتوى
الجسم :
var body: some View { List { ForEach(userData.latinities) { section in Section(header: HeaderView(section: section) .onTapGesture { self.userData.latinities[self.sectionIndex(section: section)].expanded.toggle() }, footer: EmptyView()) { if section.expanded { ForEach(section.quotes) { quote in QuoteView(quote: quote) .onTapGesture { let sectionIndex = self.sectionIndex(section: section) let quoteIndex = self.quoteIndex(section: sectionIndex, quote: quote) self.userData.latinities[sectionIndex].quotes[quoteIndex].expanded.toggle() } } } } } } .listStyle(GroupedListStyle()) }
تُرجع
الدالتان إينديكس و
quoteUndex فهرس الأقسام والعبارات التي تم تمريرها إليهما. بعد تلقي هذه الفهارس ، نقوم بتغيير قيم الخصائص
الموسعة في
صفيفنا من
اللاتينيات ، مما يؤدي إلى طي /
كشف القسم أو التعبير.

استنتاج
المشروع النهائي يمكن
تحميله من هنا .
بعض الروابط المفيدة:
آمل أن يكون المنشور مفيدًا لك!