تكييف حل عملك الحالي مع SwiftUI. الجزء الأول

يوم جيد للجميع. اسمي آنا زاركوفا ، أنا مطور رائد للهواتف المحمولة في تقنية Usetech

حول تقنية SwiftUI ، التي أعلنت عنها Apple العام الماضي WWDC 2019 ، قيل الكثير. من حيث المبدأ ، هناك بالفعل ما يكفي من المواد حول ميزاته ، وما تحت غطاء محرك السيارة وكيفية استخدامه لواجهة مستخدم سريعة وجميلة.



أقدم لكم سلسلة قصيرة من المقالات حول كيفية استخدام هذا الإطار في الحياة الحقيقية والتطبيقات الحقيقية ، وليس في منشئي شطيرة الأفوكادو. ولجعل كل شيء جادًا ونضجًا ، سننظر في كيفية جعل تطبيق SwiftUI يتوافق مع مبادئ العمارة النظيفة والرمز النظيف.

لكن لنبدأ بالأساسيات. سيكون هذا الجزء مجرد مقدمة ومعلومات أساسية. لذلك إذا كنت بالفعل على دراية بهذه الميزات SwiftUI ، فانتقل إلى الأجزاء التالية:
الجزء 2 (كيفية العمل مع المكتبات الجاهزة لـ UIKit)
الجزء 3 (هنا حول الهندسة المعمارية)
الجزء 4 (هنا حول التنقل)

إذا لم تكن قد تمكنت من الالتقاء بعد ، ثم توقف مؤقتًا للحصول على بضع دقائق)

تتمثل السمات الرئيسية للتطوير التعريفي في الإطار الجديد في الابتعاد عن الاستخدام المباشر لـ UIViewController و UIView والاستبدال بالهياكل التي تنفذ بروتوكول العرض. يتم أيضًا وصف جميع مكونات الجزء المرئي باستخدام بناء الجملة التعريفية وتقع داخل خاصية الجسم الرئيسية لكل عرض. يتم أيضًا ضبط الإعدادات ، وتخصيص المكونات وتخصيصها ، والتنقل بين العرض على الشاشة باستخدام بناء الجملة التعريفي.

على سبيل المثال ، يصف هذا الرمز طريقة عرض لقائمة أخبار ، عن طريق النقر فوق أي شاشة تفتح بطريقة عرض أخبار منفصلة:

struct NewsListView: View{
    @State var data: [NewsItemMock]
    
    var body: some View {
        NavigationView{
        List(data) { item in
            NavigationLink(destination:NewsItemView(item:item)) {
            NewsItemRow(data: item)
            }
        }
    }
}

يستخدم SwiftUI ViewBuilder ، وهو مصمم واجهة تعريفي يعتمد على Functional Builder. ظهرت هذه الآلية في Swift 5.1 وتسمح لك بتجميع العناصر في صفيف معين داخل كتلة الإغلاق ، على سبيل المثال ، كائن أصل. يتم عرض مثال على استخدام ViewBuilder على الشريحة. نحن ببساطة نضع عناصر التحكم في العرض بالترتيب الذي نحتاجه ، على سبيل المثال ، داخل مكدس رأسي أو أفقي ، دون استخدام addSubview ، وعند تجميع SwiftUI نفسه يضيف ويجمع العناصر في حاوية رئيسية أكثر تعقيدًا.

وإليك الرمز:


          VStack {
            HStack {
                VStack(alignment: .leading,spacing: 10) {
                    HeaderText(text: data.title ?? "")
                    SubheaderText(text: data.description ?? "")
                    SmallText(text: data.publishedAt?
                                   .formatToString("dd.MM.yyyy") ?? "")
                }
               ThumbImage(withURL: data.urlToImage ?? "")
             }

تحويل إلى عنصر قائمة من 3 حقول نص وصورة واحدة:


على الرغم من أن SwiftUI يرفض مفهوم UIViewController ، فإن نقطة الدخول إلى التطبيق هي UIHostingController ، حيث يتم تمرير العرض المعروض وإدماجه. هذا ، في الواقع ، التكنولوجيا الجديدة هي إضافة على UIKit:


@available(iOS 13.0, tvOS 13.0, *)
open class UIHostingController<Content> : UIViewController where Content : View {

    public var rootView: Content

    public init(rootView: Content)


بالمناسبة ، جميع عناصر تحكم SwiftUI هي نظائرها التعريفية لعناصر تحكم UIKit.

على سبيل المثال ، VStack و HStack هي نظائرها من UIStackView الرأسي والأفقي المعتاد ، على التوالي. القائمة هي UITableView ، النص هو UILabel ، الزر هو UIButton ، الصورة هي UIImage ، إلخ.

يتم توصيل وتكوين عناصر التحكم بشكل معلن باستخدام المعدلات المتاحة. يتم تجميع العناصر داخل نظائر UIStackView مع بعض الخصائص المحددة مسبقًا.

بالإضافة إلى تغيير الطريقة التي يتم بها وصف الجزء المرئي ، فإن التحكم في تدفق البيانات وآلية تفاعل واجهة المستخدم إليه يتغيران. يعد Swift UI إطار عمل لا يعتمد على الأحداث. أولئك. العرض فيه هو نتيجة دالة لحالات معينة ، وليس سلسلة من الأحداث. الإجراء الذي يقوم به المستخدم لا يغير واجهة المستخدم مباشرة ، لا يمكنك تغيير هذا أو ذاك عرض أو إضافة أو إزالة عناصر التحكم مباشرة. أولا، خصائص أو متغيرات الحالة التي ترتبط إلى عرض من خلال واحدة أو أخرى مغلفة الملكية (مغلفة الملكية) التغيير .



أغلفة الممتلكات الرئيسية المستخدمة هي:

1.حالة - تستخدم للمتغيرات المحلية.


struct NewsItemRow: View {
    @State var title: String
    @State var  description: String
    @State var dateFormatted: String 
    @State var imageUrl: String 
    
    var body: some View {
        VStack {
            HStack {
                VStack(alignment: .leading,spacing: 10) {
                    HeaderText(text: title)
                    SubheaderText(text: description)
                    SmallText(text: dateFormatted)
                }
              ThumbImage(withURL: imageUrl)
            }
         }
     }

2.Binding - تناظرية للضعف ، تستخدم عند تمرير إشارة إلى قيمة.

نستخدمها عندما يعتمد أكثر من عرض على أي خاصية. على سبيل المثال ، إذا أردنا تمرير القيمة إلى العرض الأصلي من عرض المستوى الثاني.


struct FirstView: View {
    @State var isPresented: Bool = true
    
    var body: some View {
        NavigationView {
            NavigationLink(destination:
            SecondView(isPresented: self.$isPresented)) {
                Text("Some")
            }
        }
    }
}

struct SecondView: View {
    @Binding var isPresented: Bool
    
    var body: some View {
        Button("Dismiss") {
            self.$isPresented = false
        }
    }
}

3.EnvironmentObject - نقل الكائنات بين طريقة العرض

4.ObjectBinding وObservableObject - تستخدم لتتبع التغييرات في خصائص النموذج باستخدام أدوات دمج الإطار.


class NewsItemModel: ObservableObject,IModel {
   @Published var title: String
   @Published var  description: String
   @Published var dateFormatted: String 
   @Published var imageUrl: String 
}

سنتحدث عنه في وقت لاحق.

وبالتالي. إذا أردنا تغيير طريقة العرض الخاصة بنا ، فإننا نغير العقار ، وهو إعلان به أحد أغلفة الملكية. ثم يتم إعادة بناء العرض التعريفي مع جميع الضوابط الداخلية.
عندما يتغير أي من متغيرات الحالة ، يتم إعادة إنشاء العرض ككل.

فكر في مثال صغير. لدينا نوع من الشاشة ، في شريط التنقل يوجد به زر لإضافة محتوى إلى المفضلة. حتى نتمكن من تغيير صورة المؤشر على هذا الزر ، سنستخدم PropertyWrappers. على سبيل المثال ، في هذه الحالة ، قم بإنشاء متغير محلي وقم بتعريفه على أنهحالة:


struct NewsItemView: View {
@State var isFavorite: Bool 
....

سنقوم بتعيين قيمة الخاصية لحدث الزناد الذي يحدث عند الضغط على الزر:

struct NewsItemView: View{
    @State var isFavorite: Bool 
   
    var body: some View {
    NavigationView {
    VStack {
        Text("Some content")
       }
    }
     .navigationBarItems(trailing: Button(action: {
            self.isFavorite = !self.isFavorite
        }){
            Image(self.isFavorite ? "favorite" : "unfavorite")
           .frame(width: 20, height: 20, alignment: .topTrailing)
        })
   }
        

وبالتالي ، ستتغير وجهة نظرنا:



ومن حيث المبدأ ، هذه هي جميع الأشياء الأساسية التي تحتاج إلى معرفتها حول SwiftUI للبدء.

ولكن هل هذا يكفي للعمل؟

لإنشاء واجهات مستخدم غير معقدة من عناصر التحكم البسيطة دون الارتباط بـ Xib واللوحات المصورة بالكامل.

ولشيء أكثر ، لا.

أولاً ، لا تحتوي جميع عناصر التحكم على نظائرها في SwiftUI. ينطبق هذا على كلٍ من UIKit UISearchView القياسي و UICollectionView والعناصر من مكتبات الجزء الثالث.

ثانيًا ، لا توجد (أو لا توجد تقريبًا ، ربما يقوم شخص ما بذلك الآن) حلول من جهة خارجية للعمل مع Data Flow SwiftUI.

لذا ، عليك تكييف الحلول الحالية لتطبيقات iOS القياسية.

الرجاء الضغط على الرابط.

All Articles