أساسيات الـ Animation في Swift 3 الجزء السادس
نعود الى اكمال السلسلة بعد انقطاع لفتره طويلة
في هذا الجزء سوف نكمل ما تحدثنا عنه في الجزء السابق وهو الـ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
بحجم ١٢٢ في ١٢٢
لجعله بحجم مربع
ومن ثم اضيف زر أيضا
كما في الصورة التالية :
ومن ثم اربط الـ 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)
قم بتشغيل الكود وشاهد النتيجة :
الكود السابق وظيفته هو تحول من مربع الى دائرة
ويبدا الـ 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
قم بتشغيل التطبيق ولاحظ النتيجة في الصورة التالية :
امراً غريباً الذي حدث
تم تحويل المربع الى دائرة بشكل مفاجئة و من ثم عاد الى الشكل المربع وحدث الـ 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
الان قم بتشغيل الكود وسوف تجد النتيجة كما في الصورة التالية :
الموضوع الثالث : خصائص التوقيت 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
لأني لا أريد ابقائه على شكل الدائرة
في حال عدم حذفي لهذا السطر سوف تلاحظ امرأ غريباً
على أي حال قم بتشغيل الكود وشاهد النتيجة كما في الصورة التالية :
ماذا اذا اردت تكرار نفس الـ 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
في البداية سوف استخدم الكود السابق
بدون إضافة زيادة السرعة
لاحظ النتيجة :
ومن ثم سوف اجعل السرعة الضعف باستخدام الكود التالي :
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
قم بتشغيل الكود وشاهده النتيجة :
هل لاحظت الفرق ؟
سرعة تشغيل الـ 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
الان قم بتشغيل الكود ولاحظ النتيجة :
لاحظت بأني لم أضيف خصائص الـ 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
لنكمل شرح أخر فقره في موضوع اليوم =)
الخصائص الأساسية للتوقيت :
هل تذكر هذه الصورة ؟
تم شرحها في الدرس الثاني من السلسلة
هناك ٤ انواع رئيسية مسؤوله عن سرعة الـ 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
الان قم بتشغيل الكود ولاحظ النتيجة :
ومن ثم غير القيمة الى kCAMediaTimingFunctionEaseIn
ولاحظ النتيجة :
ملاحظة اخيره :
تستطيع عمل TimingFunction خاص بك باستخدام السطر
CAMediaTimingFunction(controlPoints: _: _: _:)
الصورة هذه توضح بعض الانواع الممكن عملها
وصفحة الـ 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)
عرض المزيد.. جديد مقالاتي
لايوجد لديك حساب في عالم البرمجة؟
تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !