أساسيات الـ Animation في Swift 3 الجزء السادس

باسل بارقبهمنذ 6 سنوات

نعود الى اكمال السلسلة بعد انقطاع لفتره طويلة

 

في هذا الجزء سوف نكمل ما تحدثنا عنه في الجزء السابق وهو الـLayer Animation

او ان صح التعبير نستكمل شروحات الـ Core Animation

 

وتحديداً الـ CABasicAnimation

 

الموضوع الاول كيفية تأخير الـ Animation

 

عند شرحنا للـ UIView Animation
ذكرنا بأنه هناك Function
يحتوي على Parameter
الـ Delay
عند إعطائه قيمة (تحسب بالثانية الواحده)
يقوم على تأخير بدأ الـ Animation

بعدد الثواني المطلوبة

 

مثال للكود المستخدم في UIView.animate

 


UIView.animate(withDuration: 1.0, delay: 0.0, options:[], animations: { 
}, completion: nil)

 

الطريقة واضحه في الـ UIView Animation

 

لكن في الـ Core Animation

الوضع مختلف

 

لا يوجد ملكية من نوع Delay

ولكن يتم تنفيذ التأخير بطريقة مختلفة

 

وهيا باختصار حساب

إعطاء ملكية beginTime قيمة الوقت الحقيقي + التاخير بالثواني

لكي يكون الامر واضح

سوف نستخدم المربع الأزرق

 

قم بإضافة UIView
بحجم ١٢٢ في ١٢٢

لجعله بحجم مربع

 

ومن ثم اضيف زر أيضا

 

كما في الصورة التالية :

5a11c7d47f4e4_ScreenShot2017-11-17at3_01_13PM.thumb.png.1dbf71db20861fcc73b8babedc917d22.png

ومن ثم اربط الـ UIView وأيضا الزر بملف الاكواد

 

قمت بإعطاء اسم squareView الى الـ UIView

وقمت بسمية اسم الـ Function الخاص بالزر باسم AnimationButton()

 

كما فعلت في الدرس السابق

 

الان سوف أقوم بكتابة الاكواد التالية بداخل الـ AnimationButton()

 


let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius")  

cornerRadiusAnimation.duration = 0.5 

cornerRadiusAnimation.fromValue = 0 

cornerRadiusAnimation.toValue = 61 

cornerRadiusAnimation.beginTime = CACurrentMediaTime() + 0.5

squareView.layer.add(cornerRadiusAnimation, forKey: nil)  

 

قم بتشغيل الكود وشاهد النتيجة :

1.gif.49a23e86e55d24f3597175d51ad7a0dc.gif

 

الكود السابق وظيفته هو تحول من مربع الى دائرة

 

ويبدا الـ Animation بعد 0.5 ثانية ولمدة 0.5 ثانية

 

فكما تلاحظ هو التالي

لعمل تأخير تحتار الى كتابة الكود


.beginTime = CACurrentMediaTime() + مدة التأخير

حان الوقت لإعطاء ملاحظة =)

 

ملاحظة :

ذكرت في الدروس الماضية

لتحويل من مربع الى دائرة

فانك تحتاج تعرف قيمة الطول او العرض ومن ثم تقسمه على ٢

واخيراً تضيف القيمة في cornerRadius

 

فهنا لكي نقوم بتحويل المربع الى دائره

قسمنا ١٢٢ على ٢ والقيمة كانت ٦١ لذا اضفناها في خانة toValue

 

الطريقة الأخرى هيا جلب القيمة الطول او العرض للـ UIView وتقسمها على ٢

 

بالطريقة التالية


squareView.frame.width / 2

 

هنا طلبنا قيمة العرض من UIView المربع ومن ثم قسمناها على ٢

 

لا فرق بين جلب العرض او الطول لانه بطيبيعه الحال المربع يكون متساوي القيمة في العرض وطوال

 

نعود للدرس

 

كما لاحظت في الصورة المتحركة السابقة بعد انتهاء الـ Animation

 

عادت الدائرة الى مربع

 

ذكرت في الدرس الماضي لجعل الـ UIView

في حالته النهائية

 

يتوجب عليك كتابة القيمة مره أخرى بعد كود تنفيذ الـ Animation

الذي هو بعد سطر


squareView.layer.add(cornerRadiusAnimation, forKey: nil)

بما يعني يتوجب عليك كتابة السطر التالي
 


 squareView.layer.cornerRadius = squareView.frame.width / 2

 

 

فيصبح الكود بالشكل التالي :
 


let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius") 

cornerRadiusAnimation.duration = 0.5

 cornerRadiusAnimation.fromValue = 0 

cornerRadiusAnimation.toValue = squareView.frame.width / 2

cornerRadiusAnimation.beginTime = CACurrentMediaTime() + 0.5

squareView.layer.add(cornerRadiusAnimation, forKey: nil) 

squareView.layer.cornerRadius = squareView.frame.width / 2

 

قم بتشغيل التطبيق ولاحظ النتيجة في الصورة التالية :

2.gif.b6e7e0e69a4005c869430e819b98130a.gif

 

امراً غريباً الذي حدث

تم تحويل المربع الى دائرة بشكل مفاجئة و من ثم عاد الى الشكل المربع وحدث الـ Animation

واخيراً توقف الـ Animation

على شكل الدائرة

 

ما الذي حدث ؟

 

لماذا تحول في البداية الى دائرة بشكل سريع ؟

 

هذا الحدث الغريب يوجهنا لموضوع جديد وهو الـ FillMode

 

 

الموضوع الثاني : ماهو الـ FillMode ؟

 

الـ FillMode يسمح لك بالتحكم بسلوك الـ Animation اثناء بدايته ونهايته

 

وهو ٤ أنواع :

 

kCAFillModeRemoved النوع الأول يطلق عليه

وظيفته يبدا الـ Animation فوراً ومن ثم يحذفه عند اكتماله (انتهائه)

هذا هو الخيار الافتراضي

 

النوع الثاني يطلق عليه  kCAFillModeBackwards

وظيفته يعرض اول Frame للـ Animation قبل بداية الـ Animation

ومن ثم يبدأ الـ Animation ومن ثم ينتهي

 

النوع الثالث يطلق عليه  kCAFillModeForwards

يبدا الـ Animation فوراً ولكن يبقى أخر Frame حتى تقوم بإزالته

 

النوع الرابع يطلق عليه  kCAFillModeBoth

وهو دمج النوعين السابقين مع بعض

بحيث يعرض اول Frame قبل بداية الـ Animation

ومن ثم يبدأ الـ Animation ومن ثم ينتهي

واخيراً يبقى أخر Frame بعد انتهاء الـ Animation

 

نعود للموضوع لحل المشكلة التي واجهتنا

 

أعتقد بأن الان فهمت الفكرة وكيف تحلها !

 

من تجربتي وجدت المشكلة تحدث في بعض الحالات

ومن هذه الحالات عند تأخير بدأ الـ Animation

فيحدث أمر مختلف عن ما كنت تتوقعه كما حدث معانا

 

وطريقة حل المشكلة اما بتغيير الـ FillMode الى kCAFillModeBackwards

وبالتالي سوف يبقي أول Frame في قبل بدأ الـ Animation وبعدها يبدا الـ Animation

وبالتالي تنحل المشكلة او يمكنك اذا اردت استخدام الـ kCAFillModeBoth

 

في كلا الحالتين سوف تحل المشكلة التي واجهتنا

 

 

سوف اقوم باستخدام الـ kCAFillModeBackwards

 

لذا نقوم بإضافة الكود التالي


cornerRadiusAnimation.fillMode = kCAFillModeBackwards

وبتالي سوف يصبح الكود بالشكل التالي :


let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius") 

cornerRadiusAnimation.duration = 0.5   

cornerRadiusAnimation.fromValue = 0

cornerRadiusAnimation.toValue = squareView.frame.width / 2 

cornerRadiusAnimation.beginTime = CACurrentMediaTime() + 0.5

cornerRadiusAnimation.fillMode = kCAFillModeBackwards

squareView.layer.add(cornerRadiusAnimation, forKey: nil)  

squareView.layer.cornerRadius = squareView.frame.width / 2

 

الان قم بتشغيل الكود وسوف تجد النتيجة كما في الصورة التالية :

3.gif.05e2a9a9bd9e873f813f81283d38eeaa.gif

 

الموضوع الثالث : خصائص التوقيت Timing Options

 

كيف تقوم باستخدام  autoreverses

