التنقل بين طرق العرض باستخدامEnvironmentObject في SwiftUI

تم إعداد ترجمة المقال عشية إطلاق الدورة المتقدمة "iOS Developer" .




أهلا ومرحبا بكم في برنامجنا التعليمي! في هذه السلسلة ، نتحدث عن كيفية التنقل بين طرق العرض في SwiftUI (بدون استخدام عرض التنقل!). على الرغم من أن هذه الفكرة قد تبدو تافهة ، ولكن بعد فهمها أعمق قليلاً ، يمكننا معرفة الكثير عن مفاهيم تدفق البيانات المستخدمة في SwiftUI.

في الجزء السابق ، تعلمنا كيفية تنفيذ ذلك باستخدام @ObservableObject. في هذا الجزء ، سننظر في كيفية القيام بنفس الشيء ، ولكن بكفاءة أكبر باستخدامEnvironmentObject. سنضيف أيضًا القليل من الرسوم المتحركة الانتقالية.

إليك ما سنحققه:


ما لدينا


لذا ، توصلنا للتو إلى كيفية التنقل بين طرق العرض المختلفة باستخدام ObservableObject. باختصار ، أنشأنا ViewRouter وربطناه بـ Mother View و Content View. بعد ذلك ، نقوم ببساطة بمعالجة خاصية CurrentPage ViewRouter بالنقر فوق أزرار عرض المحتوى. بعد ذلك ، يتم تحديث MotherView لعرض "عرض المحتوى" المطابق!

ولكن هناك طريقة ثانية أكثر كفاءة لتحقيق هذه الوظيفة: استخدم @EnvironmentObject!

تلميح: يمكنك تنزيل آخر التطورات هنا (هذا هو المجلد "NavigateInSwiftUIComplete") : GitHub

لماذا الاستخدام ObservableObjectليس هو الحل الأفضل


ربما تتساءل: لماذا يجب علينا تنفيذ ذلك بأي طريقة أخرى ، عندما يكون لدينا بالفعل حل عملي؟ حسنًا ، إذا نظرت إلى منطق التسلسل الهرمي لتطبيقنا ، فسوف يتضح لك. MotherView الخاص بنا هو العرض الجذر الذي يقوم بتهيئة مثيل ViewRouter. في MotherView ، نقوم أيضًا بتهيئة ContentViewA و ContentViewB ، ونمررهما مثيل ViewRouter باعتباره BindableObject.

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



لتلخيص: استخدام التنظيف ObservableObjectيمكن أن يكون مشكلة عندما يتعلق الأمر بتسلسل هرمي أكثر تعقيدًا للتطبيق.

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

ما هو EnvironmentObject؟


EnvironmentObjectهو نموذج بيانات يمكنه ، بعد التهيئة ، تبادل البيانات مع جميع تمثيلات تطبيقك. ما هو جيد بشكل خاص هو ما تم EnvironmentObjectإنشاؤه من خلال تقديم ObservableObject، حتى نتمكن من استخدام ما لدينا ViewRouterلخلق EnvironmentObject!

بمجرد إعلاننا ViewRouterعن EnvironmentObjectوجهة نظرنا ، يمكن إرفاق جميع المشاهدات بها وكذلك العروض العادية ObservableObject، ولكن دون الحاجة إلى سلسلة من التهيئة أسفل التسلسل الهرمي للتطبيق!

كما سبق ذكره ، EnvironmentObjectيجب أن تتم تهيئته بالفعل في المرة الأولى التي يتم الوصول إليه. نظرًا لأننا MotherView، كعرض الجذر ، سوف ننظر إلى الخاصية CurrentPage ViewRouter، يجب أن نهيئها EnvironmentObjectعند بدء التطبيق. ثم يمكننا تغيير الصفحة الحالية تلقائيًاEnvironmentObjectمن ContentView، والذي يتطلب بعد ذلك MotherViewإعادة العرض.



التنفيذ على ViewRouterالنحوEnvironmentObject


لذا ، دعنا نقوم بتحديث رمز تطبيقنا!

أولاً ، قم بتغيير غلاف الخاصية من viewRouterالداخل إلى .MotherView@ObservableObject@EnvironmentObject

import SwiftUI

struct MotherView : View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
//...
    }
}

الخاصية viewRouterتبحث الآن ViewRouter-EnvironmentObject. وبالتالي ، نحتاج إلى تزويد هيكلنا بالمثال MotherView_Previewsالمناسب:

#if DEBUG
struct MotherView_Previews : PreviewProvider {
    static var previews: some View {
        MotherView().environmentObject(ViewRouter())
    }
}
#endif

كما ذكرنا أعلاه - عند بدء تطبيقنا ، يجب أن يتم توفيره على الفور بمثيل ViewRouterفي الجودة EnvironmentObject، لأنه MotherViewيشير الآن إلى تمثيل الجذر EnvironmentObject. لذلك ، قم بتحديث وظيفة المشهد داخل الملف على SceneDelegage.swiftالنحو التالي:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: MotherView().environmentObject(ViewRouter()))
            self.window = window
            window.makeKeyAndVisible()
        }
    }

رائع ، الآن عند بدء التطبيق ، يقوم SwiftUI بإنشاء مثيل ViewRouterفي الجودة EnvironmentObject، يمكن الآن إرفاق جميع طرق عرض تطبيقنا به.

بعد ذلك ، دعنا نقوم بتحديث موقعنا ContentViewA. قم بتغيير خصائصه viewRouterإلى EnvironmentObjectوتحديث البنية أيضًا ContentViewA_Previews.

import SwiftUI

struct ContentViewA : View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    
    var body: some View {
       //...
    }
}
#if DEBUG
struct ContentViewA_Previews : PreviewProvider {
    static var previews: some View {
        ContentViewA().environmentObject(ViewRouter())
    }
}
#endif

تلميح: مرة أخرى ، للهيكل ContentViewsA_Previewsمثيله الخاص ViewRouter، ولكنه ContentViewAمرتبط بالمثيل الذي تم إنشاؤه عند تشغيل التطبيق!

دعنا نكرر هذا لـ ContentViewB:

import SwiftUI

struct ContentViewB : View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    
    var body: some View {
        //...
    }
}
#if DEBUG
struct ContentViewB_Previews : PreviewProvider {
    static var previews: some View {
        ContentViewB().environmentObject(ViewRouter())
    }
}
#endif

منذ viewRouterلدينا هي الخصائص ContentViewالمتعلقة الآن مباشرة إلى / مراقبة المثال الأولي ViewRouterكما EnvironmentObjectأننا لم تعد بحاجة إلى تهيئة لهم في بلدنا MotherView. لذا ، دعنا نقوم بتحديث MotherView:

struct MotherView : View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    
    var body: some View { 
        VStack {
            if viewRouter.currentPage == "page1" {
                ContentViewA()
            } else if viewRouter.currentPage == "page2" {
                ContentViewB()
            }
        }
    }
}

هذا أمر رائع: لم نعد بحاجة إلى التهيئة ViewRouterداخل منطقتنا MotherViewوتمرير مثيله إلى ContentView ، والذي يمكن أن يكون فعالًا للغاية ، خاصة بالنسبة للتسلسلات الهرمية الأكثر تعقيدًا.

رائع ، دعنا نشغل تطبيقنا ونرى كيف يعمل.


رائع ، لا يزال بإمكاننا التنقل بين وجهات نظرنا!

مضيفا الرسوم المتحركة الانتقالية


كمكافأة ، دعنا نلقي نظرة على كيفية إضافة رسم متحرك انتقالي عند التبديل من "page1" إلى "page2".

في SwiftUI ، هذا بسيط للغاية.
انظر إلى willChangeالطريقة التي نسميها الملف ViewRouter.swiftعند CurrentPageالتحديث. كما تعلمون بالفعل ، يؤدي هذا إلى إلزام MotherViewبإعادة عرض جسمك ، وفي النهاية إظهار آخر ContentView، مما يعني الانتقال إلى آخر ContentView. يمكننا إضافة رسوم متحركة ببساطة عن طريق لف الطريقة willChangeفي دالة withAnimation:

var currentPage: String = "page1" {
        didSet {
            withAnimation() {
                willChange.send(self)
            }
        }
    }

يمكننا الآن إضافة رسم متحرك انتقالي عند عرض آخر Content View.
"WithAnimation (_: _ :) - تُرجع نتيجة إعادة حساب جسم العرض باستخدام الرسوم المتحركة المتوفرة"
Apple
نريد تزويد تطبيقنا بانتقال منبثق عند الانتقال من ContentViewAإلى ContentViewB. للقيام بذلك ، انتقل إلى الملف MotherView.swiftوأضف معدّل الانتقال عند الاستدعاء ContentViewB. يمكنك اختيار أحد أنواع الانتقال المحددة مسبقًا أو حتى إنشاء الخاصة بك (ولكن هذا موضوع لمقال آخر). لإضافة انتقال منبثق ، نختار نوعًا .scale.

var body: some View {
        VStack {
            if viewRouter.currentPage == "page1" {
                ContentViewA()
            } else if viewRouter.currentPage == "page2" {
                ContentViewB()
                    .transition(.scale)
            }
        }
    }

لمعرفة كيفية عملها ، قم بتشغيل التطبيق الخاص بك في جهاز محاكاة عادي:


رائع ، لقد أضفنا رسومًا متحركة لطيفة إلى تطبيقنا في بضعة أسطر من التعليمات البرمجية!

يمكنك تنزيل جميع التعليمات البرمجية المصدر هنا !

استنتاج


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



درس مجاني: "تسريع تطبيقات iOS بالأدوات"



All Articles