يحظى التصميم الذري وتصميم النظام بشعبية في التصميم: هذا عندما يتكون كل شيء من مكونات ، من أدوات التحكم إلى الشاشات. ليس من الصعب على المبرمج كتابة عناصر تحكم منفصلة ، ولكن ماذا تفعل مع الشاشات بأكملها؟
دعونا نلقي نظرة على مثال العام الجديد:
- دعنا نلتصق بكل شيء
- مقسمة إلى وحدات تحكم: حدد التنقل ، القالب والمحتوى ؛
- إعادة استخدام الرمز للشاشات الأخرى.

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

لكن. في المرة القادمة ، عندما نحتاج إلى شاشة مماثلة ، سيتعين علينا تكرار كل شيء مرة أخرى ، ثم إجراء نفس التغييرات على جميع الشاشات. حسنًا ، هذا لا يحدث بدون تعديلات.
لذلك ، من المعقول تقسيمه إلى أجزاء واستخدامه للشاشات الأخرى. سلطت الضوء على ثلاثة:
- الملاحة
- قالب به مساحة للمحتوى ومكان لإجراءات أسفل الشاشة ،
- محتوى فريد من نوعه في المركز.
حدد كل جزء في UIViewController
الخاصة به.
حاوية الملاحة
الأمثلة الأكثر لفتا للحاويات الملاحة هي UINavigationController
و UITabBarController
. يشغل كل واحد شريطًا على الشاشة تحت أدوات التحكم الخاصة به ، ويترك المساحة المتبقية لـ UIViewController
آخر.
في حالتنا ، سيكون هناك حاوية لجميع شاشات الوسائط مع زر إغلاق واحد فقط.
ما هي النقطة؟إذا كنا نريد نقل الزر إلى اليمين ، فسنحتاج فقط إلى تغييره في وحدة تحكم واحدة.
أو ، إذا قررنا عرض جميع الإطارات المشروطة باستخدام رسوم متحركة خاصة ، وإغلاقها بشكل تفاعلي بضغطة واحدة ، كما في بطاقات المجموعة النصية AppStore. ثم سوف تحتاج إلى تعيين UIViewControllerTransitioningDelegate
فقط لوحدة التحكم هذه.

يمكنك استخدام container view
لفصل وحدات التحكم: ستقوم بإنشاء UIView
في الأصل وإدراج UIView
التحكم التابعة فيه.

قم بتمديد container view
إلى حافة الشاشة. سيتم تطبيق Safe area
تلقائيًا على وحدة التحكم التابعة:

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

الأول هو المسؤول عن تخطيط الشاشة: يجب أن يكون المحتوى مركَّزًا ، ويكون الزر مسمرًا أسفل الشاشة. والثاني رسم المحتوى.

بدون قالب ، جميع وحدات التحكم متشابهة ، ولكن العناصر الرقص.
تختلف الأزرار الموجودة على الشاشة الأخيرة - تعتمد على المحتوى. سوف يساعد التفويض في حل المشكلة: سيطلب قالب التحكم عناصر التحكم من المحتوى ويعرضها في UIStackView
.
أزرار يمكن أن تعلق على وحدة تحكم من خلال الكائنات ذات الصلة. يتم تخزين IBAction
و IBAction
في وحدة تحكم المحتوى ، فقط العناصر لا تتم إضافتها إلى التسلسل الهرمي.

يمكنك الحصول على عناصر من المحتوى وإضافتها إلى القالب في مرحلة الإعداد لـ UIStoryboardSegue
:
في المجموعة ، نضيف عناصر تحكم إلى UIStackView
:
ونتيجة لذلك ، تم تقسيم وحدة التحكم الخاصة بنا إلى ثلاثة أجزاء: التنقل والقالب والمحتوى. في الصورة ، يظهر كل container view
باللون الرمادي:

حجم تحكم ديناميكي
وحدة التحكم في المحتوى لها الحد الأقصى لحجمها ، فهي محدودة بسبب constraints
الداخلية.
يضيف Container view
ثوابت بناءً على Autoresizing mask
، Autoresizing mask
مع الأبعاد الداخلية للمحتوى. يتم حل المشكلة في التعليمات البرمجية: في وحدة تحكم المحتوى ، تحتاج إلى الإشارة إلى أنه لا يتأثر بالثوابت من Autoresizing mask
:

هناك خطوتان أخريان لـ Interface Builder:
الخطوة 1. حدد Intrinsic size
لـ UIView
. ستظهر القيم الحقيقية بعد الإطلاق ، لكن في الوقت الحالي سنضع أي قيم مناسبة.

الخطوة 2. بالنسبة لوحدة التحكم في المحتوى ، حدد Simulated Size
. قد لا تتطابق مع حجم الماضي.
حدثت أخطاء في التخطيط ، فماذا أفعل؟تحدث الأخطاء عندما يتعذر على AutoLayout
معرفة كيفية تحليل العناصر بالحجم الحالي.
في أغلب الأحيان ، تزول المشكلة بعد تغيير أولويات الثابت. تحتاج إلى وضعها حتى يتسنى لأحد UIView
التوسع / العقد أكثر من الآخرين.
نقسم إلى أجزاء والكتابة في التعليمات البرمجية
لقد قسمنا وحدة التحكم إلى عدة أجزاء ، لكن حتى الآن لا يمكننا إعادة استخدامها ، UIStoryboard
الصعب استخراج الواجهة من UIStoryboard
في الأجزاء. إذا احتجنا إلى نقل بعض البيانات إلى المحتوى ، فسنضطر إلى التطرق إليها عبر التسلسل الهرمي بأكمله. يجب أن يكون الأمر عكس ذلك: أولاً ، قم بأخذ المحتوى ، وقم بتكوينه ، ثم لفه في الحاويات اللازمة. مثل المصباح.
تظهر ثلاث مهام في طريقنا:
- افصل كل وحدة تحكم في
UIStoryboard
الخاصة بها. - رفض
container view
، أضف وحدات التحكم إلى الحاويات في التعليمات البرمجية. - ربط كل شيء مرة أخرى.
تقاسم UIStoryboard
تحتاج إلى إنشاء جهازي UIStoryboard
ونسخ وحدة التحكم في التنقل ولصقها في وحدة التحكم في القالب. Embed segue
، ولكن سيتم نقل container view
مع القيود التي تم تكوينها. يجب حفظ القيود ، ويجب استبدال container view
بـ UIView
العادية.
أسهل طريقة هي تغيير نوع طريقة عرض الحاوية في رمز UIStoryboard. قمنا بتعيين وحدة التحكم إلى الخاصية is initial view controller
، وسوف نسمي UIStoryboard
كوحدة التحكم.
نحن تحميل وحدة تحكم من UIStoryboard.إذا كان اسم وحدة التحكم يتطابق مع اسم UIStoryboard
، عندئذٍ يمكن لف التنزيل من خلال طريقة ستجد الملف نفسه:
protocol Storyboardable { } extension Storyboardable where Self: UIViewController { static func instantiateInitialFromStoryboard() -> Self { let controller = storyboard().instantiateInitialViewController() return controller! as! Self } static func storyboard(fileName: String? = nil) -> UIStoryboard { let storyboard = UIStoryboard(name: fileName ?? storyboardIdentifier, bundle: nil) return storyboard } static var storyboardIdentifier: String { return String(describing: self) } static var storyboardName: String { return storyboardIdentifier } }
إذا تم وصف وحدة التحكم في .xib
، فسيتم تحميل المنشئ القياسي بدون مثل هذه الرقصات. للأسف ، يمكن أن يحتوي .xib
على وحدة تحكم واحدة فقط ، وغالبًا لا يكفي ذلك: في حالة جيدة ، تتكون شاشة واحدة من عدة. لذلك ، نحن نستخدم UIStoryborad
، فمن السهل تقسيم الشاشة إلى أجزاء.
إضافة وحدة تحكم في التعليمات البرمجية
لكي تعمل وحدة التحكم بشكل صحيح ، نحتاج إلى جميع أساليب دورة حياتها: will/did-appear/disappear
.
للعرض الصحيح ، تحتاج إلى استدعاء 5 خطوات:
willMove(toParent parent: UIViewController?) addChild(_ childController: UIViewController) addSubview(_ subivew: UIView) layout didMove(toParent parent: UIViewController?)
تقترح Apple تقليل الرمز إلى 4 خطوات ، لأن addChild()
نفسه سوف يستدعي willMove(toParent)
. باختصار:
addChild(_ childController: UIViewController) addSubview(_ subivew: UIView) layout didMove(toParent parent: UIViewController?)
للبساطة ، يمكنك التفاف كل شيء في extension
. بالنسبة insertSubview()
، نحتاج إلى إصدار مع insertSubview()
.
extension UIViewController { func insertFullframeChildController(_ childController: UIViewController, toView: UIView? = nil, index: Int) { let containerView: UIView = toView ?? view addChild(childController) containerView.insertSubview(childController.view, at: index) containerView.pinToBounds(childController.view) childController.didMove(toParent: self) } }
للحذف ، تحتاج إلى نفس الخطوات ، فقط بدلاً من وحدة التحكم الأصل ، تحتاج إلى تعيين nil
. الآن removeFromParent()
يستدعي didMove(toParent: nil)
، والتخطيط غير مطلوب. النسخة المختصرة مختلفة جدا:
willMove(toParent: nil) view.removeFromSuperview() removeFromParent()
تخطيط
وضع قيود
لتعيين حجم وحدة التحكم بشكل صحيح ، سوف نستخدم AutoLayout
. نحن بحاجة إلى تسمير جميع الأطراف في جميع الجوانب:
extension UIView { func pinToBounds(_ view: UIView) { view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ view.topAnchor.constraint(equalTo: topAnchor), view.bottomAnchor.constraint(equalTo: bottomAnchor), view.leadingAnchor.constraint(equalTo: leadingAnchor), view.trailingAnchor.constraint(equalTo: trailingAnchor) ]) } }
إضافة وحدة تحكم تابعة في التعليمات البرمجية
الآن يمكن الجمع بين كل شيء:
نظرًا لتكرار الاستخدام ، يمكننا التفاف كل هذا extension
:
هناك حاجة أيضًا إلى طريقة مماثلة لوحدة تحكم القالب. prepare(for segue:)
استخدام prepare(for segue:)
في prepare(for segue:)
، ولكن الآن يمكنك prepare(for segue:)
في طريقة تضمين التحكم:
إنشاء وحدة تحكم يشبه هذا:
إن توصيل شاشة جديدة بالقالب أمر بسيط:
- إزالة ما لا يتعلق بالمحتوى ؛
- تحديد أزرار الإجراءات عن طريق تطبيق بروتوكول OnboardingViewControllerDatasource ؛
- اكتب طريقة تربط قالب ومحتوى.
المزيد عن الحاويات
شريط الحالة
غالبًا ما يكون من الضروري التحكم في رؤية status bar
بواسطة وحدة تحكم ذات محتوى وليس حاوية. هناك بعض property
لهذا:
باستخدام هذه property
يمكنك إنشاء سلسلة من وحدات التحكم ، وهذه الأخيرة ستكون مسؤولة عن عرض status bar
.
منطقة آمنة
إذا كانت أزرار الحاوية تتداخل مع المحتوى ، فيجب عليك زيادة منطقة safeArea
. يمكن القيام بذلك في التعليمات البرمجية: set additinalSafeAreaInsets
لوحدات التحكم التابعة. يمكنك الاتصال به من embedController()
:
private func addSafeArea(to controller: UIViewController) { if #available(iOS 11.0, *) { let buttonHeight = CGFloat(30) let topInset = UIEdgeInsets(top: buttonHeight, left: 0, bottom: 0, right: 0) controller.additionalSafeAreaInsets = topInset } }
إذا أضفت 30 نقطة في الأعلى ، فسوف يتوقف الزر عن تداخل المحتوى safeArea
المنطقة الخضراء:

هوامش. الحفاظ على هوامش superview
وحدات التحكم لديها margins
قياسية. عادة ما تكون مساوية 16 نقطة من كل جانب من جوانب الشاشة وفقط في أحجام Plus هي 20 نقطة.
استنادًا إلى margins
يمكنك إنشاء ثوابت ، وستكون المسافة البادئة إلى الحافة مختلفة عن أجهزة iPhone المختلفة:

عندما نضع UIView
في مكان آخر ، UIView
margins
إلى النصف: إلى 8 نقاط. لمنع هذا ، تحتاج إلى تضمين Preserve superview margins
. عندئذ تكون margins
UIView
مساوية margins
الأصل. انها مناسبة للحاويات ملء الشاشة.
النهاية
وحدات تحكم الحاوية هي أداة قوية. تعمل على تبسيط التعليمات البرمجية والمهام المنفصلة ويمكن إعادة استخدامها. يمكنك كتابة وحدات التحكم المتداخلة بأي طريقة: في UIStoryboard
أو .xib
أو ببساطة في التعليمات البرمجية. الأهم من ذلك ، أنها سهلة الإنشاء ومتعة الاستخدام.
→ مثال على مقال عن جيثب
هل لديك شاشات يمكن من خلالها صنع قالب؟ شارك في التعليقات!