في الـ CoreAnimation ؟

 

اذا لا تذكر ماهي وظيفة الـ autoreverses

فوظيفته هو إعادة الـ Animation بشكل عكسي بعد الانتهاء من تنفيذه

 

في مثالنا السابق قمنا بتحويل المربع الى دائرة ومن ثم بعد اكتماله قمنا بإبقائه على شكل الدائرة

 

باستخدام الـ autoreverses

تستطيع تحويله من مربع الى دائرة ومن ثم من دائرة الى مربع

 

 

قم بكتابة الكود التالي :

 


let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius")

cornerRadiusAnimation.duration = 0.5   

cornerRadiusAnimation.fromValue = 0

cornerRadiusAnimation.toValue = squareView.frame.width / 2 

cornerRadiusAnimation.beginTime = CACurrentMediaTime() + 0.5 

cornerRadiusAnimation.fillMode = kCAFillModeBackwards    

cornerRadiusAnimation.autoreverses = true      

squareView.layer.add(cornerRadiusAnimation, forKey: nil)  

 

كل الذي اضفته هو سطر


    cornerRadiusAnimation.autoreverses = true

 

وقمت بحذف سطر


 squareView.layer.cornerRadius = squareView.frame.width / 2

 

لأني لا أريد ابقائه على شكل الدائرة

في حال عدم حذفي لهذا السطر سوف تلاحظ امرأ غريباً

 

على أي حال قم بتشغيل الكود وشاهد النتيجة كما في الصورة التالية :

4.gif.8b36fb5f8d78c747901181a9b537dcad.gif

ماذا اذا اردت تكرار نفس الـ Animation بعدد مرات محدده ؟

 

هناك امرين تستطيع فعله

 

الاول هو تكرار الـ Animation بعدد مرات محدده وذلك باستخدام الملكية repeatCount كما في الكود التالي


    cornerRadiusAnimation.repeatCount = 3

او بعدد ثواني محدده باستخدام ملكية repeatDuration كما في الكود التالي


cornerRadiusAnimation.repeatDuration = 6

لا تستطيع استخدام الاثنين في نفس الوقت لأنه سوف يتم تجاهله احداهما

 

على أي حال سوف استخدم امر التكرار بعدد مرات محدده

 

لذا سوف يصبح الكود بالشكل التالي :
 


let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius")    

cornerRadiusAnimation.duration = 0.5   

cornerRadiusAnimation.fromValue = 0 

cornerRadiusAnimation.toValue = squareView.frame.width / 2  

cornerRadiusAnimation.beginTime = CACurrentMediaTime() + 0.5    

cornerRadiusAnimation.fillMode = kCAFillModeBackwards      

cornerRadiusAnimation.autoreverses = true         

cornerRadiusAnimation.repeatCount = 3       

squareView.layer.add(cornerRadiusAnimation, forKey: nil)  

 

ملاحظة :

 

ليس ضروري وضع (عدد صحيح) في التكرار

بما يعني عند استخدام repeatCount

 

تستطيع عمل نص تكرار

مثال


.repeatCount = 3.5

هنا سوف ينفذ ٣ تكرارات كامله وفي اخر تكرار سوف ينفذ نص الـ Animation

 

اعلم بأن الامر غريباً ولكنه ممكناً ومفيداً أحياناً !

 

 

نعود للدرس

 

تغير سرعة الـ Animation :

او بعباره أخرى تسريع الـ Animation

 

لكي تتضح الصورة

هل لاحظت بأنه عند تشغيل مقطع معين على موقع الـ Youtube

تستطيع تغير سرعته ؟

 

مثال طول المقطع هو ١٠ دقائق

ووضعت السرعة x2

بمعنى اخر ضعف السرعة

فالمقطع سوف تشاهده في مدة ٥ دقائق والسبب هو سرعة تشغيله أصبحت الضعف

وبالتالي مدة مشاهدته أصبحت نص طول المقطع

 

نفس الامر تستطيع عمله مع الـ Animation

 

في البداية سوف استخدم الكود السابق

بدون إضافة زيادة السرعة

 

لاحظ النتيجة :

5.gif.66a6ade3e1c20ae3d92efa8426d1b710.gif

ومن ثم سوف اجعل السرعة الضعف باستخدام الكود التالي :
 


  cornerRadiusAnimation.speed = 2

 

وبالتالي سوف يصبح الكود النهائي بالشكل التالي :
 


let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius")  

cornerRadiusAnimation.duration = 0.5      

cornerRadiusAnimation.fromValue = 0 

cornerRadiusAnimation.toValue = squareView.frame.width / 2   

cornerRadiusAnimation.fillMode = kCAFillModeBackwards    

cornerRadiusAnimation.speed = 2  

squareView.layer.add(cornerRadiusAnimation, forKey: nil)   

squareView.layer.cornerRadius = squareView.frame.width / 2

 

قم بتشغيل الكود وشاهده النتيجة :

6.gif.95cacb242f34bcb6bf08a9a8d940e309.gif

 

هل لاحظت الفرق ؟

 

سرعة تشغيل الـ Animation أصبحت الضعف دون الحاجة الى تغير مدة الـ Animation

(بدون تغيير الـ duration)

 

الان سوف تستغرب لماذا لم نغير الـ duration

بدل من تسريع الـ Animation !

 

الاجابة سوف تتضح في الدرس القادم إن شاء الله

 

لكن في الوقت الحالي باستطاعتي إيضاح نقطه ما

 

وهيا باستطاعتك تسريع الـ layer بذاته !

 

ذكرت في الدرس السابق بأنه من فوائد الـ CoreAnimation

هيا عدم ارتباطه بأي UIView
بما يعني نقوم بإضافة خصائص الـAnimation ومن ثم ننفذها على Layer الـ UIView

المراد

بكتابة الامر


squareView.layer.add(cornerRadiusAnimation, forKey: nil)

 

يتضح بما سبق التالي :

 

نستطيع إضافة UIView أخر

 

كما فعلنا في الدرس الماضي بإضافة مربع اخضر مع المربع الأزرق

 

سوف أقوم بتسميته squareView2

 

ومن ثم سوف اضيف سطر واحد لتنفيذ نفس الـ Animation

على الـ squareView2

 

بكتابة الامر التالي :


   squareView2.layer.add(cornerRadiusAnimation, forKey: nil)

 

ومن ثم عند نهاية الكود سوف أقوم بكتابة هذا السطر

لكي احافظ على شكل الدائرة بعد انتهاء الـ  Animation


squareView2.layer.cornerRadius = squareView.frame.width / 2

 

الان قم بتشغيل الكود ولاحظ النتيجة :

7.gif.47b1c33e4e4075d808dabcbcb1009da3.gif

 

لاحظت بأني لم أضيف خصائص الـ Animation

 

مره أخرى ، فقط طلبت تنفيذ نفس الـ Animation

على الـ squareView2

 

ونفس الـ Animation تم تنفيذه على كلا المربعين

 

الان سوف أقوم بتسريع Layer
الـ squareView2

 

بكتابة الامر التالي


squareView2.layer.speed = 2

 

وبالتالي سوف يصبح الكود النهائي بالشكل التالي :
 


let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius")  

cornerRadiusAnimation.duration = 0.5        

cornerRadiusAnimation.fromValue = 0

cornerRadiusAnimation.toValue = squareView.frame.width / 2  

cornerRadiusAnimation.fillMode = kCAFillModeBackwards     

squareView.layer.add(cornerRadiusAnimation, forKey: nil)   

squareView2.layer.speed = 2        

squareView2.layer.add(cornerRadiusAnimation, forKey: nil)   

squareView.layer.cornerRadius = squareView.frame.width / 2   

squareView2.layer.cornerRadius = squareView.frame.width / 2

 

قم بتشغيل الكود ولاحظ النتيجة :

 

 

هل لاحظت ؟

 

المربع الأخضر أسرع من المربع الأزرق

لأننا قمنا بتسريع الـ Animation

للمربع الأخضر ولم نغير سرعة المربع الأزرق

 

اعتقد بأنه الان اتضحت الفائدة من تغيير سرعة الـ Animation

 

 

لنكمل شرح أخر فقره في موضوع اليوم =)

 

الخصائص الأساسية للتوقيت :

 

هل تذكر هذه الصورة ؟

AnimationOption.gif.e1ff159720b36d3938f226d1e2d6f23a.gif

تم شرحها في الدرس الثاني من السلسلة

 

