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

في البداية ، أردت أن أكتب مقالًا يفيد أنه في نظام التشغيل iOS 10 ، ظهر UIViewPropertyAnimator
المريح الذي يحل مشكلة الرسوم المتحركة التي تمت مقاطعتها. الآن يمكن إيقافها أو قلبها أو استمرارها أو إلغاؤها. آبل تدعو هذه الواجهة السوائل .
ولكن بعد ذلك أدركت: من الصعب التحدث عن مقاطعة الرسوم المتحركة لوحدات التحكم دون وصف لكيفية تحريك هذه التحولات بشكل صحيح. لذلك ، سيكون هناك مقالتان. في هذا ، سنكتشف كيفية إظهار وإخفاء الشاشة بشكل صحيح ، وحول الانقطاع في اليوم التالي.
كيف تعمل؟
يحتوي UIViewController
على خاصية " transitioningDelegate
. هذا هو بروتوكول مع وظائف مختلفة ، كل إرجاع كائن:
animationController
المتحركة ،interactionController
لمقاطعة الرسوم المتحركة ،presentationController
للعرض: التسلسل الهرمي ، الإطار ، إلخ.

بناءً على كل هذا ، سنقوم بإنشاء لوحة منبثقة:

تحكم الطبخ
يمكنك تحريك الانتقال لوحدات تحكم الوسائط و UINavigationController
(يعمل من خلال UINavigationControllerDelegate
).
سوف ننظر في التحولات مشروط. إعداد وحدة التحكم قبل العرض غير عادي بعض الشيء:
class ParentViewController: UIViewController { private let transition = PanelTransition()
- إنشاء كائن يصف الانتقال.
transitioningDelegate
وضع علامة " transitioningDelegate
على " weak
، لذا يتعين عليك تخزين " transition
بشكل منفصل بواسطة ارتباط strong
. - وضعنا انتقالنا إلى
transitioningDelegate
. - من أجل التحكم في طريقة
presentationController
في presentationController
تحتاج إلى تحديد .custom
لـ modalPresentationStyle.
.
وحدة التحكم المعروضة لا تعرف على الإطلاق كيف يتم عرضها. وهذا جيد.
تظهر في نصف الشاشة
دعنا نبدأ كود PanelTransition
مع presentationController
. لقد عملت معها إذا قمت بإنشاء النوافذ المنبثقة من خلال UIPopoverController
. يتحكم PresentationController
في عرض وحدة التحكم: الإطار ، التسلسل الهرمي ، إلخ. يقرر كيفية إظهار النوافذ المنبثقة على iPad: مع أي إطار ، أي جانب من الزر لإظهاره ، يضيف تمويه على خلفية النافذة ويظلمه تحتها.

هيكلنا مشابه: سنقوم بتغميق الخلفية ووضع الإطار في وضع ملء الشاشة:

لتبدأ ، في presentationController(forPresented:, presenting:, source:)
method ، قم بإرجاع فئة PresentationController
:
class PanelTransition: NSObject, UIViewControllerTransitioningDelegate { func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { return presentationController = PresentationController(presentedViewController: presented, presenting: presenting ?? source) }
لماذا يتم نقل 3 وحدات تحكم وما هو المصدر؟Source
هو وحدة التحكم التي أطلقنا عليها الرسوم المتحركة للعرض. لكن وحدة التحكم التي ستشارك في الشريحة هي الأولى من التسلسل الهرمي مع definesPresentationContext = true
. إذا تغيرت وحدة التحكم ، فسيكون جهاز التحكم الحقيقي في المعلمة presenting.
الآن يمكنك تطبيق فئة PresentationController
. بالنسبة للمبتدئين ، فلنضع إطارًا للتحكم في المستقبل. هناك إطار frameOfPresentedViewInContainerView
لهذا frameOfPresentedViewInContainerView
. دع وحدة التحكم تشغل النصف السفلي من الشاشة:
class PresentationController: UIPresentationController { override var frameOfPresentedViewInContainerView: CGRect { let bounds = containerView!.bounds let halfHeight = bounds.height / 2 return CGRect(x: 0, y: halfHeight, width: bounds.width, height: halfHeight) } }
يمكنك بدء المشروع ومحاولة إظهار الشاشة ، لكن لن يحدث شيء. هذا لأننا ندير الآن التسلسل الهرمي للمشاهدة بأنفسنا ونحتاج إلى إضافة عرض وحدة التحكم يدويًا:
لا تزال بحاجة لوضع إطار ل presentedView
. containerViewDidLayoutSubviews
هو أفضل مكان ، لأنه بهذه الطريقة يمكننا الاستجابة لدوران الشاشة:
الآن يمكنك الجري. ستكون الرسوم المتحركة قياسية بالنسبة لـ UIModalTransitionStyle.coverVertical
، لكن الإطار سيكون نصف الحجم.
تغميق الخلفية
المهمة التالية هي تغميق وحدة تحكم الخلفية للتركيز على ما يظهر.
سوف نرث من PresentationController
PanelTransition
بفئة جديدة في ملف PanelTransition
. في الفصل الجديد لن يكون هناك سوى رمز يعتم.
class DimmPresentationController: PresentationController
قم بإنشاء طريقة عرض سنتراكب فوق:
private lazy var dimmView: UIView = { let view = UIView() view.backgroundColor = UIColor(white: 0, alpha: 0.3) view.alpha = 0 return view }()
سنقوم بتغيير وجهات النظر alpha
وفقا للرسوم المتحركة الانتقالية. هناك 4 طرق:
presentationTransitionWillBegin
presentationTransitionDidEnd
dismissalTransitionWillBegin
dismissalTransitionDidEnd
أول واحد هو الأكثر صعوبة. أضف dimmView
إلى التسلسل الهرمي ، ضع الإطار وابدأ الرسوم المتحركة:
override func presentationTransitionWillBegin() { super.presentationTransitionWillBegin() containerView?.insertSubview(dimmView, at: 0) performAlongsideTransitionIfPossible { [unowned self] in self.dimmView.alpha = 1 } }
يتم إطلاق الرسوم المتحركة باستخدام وظيفة مساعدة:
private func performAlongsideTransitionIfPossible(_ block: @escaping () -> Void) { guard let coordinator = self.presentedViewController.transitionCoordinator else { block() return } coordinator.animate(alongsideTransition: { (_) in block() }, completion: nil) }
قمنا بتعيين إطار dimmView
في containerViewDidLayoutSubviews
(مثل آخر مرة):
override func containerViewDidLayoutSubviews() { super.containerViewDidLayoutSubviews() dimmView.frame = containerView!.frame }
يمكن مقاطعة الرسوم المتحركة dimmView
، وإذا تم إلغاؤها ، فيجب إزالة dimmView
من التسلسل الهرمي:
override func presentationTransitionDidEnd(_ completed: Bool) { super.presentationTransitionDidEnd(completed) if !completed { self.dimmView.removeFromSuperview() } }
تبدأ العملية العكسية في إخفاء الأساليب. لكن الآن تحتاج إلى إزالة dimmView
فقط إذا كانت الرسوم المتحركة قد اكتملت.
override func dismissalTransitionWillBegin() { super.dismissalTransitionWillBegin() performAlongsideTransitionIfPossible { [unowned self] in self.dimmView.alpha = 0 } } override func dismissalTransitionDidEnd(_ completed: Bool) { super.dismissalTransitionDidEnd(completed) if completed { self.dimmView.removeFromSuperview() } }
الآن الخلفية يعتم.
نحن نتحكم في الرسوم المتحركة
نعرض تحكم من أدناه
الآن يمكننا تحريك مظهر وحدة التحكم. في فئة PanelTransition
، قم بإرجاع الفئة التي تتحكم في الرسوم المتحركة للمظهر:
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return PresentAnimation() }
تنفيذ البروتوكول بسيط:
extension PresentAnimation: UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let animator = self.animator(using: transitionContext) animator.startAnimation() } func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { return self.animator(using: transitionContext) } }
رمز المفتاح أكثر تعقيدًا قليلاً:
class PresentAnimation: NSObject { let duration: TimeInterval = 0.3 private func animator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
UIViewPropertyAnimator لا يعمل في نظام التشغيل iOS 9الحل بسيط للغاية: تحتاج إلى عدم استخدام الرسوم المتحركة في رمز animateTransition
، ولكن UIView.animate…
برمجة UIView.animate…
القديمة UIView.animate…
على سبيل المثال ، مثل هذا:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let to = transitionContext.view(forKey: .to)! let finalFrame = transitionContext.finalFrame(for: transitionContext.viewController(forKey: .to)!) to.frame = finalFrame.offsetBy(dx: 0, dy: finalFrame.height) UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: [.curveEaseOut], animations: { to.frame = finalFrame }) { (_) in transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } } , `interruptibleAnimator(using transitionContext:)`
إذا لم تقم بإجراء مقاطعة ، فيمكن حذف طريقة interruptibleAnimator. سيتم النظر في التوقف في المادة التالية ، الاشتراك.
إخفاء وحدة تحكم أسفل
كل شيء هو نفسه ، فقط في الاتجاه المعاكس. في Panel Transition
بإنشاء فئة DismissAnimation
:
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return DismissAnimation() }
ونحن ندرك ذلك. DismissAnimation
الفصل DismissAnimation
class DismissAnimation: NSObject { let duration: TimeInterval = 0.3 private func animator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { let from = transitionContext.view(forKey: .from)! let initialFrame = transitionContext.initialFrame(for: transitionContext.viewController(forKey: .from)!) let animator = UIViewPropertyAnimator(duration: duration, curve: .easeOut) { from.frame = initialFrame.offsetBy(dx: 0, dy: initialFrame.height) } animator.addCompletion { (position) in transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } return animator } } extension DismissAnimation: UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let animator = self.animator(using: transitionContext) animator.startAnimation() } func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { return self.animator(using: transitionContext) } }
في هذا المكان ، يمكنك تجربة الأطراف:
- قد يظهر سيناريو بديل أدناه ؛
- على اليمين - التنقل السريع في القائمة ؛
- أعلاه - رسالة إعلامية:

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