هناك ٤ انواع رئيسية مسؤوله عن سرعة الـ Animation ، وهيا :

curveLinear : ويعني يبدا الـ Animation بنفس السرعة وينتهي بنفس السرعة (لا يحدث اختلاف في السرعة)

curveEaseIn : ويعني يبدا الـ Animation بسرعة بطيئة ومن ثم يزداد سرعته (يبدا بطئي وينتهي بسرعة)

curveEaseOut : ويعني يبدا الـ Animation بسرعة وينتهي ببطئ (يبدا بسرعة وينتهي ببطئ)

curveEaseInOut : هذا النوع هو الافتراضي،  ويعني يبدا الـ Animation ببطئ ومن ثم يسرع وينتهي ببطئ (يبدا وينتهي ببطئ)

 

 

نفس الامر ينطبق على الـ CoreAnimation

 

الاختلاف في المسميات

 

الـ curveLinear هو kCAMediaTimingFunctionLinear

الـ curveEaseIn هو kCAMediaTimingFunctionEaseIn

الـ curveEaseOut هو kCAMediaTimingFunctionEaseOut

الـ curveEaseInOut هو kCAMediaTimingFunctionEaseInEaseOut

 

طريقة استخدام الـ Options

بالطريقة التالية

 

بكتابة سطر


    cornerRadiusAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)

 

اذا إضافة الكود السابق لن تلاحظ فرق عند التحويل من مربع الى دائرة

حتى اذا غيرت النوع الى الانواع الأخرى

لكن بسهولة سوف تلاحظ عند التحريك من موقع الى اخر

 

لذا سوف اغير الكود الى هذا الكود
 


    let position = CABasicAnimation(keyPath: "position.y") 

position.duration = 0.3       

position.fromValue = squareView.layer.position.y  

position.toValue = 400   

position.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)

squareView.layer.add(position, forKey: nil) 

squareView.layer.position.y = 400  

 

من النصائح المفيدة انك تسمي المتغير بنفس اسم الـ Animation

التي تريد عمله ، هذه النصيحة سوف تفييدك في الدرس القادم =)

 

لذا قمت بتسمة المتغير باسم position

 

ولاني ارد تحريكه عاموديا كتبت


position.y

 

ملاحظة :

في سطر


position.fromValue

كتبت


squareView.layer.position.y

 

ولم أقوم بإعطائه قيمة كما فعلت في الدرس الماضي

السبب هو هذه الطريقة افضل لأنك تطلب منه يجلب القيمة الحالية للـ UIView

-  تستطيع أيضا استخدام الامر


squareView.center.y

 

الان قم بتشغيل الكود ولاحظ النتيجة :

8-EaseOut.gif.0d73487d04be96f574a4df370f32fa5c.gif

 

ومن ثم غير القيمة الى kCAMediaTimingFunctionEaseIn

 

ولاحظ النتيجة :

8-EaseIn.gif.48c772cb00bc4a984a0d173451fd4709.gif

ملاحظة اخيره :

تستطيع عمل TimingFunction خاص بك باستخدام السطر


CAMediaTimingFunction(controlPoints: _: _: _:)

 

الصورة هذه توضح بعض الانواع الممكن عملها

easings.png.1933c337d7d8524bdf256a9c298c121a.png

 

وصفحة الـ GitHub هذه سوف نجد بعض الانواع وكيفية كتابتها ككود

 

مثلا لنفترض تريد عمل نوع

 

افتح صفحة الـ GitHub
ابحث عن EaseInOutQuad

ستجده الكود التالي :


CAMediaTimingFunction(controlPoints: 0.455, 0.03, 0.515, 0.955)

قم باستبداله بالكود الذي كتبناه سابقا

فيصبح الكود بالشكل التالي :


  position.timingFunction = CAMediaTimingFunction(controlPoints: 0.455, 0.03, 0.515, 0.955)

وقم بتشغيل الكود ولاحظ النتيجة

 

اذا اردت التجربة واكتشاف الفروقات قم بتجربة الانواع الموجودة =)

فهذا الامر فقط موجود في الـ CoreAnimation

وليس موجود في UIView Animation

كلمات دليلية:
0
إعجاب
2944
مشاهدات
0
مشاركة
0
متابع
متميز
محتوى رهيب

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !