X901

Members
  • عدد منشوراتي

    7
  • تاريخ الإنضمام

  • تاريخ اخر زياره

السمعه بالموقع

8 Neutral

عن العضو X901

  • الرتبه
    مبدع جديد
  1. تعلمنا في ما سبق عن نوع من الـ Animation وهو الـ UIView.animate والذي يعتبر الأكثر استخداما واليوم سوف نتعلم عن نوع مختلف وهو UIView.transition قبل ان نبدأ في الدرس ، ما الفرق بينهما ؟ باختصار UIView.animate مسؤول عن ملكية الـ UIView من عمل تحريك وتكبير والتفاف وغيرها في حين UIView.transition مسؤول عن إضافة وحذف الـ View تستطيع عند إضافة الـ View كـ Subview انك تعمل Animation في لحظة اضافته او حذفه اعلم الكلام غير مفهوم ! مع التطبيق سوف تتضح الصورة . لتسهيل الامور لن اتطرق الى موضوع إضافة وحذف الـ SubView عن طريق الاكواد ولكن سوف اشرحها بطريقة اكثر بساطة . اذا لنبدأ الدرس . نقوم بإضافة Label ونجعل لون النص باللون الأبيض ولون الخلفية باللون الأسود ومن ثم نضيف زر كما في الصورة التالية : نقوم بإضافتهم الى ملف اكواد التطبيق ونجعل اسم الـ Label بـ Label واسم الـ Function بـ Transition @IBOutlet weak var Label: UILabel! @IBAction func Transition(_ sender: Any) { } الان في viewDidLoad نضيف التالي : Label.isHidden = true لماذا ؟ ذكرنا في السابق بأن الـ UIView.transition يعمل اثناء إضافة وحذف الـ View لذا في الكود السابق ما نقوم به هو حذفه من الـ View بجعله غير ظاهر ! في الدروس السابقة استخدمنا .alpha فما الفرق ؟ الـ .alpha هي الشفافية بمعنى 1 يعتبر لا توجد شفافية في حين 0.5 تعني وجود شفافية و 0 يعني انعدام الشفافية وهي من ملكيات الـ UIView.animate بصورة أخرى من المكيات التي يمكن عمل لها animate لكن الـ isHidden يزيل الـ View وليست من الملكيات التي تستطيع عمل لها animate فاذا استخدمتها بداخل اقواس UIView.animate لن يظهر أي Animation ! بصورة مختصرة : استخدام UIView.transition مع الملكيات التي لا يمكن عمل لها Animation نعود للدرس الان نقوم بعمل التالي بداخل اقواس Transition UIView.transition(with: Label, duration: 0.5, options: .transitionCurlDown, animations: { self.Label.isHidden = false }, completion: nil) كما تلاحظ بصورة عامة فهو مشابه للـ UIView.animate الاختلاف with وتعني ماهو الـ view الذي تريد عمل له الـ transition تكتب نفس اسم الـ View وهنا نحن استخدمنا Label فنقوم بكتابة اسم الـ Label الـ options هناك أنواع مختلف سوف اذكرها بعد قليل ما اريد توضيحه هنا الانواع التي ذكرتها في موضوعي السابق .curveEaseIn واخواتها يمكنك دمجها مع الانواع الخاصة بالـ transition بعمل مربع الاريه [] والفصل بينهم بعلامة فاصلة ماقمنا بفعله داخل الاقواس هو فقط حولنا الحالة من مخفي الى ظاهر بالتغير من true الى false أنواع الـ options : transitionCurlDown : وهو النوع الذي استخدمناه في الكود السابق transitionCurlUp : هيا عملية معاكسه للعملية السابقة لم استطيع عمل صورة متحركة توضحها transitionFlipFromTop: :transitionFlipFromBottom :transitionFlipFromRight :transitionFlipFromLeft transitionCrossDissolve: هناك نوع أخير وهو .showHideTransitionViews سيتضح فائدته في الـ Function الاخر للـ transition من الامور التي اتضحت من أنواع الـ Options السابقة بأنك قد ترغب باستخدام الـ transition لعمل تأثير معين لن تستطيع عمله باستخدام الـ UIView.animate ولكن من الامور الأخرى هو عمل تأثير انتقالي وسيكون المثال التالي توضيحا للطريقة قبل العوده لاستكمال الدرس هنا تلميحه بسيطة وهي هل تريد مشاهدة التأثير ببطيء ؟ عند تشغيل التطبيق على المحاكي اذهب الى خانة Debug واختار خيار Slow Animations ومن ثم اضغط على الزر وسظهر الـ Animation بصورة بطيئة (سلو موشن) كما في الصورة التالية : مثال : نعود للدرس الان سوف ننتقل الى نوع أخرى من أنواع الـ UIView.transition UIView.transition(from: , to: , duration: , options: , completion: nil) كما تلاحظ في الكود السابق بأنه يطلب نوعين من الـ UIView ، وهذا النوع هو نوع انتقالي بحيث يعمل عملية انتقالية بين الـ View الاول الى الـ View الثاني بحيث يخفي الاول ويظهر الثاني ! دعونا نبدأ في المثال أولا : نذهب الى الـ Storyboard ونضيف التالي : نضيف UIView ونجعله بحجم مستطيل صغير ومن ثم بداخله نضيف UIView اخر ونجعله بنفس حجم الـ UIView السابق ونغير لونه الى الاخضر ومن ثم نضيف Label اذا اردت وأيضا نضيف Button ، نحذف النص الذي بداخله ونجعله بحجم الـ UIView كما في الصورة التالية : ومن ثم نقوم بنسخ الـ UIView الذي قمنا بعمله في الخطوة السابقة ونغير النص ولون الـ UIView الى الازرق كما في الصورة التالية : الان اصبح لدينا ثلاثة من الـ UIView اثنين بداخل UIView واحد وفوق كل UIView زر بما يعني ٣ من الـ UIView و ٢ من الـ Button قد تتسأل لماذا اضفنا UIView وبداخله (فوقه) اضفنا اثنين من الـ UIView بدلا من إضافة اثنين من الـ UIView مباشرةً الى الـ ViewController؟ السبب لأنه عند عمل الـ transition سنقلب الـSuperview الذي سيكون الـ View الأساسي بمعنى سوف تنقلب كامل الصفحة !! لكن عند إضافة UIView وجعلنا اثنين من الـ UIView بداخله الذي سوف ينقلب هو الـ UIView الي اضفناه لأنه اصبح هو الـSuperview بالنسبة لهم بما يعني لن تنقلب الصفحة كامله ! بما يعطي ايحاء بتأثير انقلاب البطاقة ! ملاحظة : في الـ StoryBoard من يكون في اخر التسلسل يكون هو الاول (الظاهر) فالان اصبح الـ View 2 هو الاول والـ View 1 هو الثاني يمكنك تغيير المسميات او فقط تقوم بسحب الـ View 2 وتجعله فوق View 1 بالشكل التالي : ملاحظة ٢ : عندما يكون هناك نوعين مختلفة فوق بعض (في المثال هذا هناك اثنين من الـ View) ، سوف يكون من الصعب مشاهدة الـ View الذي في الخلف ، اذا طبقت الملاحظة الاولى بعد عمل القيود سوف تواجه مشاكل مع القيود ! اذا ما الحل ؟ كل ما عليك فعله هو الضغط على الـ View الذي في المقدمة (الذي يكون الأخير في الترتيب) ومن ثم إزالة علامة الصح من Installed وعندها ستخفي وسيظهر الـ View الذي كان في الخلف وسيسهل عليك تعديله بعد الانتهاء من التعديلات قم بتفعيل الصح مره أخرى ! صورة توضيحية : الان نقوم بإضافتهم الى ملف الاكواد سنقوم بإضافة فقط الـ UIView الذي باللون الأزرق والاخضر وأيضا سوف نضيف Action للزر ونربط الزرين بهذا الـ Action بصيغة أخرى سوف يصبح لدينا Function واحد وزرين مرتبطين به ! لأننا نريد جعل البطاقة تنقلب مرتين عند اللمس عوضا عن عمل اثنين من الـ Function سوف نعمل على Function واحد فقط كما في الصورة التالية : وبالتالي ملف الاكواد سوف يصبح بالشكل التالي : import UIKit class ViewController: UIViewController { @IBOutlet weak var View1: UIView! @IBOutlet weak var View2: UIView! override func viewDidLoad() { super.viewDidLoad() } @IBAction func FlipButton(_ sender: UIButton) { } الان بداخل الـ FlipButton نقوم بكتابة التالي : @IBAction func FlipButton(_ sender: UIButton) { UIView.transition(from: self.View1, to: self.View2, duration: 0.5, options: [.transitionFlipFromRight , .showHideTransitionViews], completion: nil) } ما قمنا به هو جعلنا الـ View1 ينقلب الى View2 لاحظ اننا استخدمنا الـ showHideTransitionViews لماذا ؟ لانه بعد عملية الانتقال سوف يتم حذف الـ View1 من الـ Suberview وبالتالي سوف يسبب بخطأ nil والخيار هذا بدل من ازالت الـ View1 سوف يقوم بإخفائه فقط الان عند التشغيل سوف تلاحظ بانه ينقلب من View1 الى View2 ومن ثم لن يعود الى View1 بل سوف يستمر بعرض View2 صورة توضيحية بالنتيجة الحالية : لماذا لم يعود الى الـ View الاول ؟ لأنه الكود الذي كتبناه يقوم على تحويل من View1 الى View2 بما يعني دائما سوف يظهر View2 باستطاعتنا تعديل الخطأ بإضافة Boolean var check = false اسفل @IBOutlet weak var View1: UIView! @IBOutlet weak var View2: UIView! ومن ثم نغير الكود الى هذا الشكل @IBAction func FlipButton(_ sender: UIButton) { check = !check let fromView = check ? View1 : View2 let toView = check ? View2 : View1 UIView.transition(from: fromView!, to: toView!, duration: 0.5, options: [.transitionFlipFromRight , .showHideTransitionViews], completion: nil) } ما الذي قمنا به هنا ؟ قمنا بتغير حالة الـ Boolean اذا كان false سوف يصبح true والعكس صحيح ملاحظة : علامة التعجب هنا معناها اعكس القيمة! ومن ثم اضفنا متغير باسم fromView واضفنا شرط اذا كان true اجعله View1 اذا كان false اجعله View2 واضفنا متغير اخر باسم toView واضفنا شرط اذا كان true اجعله View2 اذا كان false اجعله View1 ومن ثم اضفنا المتغيرات الجديدة الى from و to بداخل Function الـ UIView.transitio اعلم بانه البعض سوف يرى بأن طريقة كتابة الـ IF غريبة وغير منطقية بالنسبة له لذا لتبسيط الامور يمكن أيضا كتابتها بالطريقة التالية : @IBAction func FlipButton(_ sender: UIButton) { check = !check var formView : UIView if check == true { formView = View1 }else { formView = View2 } var toView : UIView if check == true { toView = View2 }else { toView = View1 } UIView.transition(from: formView, to: toView, duration: 0.5, options: [.transitionFlipFromRight , .showHideTransitionViews], completion: nil) } صورة توضيحية بالنتيجة :
  2. مجهود تشكر عليه ، جزاك الله كل خير
  3. البعض اخبرني بأنه يرغب بأمثله في الـ Animation وليس فقط شرح الأساسيات لذلك قررت اشرح مثال لعمل Menu مشابه للمدرج في تطبيق Telegram الفكرة باختصار هي إضافة زر بدلاَ من النص في الـ Navigation bar ومن ثم تغير موقع الـ View عند الضغط على الزر اذا لنبدأ في الشرح أولا نقوم بإضافة الـ Navigation Controller عن الطريق تطبيق اتباع الصورة التالية : ومن ثم نضيف View اسفل الـ Navigation bar بحجم عرض 375 وارتفاع 50 طبعا يعتمد على الابعاد الي انت تبغاها انا استخدمت هذه الابعاد نضيف القيود التالية : ومن ثم نضيف 3 ازرار احجام الايقونات كانت متفاوتة ، لكن اعتمدت على حجم 60 في 60 او اصغر بقليل بالنسبة لأحجام ايقونات الـ x2 الخاصة بحجم iPhone 7 و iPhone SE الان سوف نحتاج نستخدم الـ Stack View لأنه سوف يسهل علينا جعل الايقونات بمسافة متساوية ! قم بتحديد جميع الايقونات في أنٍ واحد ومن ثم ثم باتباع الصورة التالية : سوف تلاحظ تلاصق الايقونات مع بعض قوم بالذهاب الى خصائص الـ Stack View واجعل المسافة Spacing بـ 60 كما في الصورة التالية : واجعل الايقونات في منتصف الـ View الان قم بإضافة قيود الى الـ Stack View: لاحظ باني اضفت القيود بشكل يدوي لجعله الـ Stack View ياخد حجم الـ View الذي خلفه عند التعامل مع الـ Stack View يتوجب عليك استخدام القيود لتغير ابعاده لاحظ الفرق قبل القيود كان بالشكل التالي : بعد إضافة القيود اصبح بالشكل التالي : الان سوف تلاحظ بان الازرار والـ View متناسبة مع جميع احجام الاجهزة أيضا لاحظ بأنه عند استخدام الـ Stack View تكون لست بحاجة الى وضع قيود لكل زر على حدى ! كل ما عليك هو وضع القيود على نفس الـ Stack View فقط هذه هي قوة الـ Stack View سوف تحتاج الى استخدامه بكثره وخاصة عند عمل الـ Animation الان نعود للدرس قبل ان نبدأ تأكد بأن الـ Stack View بداخل الـ View الذي اضفناها لأننا سوف نضيف الـ View فقط الى ملف الاكواد ! يمكن التأكد بمشاهده الترتيب الـ Stack يكون بداخل الـ View الذي اضفناه سابقا كما في الصورة التالية: الان نقوم بإضافة الـ View الى ملف الاكواد ونجعله باسم Menu View وأيضا نضيف قيد الـ top الخاص بـ View ونجعله اسم menuTop يصبح ملف الاكواد بالشكل التالي import UIKit class ViewController: UIViewController { @IBOutlet weak var menuTop: NSLayoutConstraint! @IBOutlet weak var menuView: UIView! override func viewDidLoad() { super.viewDidLoad() } الان نقوم بإضافة الاكواد التالية في الـ ViewDidLoad override func viewDidLoad() { super.viewDidLoad() menuTop.constant = -65 let button = UIButton(type: .custom) button.frame = CGRect(x: 0, y: 0, width: 200, height: 40) button.setTitle("Menu", for: .normal) button.addTarget(self, action: #selector(self.menuButton), for: .touchUpInside) self.navigationItem.titleView = button menuView.layer.borderWidth = 0.5 menuView.layer.borderColor = UIColor.gray.cgColor } في البداية جعلنا الـ View مخفي بجعل القيمة -65 وبالتالي يصبح الـ View في الأعلى خلف الـ Navigation bar ومن ثم انشأنا زر من نوع .custom اعطينا ابعاد له وهيا عرض 200 وارتفاع 40 العرض تستطيع تغيره انا جعلته 200 لأنه في حال وجود زر في اليمين واليسار لن يغطي عليه اما الارتفاع فهو 40 ارتفاع الـ Navigation bar بدون شريط الساعة هو 44 لذلك حجم 40 يعتبر مناسب ومن ثم اضفنا عنوان للزر باسم Menu وبعدها اضفنا Target الي هو جعل الزر يستجيب للضغط طبعا اضفنا اسم للـ Function وهو menuButton سوف نضيفه لاحقا اخيراً قمنا بإضافة الزر بداخل العنوان الـ Navigation bar في الأسفل اضفنا الحدود حولينا الـ view وغيرنا لون الحدود الى اللون الرصاصي الان نضيف Function الزر باضافة التالي : func menuButton(button: UIButton) { } الان سوف نضيف الـ Aniamtion الفكره باختصار عند الضغط على الزر لأول مره نغير القيود الى 0 ليظهر الـ View لماذا 0 ؟ لانه عند اضافة الـ Navigation bar تصبح المسافه اسفله بقيمة 0 بالنسبة الى القيود فين حين بدون الـ Navigation bar تكون المسافة اسفل شريط الساعه هي الـ 0 وعند الضغط على الزر للمره الثانية نغير القيود الى -65 وبالتالي يختفي الـ View كيف نعلم بأمر الـ Menu هو ظاهر او مختفي ؟ الطريقة هي باستخدام متغير من نوع bool يكون true وتعني بانه مخفي ويصبح false اذا كان غير مخفي لذلك نضيف متغير في خارج الـ Function وأيضا بخارج الـ ViewDidLoad باسم var check = true ومن ثم نضيف التالي في الـ menuButton func menuButton(button: UIButton) { if check == true { UIView.animate(withDuration: 0.5, animations: { self.menuTop.constant = 0 self.view.layer.layoutIfNeeded() self.check = false }) }else { UIView.animate(withDuration: 0.5, animations: { self.menuTop.constant = -65 self.view.layer.layoutIfNeeded() self.check = true }) } } الذي قمنا به هو عمل if وقبنا بالتشيك اذا كان القيمة true فهو مخفي لذلك سوف نظهره واذا كان غير مخفي سوف نخفيه صورة بالنتيجة النهائية : اذا كنت تتسأل كيف غيرت لون الـ Navigation Bar فقم باتباع الخطوات التالية : يمكن تحميل الكود من هنا
  4. تحدثنا سابقا عن أنواع مختلفة للـ Animation وفي هذا الموضوع سوف نتحدث أيضا عن نوع اخر النوع هذا مختلف عن الانواع الأخرى فهو يتركز على موضوع التسلسل الزمني ذكرت في موضوع الاول من هذه السلسلة عن كيفية عمل Animation للمربع بحيث يتحرك بشكل حرف L اذا لاحظت في الكود السابق سوف تلاحظ عدة امور UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 300) }) { (true) in UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:-122, y: 300) }) } الاول : هو كثرة الاقواس وتداخلها ، في حال كان التحرك اكثر تعقيداً سوف يزداد تعقيد الكود وتكثر الاقواس وبالتالي يصعب تعديله الثاني : اذا اردت جعل الحركة متسلسلة بنفس التوقيت سوف ينبغي عليك تعديل توقيت كل Animation على حدى هذه هي فوائد النوع الجديد ازلت الاقواس المتداخله سهولة تعديل الكود او الوقت الكلي للـ Animation قبل أن نبدأ في الشرح العملي ، يتوجب عليا شرحه بصورة نظرية كيف يعمل ؟ عند استخدام UIView.animate فنحن نقوم بإضافة وقت زمني واذا استخدمنا Function معين سوف نستطيع اضافة أيضا زمن تأخير الـ Animation سوف يبدا وينتهي في الزمن المعين بصورة ثابته واذا اردنا ان نحرك في اتجاه مختلف فإننا نقوم بإضافة UIView.animate أخر بداخل اقواس الاستكمال التي نضعها أحيانا بقيمة true بما يعني في كل مره سوف نضع زمن مختلف ونعيد كتابة UIView.animate في هذا النوع الذي سوف نقوم به هو التالي : سوف نعطي زمن كلي محدد ولنقول مدة ثانية واحده ومن ثم ننفذ الـ Aniamtion بالنسبة المئوية ! ما المقصود بالنسبة المئوية؟ افرض بأن مدة الثانية الواحدة كامله منذ بدايتها الى نهاتيها تكون بنسبة 100% اذا سوف نضع Animation يبدا مباشرة وتكون بنسبة 0% الى نسبة 50% يتم التحريك بصورة عامودية ومن النسبة 50% الى 100% يتم التحريك بصورة افقيه صورة توضيحية : هذا هو فكرة النوع هذا باختصار وقت واحد ، يتم تجزئيه بالنسبة وعمل تحريك مختلف لذا ذكرت في بداية الموضوع بأن هذا النوع يعتمد على التسلسل الزمني فهو يعمل كالخط الزمني ! أخيرا يتوجب عليك ان تعلم بأن : النسبة لن تكون 100% ولكن سوف تكون 1 ، بمعنى نقوم بالقسمة على 100 اذا اردنا نسبة 25% سوف تكون 0.25 اذا اردنا نسبة 50 سوف تكون 0.5 وهكذا اذا استوعبت وفهمت الكلام السابق ، تكون فهمت فكرة هذا النوع ! اسم النوع الجديد هو UIView.animateKeyframes الان لجعلك تلاحظ الفرق الجوهري بين استخدام UIView.animate وبين استخدام UIView.animateKeyframes سوف اعطيك نفس المثال مرتين الاولى بالطريقة التقليدية باستخدام UIView.animate الثانية باستخدام UIView.animateKeyframes في هذا الدرس سوف نعود الى استخدام المربع الأزرق التي استخدمناه في الجزء الاول والثاني من هذه السلسلة ما الذي سوف نقوم به ؟ سوف نقوم بتحريك المربع الأزرق بشكل مستطيل يبدا من الزاوية السفلية اليسرى ويتحرك الى الزاوية السفلية اليمنى ومن ثم يتحك الى الزاوية العلوية اليمنى ومن ثم يتحرك الى الزاوية العلوية اليسرى واخيراً يعود الى الزاوية السفلية اليسرى قبل اذهب الى الـ Storyboard واجعله بالشكل التالي : ومن ثم اضيفهم الى ملف الاكواد الان سوف نبدأ الدرس باستخدام الطريقة القديمة الكود سوف يصبح بالشكل التالي : @IBAction func Animation(_ sender: Any) { UIView.animate(withDuration: 0.75, animations: { self.square.transform = CGAffineTransform(translationX: 225, y: 0) }) { (true) in UIView.animate(withDuration: 0.75, animations: { self.square.transform = CGAffineTransform(translationX: 225, y: -500) }, completion: { (true) in UIView.animate(withDuration: 0.75, animations: { self.square.transform = CGAffineTransform(translationX: -0, y: -500) }, completion: { (true) in UIView.animate(withDuration: 0.75, animations: { self.square.transform = .identity }, completion: { (true) in }) }) }) } } صورة توضيحية بالنتيجة : هل لاحظت المشكلة في الكود ؟ كثرة الاقواس وتداخلها يصعب من عملية تعديل الكود ماذا اذا اردت حذف احدى الـ Animation ؟ ستواجه صعوبة في إيجاد كود الـ Animation المراد حذفه ماذا اذا اردت تعديل الوقت الكلي ؟ سوف يتوجب عليك تعديل كود كل Animation على حده في المثال السابق استخدمنا وقت 0.75 لماذا ؟ لنصل الى توقيت كلي 3.0 فكل Animation سوف يستغرق 0.75 ثانية فقط الان للتنقل الى النوع الجديد @IBAction func Animation(_ sender: Any) { UIView.animateKeyframes(withDuration: 3.0, delay: 0.0, options: [], animations: { UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25, animations: { self.square.transform = CGAffineTransform(translationX: 225, y: 0) }) UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.5, animations: { self.square.transform = CGAffineTransform(translationX: 225, y: -500) }) UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.75, animations: { self.square.transform = CGAffineTransform(translationX: -0, y: -500) }) UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 1.0, animations: { self.square.transform = .identity }) }, completion: nil) } النتيجة الذي وصلنا اليها : ما الجديد ؟ أولا اضفنا animateKeyframes ومن ثم اضفنا التوقيت الكلي وهو 3 ثواني ولم نضيف تأخير لأننا نريد ان يبدا فوراُ فجعلنا القيمة 0.0 في الـ options لم نضيف شيء لذا وضعنا اقواس اريه فاضية مع العلم أنواع الـ options مختلفة تماما عن الانواع السابقة الموجودة في UIView.animate ! (لن نتطرق لها في هذا الموضوع) والان لاحظ كل Animation ينضاف لحاله بداخل اقواس animateKeyframes ويطلق عليه UIView.addKeyframe يتكون من امرين وهي withRelativeStartTime والتي تعبر بداية الـ Animation كما ذكرنا في بداية الموضوع تعتبر نسبة في اول Animation وضعنا قيمة 0.0 ليبدا في البداية بالنسبة الى relativeDuration يعتبر وقت انتهاء الـ Animation في اول Animation وضعنا نسبة 0.25 والتي تساوي نسبة 25% لجعل الامر اكثر وضوحاً اذا نظرنا في animateKeyframes وضعنا وقت كلي هو 3 ثواني فاذا قمنا بعملية حسابية بسيطة 3 ضرب 25 قسمة 100 النتيجة سوف تكون 0.75 ثانية اذاً اول Animation سوف يستغرق 0.75 ثانية فقط والامر ينطبق مع البقية في الـ Animation الثاني وضعنا قيمة withRelativeStartTime بـ 0.25 وقيمة الـ relativeDuration بـ 0.50 اذا نقصنا القيمتين من بعض سوف تكون النسبة 0.25 وذا اتبعنا نفس الحسبة السابقة سوف تكون النتيجة 0.75 ثانية من الشرح السابق اتضح لنا فائدة الـ UIView.animateKeyframes بما يعني : اذا اردنا تغير الوقت الكلي فقط نقوم بتغير القيمة الموجودة في UIView.animateKeyframes اذا اردنا تغير مدة Animation معين ، نقوم بتغير نسبة الـ Animation فقط والاهم من ذا كله الكود اصبح نضيف وسهل القراءة والتعديل !
  5. تحدثنا سابقا عن أنواع مختلفة للـ Animation وفي هذا الموضوع سوف نتحدث أيضا عن نوع اخر النوع هذا مختلف عن الانواع الأخرى فهو يتركز على موضوع التسلسل الزمني ذكرت في موضوع الاول من هذه السلسلة عن كيفية عمل Animation للمربع بحيث يتحرك بشكل حرف L اذا لاحظت في الكود السابق سوف تلاحظ عدة امور UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 300) }) { (true) in UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:-122, y: 300) }) } الاول : هو كثرة الاقواس وتداخلها ، في حال كان التحرك اكثر تعقيداً سوف يزداد تعقيد الكود وتكثر الاقواس وبالتالي يصعب تعديله الثاني : اذا اردت جعل الحركة متسلسلة بنفس التوقيت سوف ينبغي عليك تعديل توقيت كل Animation على حدى هذه هي فوائد النوع الجديد ازلت الاقواس المتداخله سهولة تعديل الكود او الوقت الكلي للـ Animation قبل أن نبدأ في الشرح العملي ، يتوجب عليا شرحه بصورة نظرية كيف يعمل ؟ عند استخدام UIView.animate فنحن نقوم بإضافة وقت زمني واذا استخدمنا Function معين سوف نستطيع اضافة أيضا زمن تأخير الـ Animation سوف يبدا وينتهي في الزمن المعين بصورة ثابته واذا اردنا ان نحرك في اتجاه مختلف فإننا نقوم بإضافة UIView.animate أخر بداخل اقواس الاستكمال التي نضعها أحيانا بقيمة true بما يعني في كل مره سوف نضع زمن مختلف ونعيد كتابة UIView.animate في هذا النوع الذي سوف نقوم به هو التالي : سوف نعطي زمن كلي محدد ولنقول مدة ثانية واحده ومن ثم ننفذ الـ Aniamtion بالنسبة المئوية ! ما المقصود بالنسبة المئوية؟ افرض بأن مدة الثانية الواحدة كامله منذ بدايتها الى نهاتيها تكون بنسبة 100% اذا سوف نضع Animation يبدا مباشرة وتكون بنسبة 0% الى نسبة 50% يتم التحريك بصورة عامودية ومن النسبة 50% الى 100% يتم التحريك بصورة افقيه صورة توضيحية : هذا هو فكرة النوع هذا باختصار وقت واحد ، يتم تجزئيه بالنسبة وعمل تحريك مختلف لذا ذكرت في بداية الموضوع بأن هذا النوع يعتمد على التسلسل الزمني فهو يعمل كالخط الزمني ! أخيرا يتوجب عليك ان تعلم بأن : النسبة لن تكون 100% ولكن سوف تكون 1 ، بمعنى نقوم بالقسمة على 100 اذا اردنا نسبة 25% سوف تكون 0.25 اذا اردنا نسبة 50 سوف تكون 0.5 وهكذا اذا استوعبت وفهمت الكلام السابق ، تكون فهمت فكرة هذا النوع ! اسم النوع الجديد هو UIView.animateKeyframes الان لجعلك تلاحظ الفرق الجوهري بين استخدام UIView.animate وبين استخدام UIView.animateKeyframes سوف اعطيك نفس المثال مرتين الاولى بالطريقة التقليدية باستخدام UIView.animate الثانية باستخدام UIView.animateKeyframes في هذا الدرس سوف نعود الى استخدام المربع الأزرق التي استخدمناه في الجزء الاول والثاني من هذه السلسلة ما الذي سوف نقوم به ؟ سوف نقوم بتحريك المربع الأزرق بشكل مستطيل يبدا من الزاوية السفلية اليسرى ويتحرك الى الزاوية السفلية اليمنى ومن ثم يتحك الى الزاوية العلوية اليمنى ومن ثم يتحرك الى الزاوية العلوية اليسرى واخيراً يعود الى الزاوية السفلية اليسرى قبل اذهب الى الـ Storyboard واجعله بالشكل التالي : ومن ثم اضيفهم الى ملف الاكواد الان سوف نبدأ الدرس باستخدام الطريقة القديمة الكود سوف يصبح بالشكل التالي : @IBAction func Animation(_ sender: Any) { UIView.animate(withDuration: 0.75, animations: { self.square.transform = CGAffineTransform(translationX: 225, y: 0) }) { (true) in UIView.animate(withDuration: 0.75, animations: { self.square.transform = CGAffineTransform(translationX: 225, y: -500) }, completion: { (true) in UIView.animate(withDuration: 0.75, animations: { self.square.transform = CGAffineTransform(translationX: -0, y: -500) }, completion: { (true) in UIView.animate(withDuration: 0.75, animations: { self.square.transform = .identity }, completion: { (true) in }) }) }) } } صورة توضيحية بالنتيجة : هل لاحظت المشكلة في الكود ؟ كثرة الاقواس وتداخلها يصعب من عملية تعديل الكود ماذا اذا اردت حذف احدى الـ Animation ؟ ستواجه صعوبة في إيجاد كود الـ Animation المراد حذفه ماذا اذا اردت تعديل الوقت الكلي ؟ سوف يتوجب عليك تعديل كود كل Animation على حده في المثال السابق استخدمنا وقت 0.75 لماذا ؟ لنصل الى توقيت كلي 3.0 فكل Animation سوف يستغرق 0.75 ثانية فقط الان للتنقل الى النوع الجديد @IBAction func Animation(_ sender: Any) { UIView.animateKeyframes(withDuration: 3.0, delay: 0.0, options: [], animations: { UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25, animations: { self.square.transform = CGAffineTransform(translationX: 225, y: 0) }) UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.5, animations: { self.square.transform = CGAffineTransform(translationX: 225, y: -500) }) UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.75, animations: { self.square.transform = CGAffineTransform(translationX: -0, y: -500) }) UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 1.0, animations: { self.square.transform = .identity }) }, completion: nil) } النتيجة الذي وصلنا اليها : ما الجديد ؟ أولا اضفنا animateKeyframes ومن ثم اضفنا التوقيت الكلي وهو 3 ثواني ولم نضيف تأخير لأننا نريد ان يبدا فوراُ فجعلنا القيمة 0.0 في الـ options لم نضيف شيء لذا وضعنا اقواس اريه فاضية مع العلم أنواع الـ options مختلفة تماما عن الانواع السابقة الموجودة في UIView.animate ! (لن نتطرق لها في هذا الموضوع) والان لاحظ كل Animation ينضاف لحاله بداخل اقواس animateKeyframes ويطلق عليه UIView.addKeyframe يتكون من امرين وهي withRelativeStartTime والتي تعبر بداية الـ Animation كما ذكرنا في بداية الموضوع تعتبر نسبة في اول Animation وضعنا قيمة 0.0 ليبدا في البداية بالنسبة الى relativeDuration يعتبر وقت انتهاء الـ Animation في اول Animation وضعنا نسبة 0.25 والتي تساوي نسبة 25% لجعل الامر اكثر وضوحاً اذا نظرنا في animateKeyframes وضعنا وقت كلي هو 3 ثواني فاذا قمنا بعملية حسابية بسيطة 3 ضرب 25 قسمة 100 النتيجة سوف تكون 0.75 ثانية اذاً اول Animation سوف يستغرق 0.75 ثانية فقط والامر ينطبق مع البقية في الـ Animation الثاني وضعنا قيمة withRelativeStartTime بـ 0.25 وقيمة الـ relativeDuration بـ 0.50 اذا نقصنا القيمتين من بعض سوف تكون النسبة 0.25 وذا اتبعنا نفس الحسبة السابقة سوف تكون النتيجة 0.75 ثانية من الشرح السابق اتضح لنا فائدة الـ UIView.animateKeyframes بما يعني : اذا اردنا تغير الوقت الكلي فقط نقوم بتغير القيمة الموجودة في UIView.animateKeyframes اذا اردنا تغير مدة Animation معين ، نقوم بتغير نسبة الـ Animation فقط والاهم من ذا كله الكود اصبح نضيف وسهل القراءة والتعديل !
  6. البعض اخبرني بأنه يرغب بأمثله في الـ Animation وليس فقط شرح الأساسيات لذلك قررت اشرح مثال لعمل Menu مشابه للمدرج في تطبيق Telegram الفكرة باختصار هي إضافة زر بدلاَ من النص في الـ Navigation bar ومن ثم تغير موقع الـ View عند الضغط على الزر اذا لنبدأ في الشرح أولا نقوم بإضافة الـ Navigation Controller عن الطريق تطبيق اتباع الصورة التالية : ومن ثم نضيف View اسفل الـ Navigation bar بحجم عرض 375 وارتفاع 50 طبعا يعتمد على الابعاد الي انت تبغاها انا استخدمت هذه الابعاد نضيف القيود التالية : ومن ثم نضيف 3 ازرار احجام الايقونات كانت متفاوتة ، لكن اعتمدت على حجم 60 في 60 او اصغر بقليل بالنسبة لأحجام ايقونات الـ x2 الخاصة بحجم iPhone 7 و iPhone SE الان سوف نحتاج نستخدم الـ Stack View لأنه سوف يسهل علينا جعل الايقونات بمسافة متساوية ! قم بتحديد جميع الايقونات في أنٍ واحد ومن ثم ثم باتباع الصورة التالية : سوف تلاحظ تلاصق الايقونات مع بعض قوم بالذهاب الى خصائص الـ Stack View واجعل المسافة Spacing بـ 60 كما في الصورة التالية : واجعل الايقونات في منتصف الـ View الان قم بإضافة قيود الى الـ Stack View: لاحظ باني اضفت القيود بشكل يدوي لجعله الـ Stack View ياخد حجم الـ View الذي خلفه عند التعامل مع الـ Stack View يتوجب عليك استخدام القيود لتغير ابعاده لاحظ الفرق قبل القيود كان بالشكل التالي : بعد إضافة القيود اصبح بالشكل التالي : الان سوف تلاحظ بان الازرار والـ View متناسبة مع جميع احجام الاجهزة أيضا لاحظ بأنه عند استخدام الـ Stack View تكون لست بحاجة الى وضع قيود لكل زر على حدى ! كل ما عليك هو وضع القيود على نفس الـ Stack View فقط هذه هي قوة الـ Stack View سوف تحتاج الى استخدامه بكثره وخاصة عند عمل الـ Animation الان نعود للدرس قبل ان نبدأ تأكد بأن الـ Stack View بداخل الـ View الذي اضفناها لأننا سوف نضيف الـ View فقط الى ملف الاكواد ! يمكن التأكد بمشاهده الترتيب الـ Stack يكون بداخل الـ View الذي اضفناه سابقا كما في الصورة التالية: الان نقوم بإضافة الـ View الى ملف الاكواد ونجعله باسم Menu View وأيضا نضيف قيد الـ top الخاص بـ View ونجعله اسم menuTop يصبح ملف الاكواد بالشكل التالي import UIKit class ViewController: UIViewController { @IBOutlet weak var menuTop: NSLayoutConstraint! @IBOutlet weak var menuView: UIView! override func viewDidLoad() { super.viewDidLoad() } الان نقوم بإضافة الاكواد التالية في الـ ViewDidLoad override func viewDidLoad() { super.viewDidLoad() menuTop.constant = -65 let button = UIButton(type: .custom) button.frame = CGRect(x: 0, y: 0, width: 200, height: 40) button.setTitle("Menu", for: .normal) button.addTarget(self, action: #selector(self.menuButton), for: .touchUpInside) self.navigationItem.titleView = button menuView.layer.borderWidth = 0.5 menuView.layer.borderColor = UIColor.gray.cgColor } في البداية جعلنا الـ View مخفي بجعل القيمة -65 وبالتالي يصبح الـ View في الأعلى خلف الـ Navigation bar ومن ثم انشأنا زر من نوع .custom اعطينا ابعاد له وهيا عرض 200 وارتفاع 40 العرض تستطيع تغيره انا جعلته 200 لأنه في حال وجود زر في اليمين واليسار لن يغطي عليه اما الارتفاع فهو 40 ارتفاع الـ Navigation bar بدون شريط الساعة هو 44 لذلك حجم 40 يعتبر مناسب ومن ثم اضفنا عنوان للزر باسم Menu وبعدها اضفنا Target الي هو جعل الزر يستجيب للضغط طبعا اضفنا اسم للـ Function وهو menuButton سوف نضيفه لاحقا اخيراً قمنا بإضافة الزر بداخل العنوان الـ Navigation bar في الأسفل اضفنا الحدود حولينا الـ view وغيرنا لون الحدود الى اللون الرصاصي الان نضيف Function الزر باضافة التالي : func menuButton(button: UIButton) { } الان سوف نضيف الـ Aniamtion الفكره باختصار عند الضغط على الزر لأول مره نغير القيود الى 0 ليظهر الـ View لماذا 0 ؟ لانه عند اضافة الـ Navigation bar تصبح المسافه اسفله بقيمة 0 بالنسبة الى القيود فين حين بدون الـ Navigation bar تكون المسافة اسفل شريط الساعه هي الـ 0 وعند الضغط على الزر للمره الثانية نغير القيود الى -65 وبالتالي يختفي الـ View كيف نعلم بأمر الـ Menu هو ظاهر او مختفي ؟ الطريقة هي باستخدام متغير من نوع bool يكون true وتعني بانه مخفي ويصبح false اذا كان غير مخفي لذلك نضيف متغير في خارج الـ Function وأيضا بخارج الـ ViewDidLoad باسم var check = true ومن ثم نضيف التالي في الـ menuButton func menuButton(button: UIButton) { if check == true { UIView.animate(withDuration: 0.5, animations: { self.menuTop.constant = 0 self.view.layer.layoutIfNeeded() self.check = false }) }else { UIView.animate(withDuration: 0.5, animations: { self.menuTop.constant = -65 self.view.layer.layoutIfNeeded() self.check = true }) } } الذي قمنا به هو عمل if وقبنا بالتشيك اذا كان القيمة true فهو مخفي لذلك سوف نظهره واذا كان غير مخفي سوف نخفيه صورة بالنتيجة النهائية : اذا كنت تتسأل كيف غيرت لون الـ Navigation Bar فقم باتباع الخطوات التالية : يمكن تحميل الكود من هنا
  7. تعلمنا في ما سبق عن نوع من الـ Animation وهو الـ UIView.animate والذي يعتبر الأكثر استخداما واليوم سوف نتعلم عن نوع مختلف وهو UIView.transition قبل ان نبدأ في الدرس ، ما الفرق بينهما ؟ باختصار UIView.animate مسؤول عن ملكية الـ UIView من عمل تحريك وتكبير والتفاف وغيرها في حين UIView.transition مسؤول عن إضافة وحذف الـ View تستطيع عند إضافة الـ View كـ Subview انك تعمل Animation في لحظة اضافته او حذفه اعلم الكلام غير مفهوم ! مع التطبيق سوف تتضح الصورة . لتسهيل الامور لن اتطرق الى موضوع إضافة وحذف الـ SubView عن طريق الاكواد ولكن سوف اشرحها بطريقة اكثر بساطة . اذا لنبدأ الدرس . نقوم بإضافة Label ونجعل لون النص باللون الأبيض ولون الخلفية باللون الأسود ومن ثم نضيف زر كما في الصورة التالية : نقوم بإضافتهم الى ملف اكواد التطبيق ونجعل اسم الـ Label بـ Label واسم الـ Function بـ Transition @IBOutlet weak var Label: UILabel! @IBAction func Transition(_ sender: Any) { } الان في viewDidLoad نضيف التالي : Label.isHidden = true لماذا ؟ ذكرنا في السابق بأن الـ UIView.transition يعمل اثناء إضافة وحذف الـ View لذا في الكود السابق ما نقوم به هو حذفه من الـ View بجعله غير ظاهر ! في الدروس السابقة استخدمنا .alpha فما الفرق ؟ الـ .alpha هي الشفافية بمعنى 1 يعتبر لا توجد شفافية في حين 0.5 تعني وجود شفافية و 0 يعني انعدام الشفافية وهي من ملكيات الـ UIView.animate بصورة أخرى من المكيات التي يمكن عمل لها animate لكن الـ isHidden يزيل الـ View وليست من الملكيات التي تستطيع عمل لها animate فاذا استخدمتها بداخل اقواس UIView.animate لن يظهر أي Animation ! بصورة مختصرة : استخدام UIView.transition مع الملكيات التي لا يمكن عمل لها Animation نعود للدرس الان نقوم بعمل التالي بداخل اقواس Transition UIView.transition(with: Label, duration: 0.5, options: .transitionCurlDown, animations: { self.Label.isHidden = false }, completion: nil) كما تلاحظ بصورة عامة فهو مشابه للـ UIView.animate الاختلاف with وتعني ماهو الـ view الذي تريد عمل له الـ transition تكتب نفس اسم الـ View وهنا نحن استخدمنا Label فنقوم بكتابة اسم الـ Label الـ options هناك أنواع مختلف سوف اذكرها بعد قليل ما اريد توضيحه هنا الانواع التي ذكرتها في موضوعي السابق .curveEaseIn واخواتها يمكنك دمجها مع الانواع الخاصة بالـ transition بعمل مربع الاريه [] والفصل بينهم بعلامة فاصلة ماقمنا بفعله داخل الاقواس هو فقط حولنا الحالة من مخفي الى ظاهر بالتغير من true الى false أنواع الـ options : transitionCurlDown : وهو النوع الذي استخدمناه في الكود السابق transitionCurlUp : هيا عملية معاكسه للعملية السابقة لم استطيع عمل صورة متحركة توضحها transitionFlipFromTop: :transitionFlipFromBottom :transitionFlipFromRight :transitionFlipFromLeft transitionCrossDissolve: هناك نوع أخير وهو .showHideTransitionViews سيتضح فائدته في الـ Function الاخر للـ transition من الامور التي اتضحت من أنواع الـ Options السابقة بأنك قد ترغب باستخدام الـ transition لعمل تأثير معين لن تستطيع عمله باستخدام الـ UIView.animate ولكن من الامور الأخرى هو عمل تأثير انتقالي وسيكون المثال التالي توضيحا للطريقة قبل العوده لاستكمال الدرس هنا تلميحه بسيطة وهي هل تريد مشاهدة التأثير ببطيء ؟ عند تشغيل التطبيق على المحاكي اذهب الى خانة Debug واختار خيار Slow Animations ومن ثم اضغط على الزر وسظهر الـ Animation بصورة بطيئة (سلو موشن) كما في الصورة التالية : مثال : نعود للدرس الان سوف ننتقل الى نوع أخرى من أنواع الـ UIView.transition UIView.transition(from: , to: , duration: , options: , completion: nil) كما تلاحظ في الكود السابق بأنه يطلب نوعين من الـ UIView ، وهذا النوع هو نوع انتقالي بحيث يعمل عملية انتقالية بين الـ View الاول الى الـ View الثاني بحيث يخفي الاول ويظهر الثاني ! دعونا نبدأ في المثال أولا : نذهب الى الـ Storyboard ونضيف التالي : نضيف UIView ونجعله بحجم مستطيل صغير ومن ثم بداخله نضيف UIView اخر ونجعله بنفس حجم الـ UIView السابق ونغير لونه الى الاخضر ومن ثم نضيف Label اذا اردت وأيضا نضيف Button ، نحذف النص الذي بداخله ونجعله بحجم الـ UIView كما في الصورة التالية : ومن ثم نقوم بنسخ الـ UIView الذي قمنا بعمله في الخطوة السابقة ونغير النص ولون الـ UIView الى الازرق كما في الصورة التالية : الان اصبح لدينا ثلاثة من الـ UIView اثنين بداخل UIView واحد وفوق كل UIView زر بما يعني ٣ من الـ UIView و ٢ من الـ Button قد تتسأل لماذا اضفنا UIView وبداخله (فوقه) اضفنا اثنين من الـ UIView بدلا من إضافة اثنين من الـ UIView مباشرةً الى الـ ViewController؟ السبب لأنه عند عمل الـ transition سنقلب الـSuperview الذي سيكون الـ View الأساسي بمعنى سوف تنقلب كامل الصفحة !! لكن عند إضافة UIView وجعلنا اثنين من الـ UIView بداخله الذي سوف ينقلب هو الـ UIView الي اضفناه لأنه اصبح هو الـSuperview بالنسبة لهم بما يعني لن تنقلب الصفحة كامله ! بما يعطي ايحاء بتأثير انقلاب البطاقة ! ملاحظة : في الـ StoryBoard من يكون في اخر التسلسل يكون هو الاول (الظاهر) فالان اصبح الـ View 2 هو الاول والـ View 1 هو الثاني يمكنك تغيير المسميات او فقط تقوم بسحب الـ View 2 وتجعله فوق View 1 بالشكل التالي : ملاحظة ٢ : عندما يكون هناك نوعين مختلفة فوق بعض (في المثال هذا هناك اثنين من الـ View) ، سوف يكون من الصعب مشاهدة الـ View الذي في الخلف ، اذا طبقت الملاحظة الاولى بعد عمل القيود سوف تواجه مشاكل مع القيود ! اذا ما الحل ؟ كل ما عليك فعله هو الضغط على الـ View الذي في المقدمة (الذي يكون الأخير في الترتيب) ومن ثم إزالة علامة الصح من Installed وعندها ستخفي وسيظهر الـ View الذي كان في الخلف وسيسهل عليك تعديله بعد الانتهاء من التعديلات قم بتفعيل الصح مره أخرى ! صورة توضيحية : الان نقوم بإضافتهم الى ملف الاكواد سنقوم بإضافة فقط الـ UIView الذي باللون الأزرق والاخضر وأيضا سوف نضيف Action للزر ونربط الزرين بهذا الـ Action بصيغة أخرى سوف يصبح لدينا Function واحد وزرين مرتبطين به ! لأننا نريد جعل البطاقة تنقلب مرتين عند اللمس عوضا عن عمل اثنين من الـ Function سوف نعمل على Function واحد فقط كما في الصورة التالية : وبالتالي ملف الاكواد سوف يصبح بالشكل التالي : import UIKit class ViewController: UIViewController { @IBOutlet weak var View1: UIView! @IBOutlet weak var View2: UIView! override func viewDidLoad() { super.viewDidLoad() } @IBAction func FlipButton(_ sender: UIButton) { } الان بداخل الـ FlipButton نقوم بكتابة التالي : @IBAction func FlipButton(_ sender: UIButton) { UIView.transition(from: self.View1, to: self.View2, duration: 0.5, options: [.transitionFlipFromRight , .showHideTransitionViews], completion: nil) } ما قمنا به هو جعلنا الـ View1 ينقلب الى View2 لاحظ اننا استخدمنا الـ showHideTransitionViews لماذا ؟ لانه بعد عملية الانتقال سوف يتم حذف الـ View1 من الـ Suberview وبالتالي سوف يسبب بخطأ nil والخيار هذا بدل من ازالت الـ View1 سوف يقوم بإخفائه فقط الان عند التشغيل سوف تلاحظ بانه ينقلب من View1 الى View2 ومن ثم لن يعود الى View1 بل سوف يستمر بعرض View2 صورة توضيحية بالنتيجة الحالية : لماذا لم يعود الى الـ View الاول ؟ لأنه الكود الذي كتبناه يقوم على تحويل من View1 الى View2 بما يعني دائما سوف يظهر View2 باستطاعتنا تعديل الخطأ بإضافة Boolean var check = false اسفل @IBOutlet weak var View1: UIView! @IBOutlet weak var View2: UIView! ومن ثم نغير الكود الى هذا الشكل @IBAction func FlipButton(_ sender: UIButton) { check = !check let fromView = check ? View1 : View2 let toView = check ? View2 : View1 UIView.transition(from: fromView!, to: toView!, duration: 0.5, options: [.transitionFlipFromRight , .showHideTransitionViews], completion: nil) } ما الذي قمنا به هنا ؟ قمنا بتغير حالة الـ Boolean اذا كان false سوف يصبح true والعكس صحيح ملاحظة : علامة التعجب هنا معناها اعكس القيمة! ومن ثم اضفنا متغير باسم fromView واضفنا شرط اذا كان true اجعله View1 اذا كان false اجعله View2 واضفنا متغير اخر باسم toView واضفنا شرط اذا كان true اجعله View2 اذا كان false اجعله View1 ومن ثم اضفنا المتغيرات الجديدة الى from و to بداخل Function الـ UIView.transitio اعلم بانه البعض سوف يرى بأن طريقة كتابة الـ IF غريبة وغير منطقية بالنسبة له لذا لتبسيط الامور يمكن أيضا كتابتها بالطريقة التالية : @IBAction func FlipButton(_ sender: UIButton) { check = !check var formView : UIView if check == true { formView = View1 }else { formView = View2 } var toView : UIView if check == true { toView = View2 }else { toView = View1 } UIView.transition(from: formView, to: toView, duration: 0.5, options: [.transitionFlipFromRight , .showHideTransitionViews], completion: nil) } صورة توضيحية بالنتيجة :
  8. تعلمنا في الجزء الاول من اساسيات الـ Animation عن نوعين من الـ Function احداهم مسؤول عن عمل الـ Animation مباشرةً والأخر في حالت اردت تنفيذ شرط عند اكتمال الـ Animation واستخدمناه عند تحريك المربع بشكل حرف L لكن هناك أنواع أخرى وهذا ما سوف نتعلمه في هذا الدرس عند كتابة UiView.animate سوف تلاحظ وجود 4 أنواع نوعين تحدثنا عنهم سابقا ، ونوعين لم نتحدث عنهم في هذه الفقرة اختار هذا النوع شاهد الصورة ولاحظ النص المكتوب في الأسفل : الشكل النهائي سوف يكون بالشكل التالي : UIView.animate(withDuration: 1.0, delay: 0.0, options:[], animations: { }, completion: nil) معاني الـ Parameter : withDuration : تحدثنا عنها في الدرس الماضي ومعناها كم الفترة المرادة لعمل الـ Animation بالثانية الواحده ، ربما لاحظت بأني اضعها دائما "ثانية واحده" ولكن غالبا راح تحتاج أن تضعها من 0.5 الى 0.3 ، لانها تعتبر افضل مدة لأغلب الـ Animation delay : هذا الخيار جديد ، ومعناه التأخير ، كم الفترة التي تريد فيها الانتظار قبل ان يبدا الـ Animation ؟ اجعله على 0.0 في حال اردت ان يبدا مباشرةً بدون تأخير options : هذا أيضا من ضمن الخيارات الجديدة وسيكون اغلب شرحنا يتركز عليه ، لذا سيتم شرحه في الفقرة التالية في الوقت الراهن ، الذي يفترض معرفته هو عند وضع علامة مربع الاريه [] تعني استخدم النوع الافتراضي وأيضا باستطاعتك وضع عدة options وذلك بكتابة علامة الاريه [] ووضع عدة أنواع والفصل فيما بينهم بعلامة الفاصلة , completion : تم ذكره سابقا ومعناه عند اكتمال الـ Animation ماذا تريد ان تفعل ؟ ، وضعنا هنا nil لأننا لا نريد تنفيذ امرا بعد انتهاء الـ Animation . أنواع الـ Options : هناك عدة أنواع للـ Animation سوف نتطرق الى اهمها هناك ٤ انواع رئيسية مسؤوله عن سرعة الـ Animation ، وهيا : curveLinear : ويعني يبدا الـ Animation بنفس السرعة وينتهي بنفس السرعة (لا يحدث اختلاف في السرعة) curveEaseIn : ويعني يبدا الـ Animation بسرعة بطيئة ومن ثم يزداد سرعته (يبدا بطئي وينتهي بسرعة) curveEaseOut : ويعني يبدا الـ Animation بسرعة وينتهي ببطئ (يبدا بسرعة وينتهي ببطئ) curveEaseInOut : هذا النوع هو الافتراضي، ويعني يبدا الـ Animation ببطئ ومن ثم يسرع وينتهي ببطئ (يبدا وينتهي ببطئ) كيف يتم كتابة الـ Options ؟ قم بكتابة علامة الـ . ومن ثم اكتب حرف c لتقوم بفلترة النتائج ولتظهر الانواع الأربعة السابقة ومن ثم اختار النوع الذي تريده مثال لطريقة الكتابة : UIView.animate(withDuration: 1.0, delay: 0.0, options: .curveEaseIn, animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) حقيقة واجهة صعوبة لتوضيح الفروقات فيما بينهم عند عمل صورة متحركة لكل نوع على حدى ! لذا وجدت أفضل طريقة هي باضافتهم جميعا في صورة متحركة واحده ! سوف تلاحظ الفروقات بوضوح مع هذه الصورة : الان سوف نتطرق لنوعين اخرين : autorevers : باختصار هو عملية تكرار "لمره واحده" بحيث يتم عمل الـ Animation وعند الانتهاء اعادته بالعكس "مره واحده" ومن ثم يتوقف UIView.animate(withDuration: 1.0, delay: 0.0, options: .autoreverse, animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) لاحظ الصورة المتحركة ، ذهابا ومن ثم عودة ومن ثم التوقف في الأسفل : repeat: هو التكرار ، يمكن تشبيه بأنه كالصورة المتحركة عندما ينتهي يعيد من البداية وهكذا (سوف تلاحظ بانه تمت عملية قطع فجائية للـ Animation) UIView.animate(withDuration: 1.0, delay: 0.0, options: .repeat, animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) صورة توضيحية : ماذا اذا ثم دمج النوعين السابقين مع بعض ؟ كما ذكرت سابقا يمكن دمج عدة انواع مع بعض وهنا لاحظ عندما ندمج الـ autorevers مع الـ repeat النتيجه سوف تكون عملية تكرار بدون توقف ، بمعنى تكون Perfect loop UIView.animate(withDuration: 1.0, delay: 0.0, options: [.autoreverse , .repeat], animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) صورة توضيحية : اخر خيار هو allowUserInteraction : كما هو واضح من اسمه ، السماح بتفاعل المستخدم مع الـ view اثناء الـ Animation ! لجعل الامر واضحاً لك ، قمت بإضافة زر في داخل المربع وجعلت ابعداه بكامل المربع ومن ثم قم بتجربة بدون استخدام خيار الـ allowUserInteraction بكتابة الكود التالي : UIView.animate(withDuration: 3.0, delay: 0.0, options: [.autoreverse], animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) شاهد الصورة التالية : ولاحظ باني لم استطع الضغط على الزر اثناء الـ Animation ولكن استطعت فقط الضغط على الزر قبل بداية الـ Animation وبعد انتهائه بعد إضافة خيار allowUserInteraction بكتابة الكود بالشكل التالي : UIView.animate(withDuration: 3.0, delay: 0.0, options: [.autoreverse], animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) شاهد الصورة التالية : سوف تلاحظ باني استطعت الضغط على الزر اثناء الـ Animation الفقرة الثانية : الان سوف نتعلم نوع اخر للـ Uiview.animate النوع هذا يطلق عليه Spring Animation معنى الـ Spring هو النابض بمعنى ارتداد الـ Animation سوف تتضح فكرة الـ Spring Animation مع هذه الصورة : كيف يستخدم ؟ بداية نقوم باختيار هذا الـ Function ، كما في الصورة التالية : الكود النهائي سوف يكون بالشكل التالي : UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.0, options:.curveEaseOut, animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 400) }, completion: nil) لاحظ هناك خيارين جديدة وهما : usingSpringWithDamping : ويعني هذا الخيار بالتذبذب (مقدار الاهتزاز) كل ما كان الرقم قريب من الصفر كان مقدار الاهتزاز اعلى ، بما يعني القيمة تكون اقل من 1 واكبر من 0 وضعنا هنا قيمة 0.3 يمكنك التجربة لاستيعاب الفكرة initialSpringVelocity : هذا الخيار المقصود به هو سرعة الاندفاع في البداية ، آبل تنصح بجعله بقيمة 0 يمكنك تغير قيمته أيضا من بين قيمة اقل من 1 واكبر من 0 صورة توضيحية للنتيجة : مثال لأحدى الاستخدامات للـ Spring Animation لنفترض تريد عمل رسالة تنبيه للمستخدم بإظهارها بشكل الـ popup باستطاعتك عملها بالطريقة التالية : بداية سوف نغير لون وحجم المربع الأزرق ونضعه في المنتصف ونضيف label وزر لجعله كالـ popup مثل الصورة التالية : في الـ Viewdidload() نضيف التالي : override func viewDidLoad() { super.viewDidLoad() squareView.layer.cornerRadius = 20 squareView.layer.borderWidth = 1 squareView.layer.borderColor = UIColor.lightGray.cgColor squareView.alpha = 0.0 } الذي قمنا بكتابته : اضفنا حواف ناعمه في اطرف المربع وجعلنا قيمتها 20 اضفنا حدود بحجم 1 بكسل جعلنا لون الحدود باللون الرصاصي الفاتح اخفينا الـ View عند بداية تشغيل البرنامج في الـ AnimationButton نضيف التالي : @IBAction func AnimationButton(_ sender: Any) { squareView.alpha = 1.0 self.squareView.transform = CGAffineTransform(scaleX: 0.3, y: 1) UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.0, options:.curveEaseOut, animations: { self.squareView.transform = .identity }, completion: nil) } الذي قمنا بكتابته التالي : اظهرنا الـ View بجعل قيمة الـ alpha تساوي 1 قبنا بتصغير قيمة x ولم نغير قيمة y ، لماذا في خارج الـ uiview.animate ؟ لأننا نريد أن نجعل الـ View يرجع الى حجمه الطبيعي اثناء الـ animation اضفنا سطر واحد يقوم بإرجاع الـ View الى حجمه الطبيعية ، ولأننا استخدمنا الـ Spring Animation .. سيظهر الـ View بشكل جميل شاهد الصورة التالية :
  9. تعلمنا في الجزء الاول من اساسيات الـ Animation عن نوعين من الـ Function احداهم مسؤول عن عمل الـ Animation مباشرةً والأخر في حالت اردت تنفيذ شرط عند اكتمال الـ Animation واستخدمناه عند تحريك المربع بشكل حرف L لكن هناك أنواع أخرى وهذا ما سوف نتعلمه في هذا الدرس عند كتابة UiView.animate سوف تلاحظ وجود 4 أنواع نوعين تحدثنا عنهم سابقا ، ونوعين لم نتحدث عنهم في هذه الفقرة اختار هذا النوع شاهد الصورة ولاحظ النص المكتوب في الأسفل : الشكل النهائي سوف يكون بالشكل التالي : UIView.animate(withDuration: 1.0, delay: 0.0, options:[], animations: { }, completion: nil) معاني الـ Parameter : withDuration : تحدثنا عنها في الدرس الماضي ومعناها كم الفترة المرادة لعمل الـ Animation بالثانية الواحده ، ربما لاحظت بأني اضعها دائما "ثانية واحده" ولكن غالبا راح تحتاج أن تضعها من 0.5 الى 0.3 ، لانها تعتبر افضل مدة لأغلب الـ Animation delay : هذا الخيار جديد ، ومعناه التأخير ، كم الفترة التي تريد فيها الانتظار قبل ان يبدا الـ Animation ؟ اجعله على 0.0 في حال اردت ان يبدا مباشرةً بدون تأخير options : هذا أيضا من ضمن الخيارات الجديدة وسيكون اغلب شرحنا يتركز عليه ، لذا سيتم شرحه في الفقرة التالية في الوقت الراهن ، الذي يفترض معرفته هو عند وضع علامة مربع الاريه [] تعني استخدم النوع الافتراضي وأيضا باستطاعتك وضع عدة options وذلك بكتابة علامة الاريه [] ووضع عدة أنواع والفصل فيما بينهم بعلامة الفاصلة , completion : تم ذكره سابقا ومعناه عند اكتمال الـ Animation ماذا تريد ان تفعل ؟ ، وضعنا هنا nil لأننا لا نريد تنفيذ امرا بعد انتهاء الـ Animation . أنواع الـ Options : هناك عدة أنواع للـ Animation سوف نتطرق الى اهمها هناك ٤ انواع رئيسية مسؤوله عن سرعة الـ Animation ، وهيا : curveLinear : ويعني يبدا الـ Animation بنفس السرعة وينتهي بنفس السرعة (لا يحدث اختلاف في السرعة) curveEaseIn : ويعني يبدا الـ Animation بسرعة بطيئة ومن ثم يزداد سرعته (يبدا بطئي وينتهي بسرعة) curveEaseOut : ويعني يبدا الـ Animation بسرعة وينتهي ببطئ (يبدا بسرعة وينتهي ببطئ) curveEaseInOut : هذا النوع هو الافتراضي، ويعني يبدا الـ Animation ببطئ ومن ثم يسرع وينتهي ببطئ (يبدا وينتهي ببطئ) كيف يتم كتابة الـ Options ؟ قم بكتابة علامة الـ . ومن ثم اكتب حرف c لتقوم بفلترة النتائج ولتظهر الانواع الأربعة السابقة ومن ثم اختار النوع الذي تريده مثال لطريقة الكتابة : UIView.animate(withDuration: 1.0, delay: 0.0, options: .curveEaseIn, animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) حقيقة واجهة صعوبة لتوضيح الفروقات فيما بينهم عند عمل صورة متحركة لكل نوع على حدى ! لذا وجدت أفضل طريقة هي باضافتهم جميعا في صورة متحركة واحده ! سوف تلاحظ الفروقات بوضوح مع هذه الصورة : الان سوف نتطرق لنوعين اخرين : autorevers : باختصار هو عملية تكرار "لمره واحده" بحيث يتم عمل الـ Animation وعند الانتهاء اعادته بالعكس "مره واحده" ومن ثم يتوقف UIView.animate(withDuration: 1.0, delay: 0.0, options: .autoreverse, animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) لاحظ الصورة المتحركة ، ذهابا ومن ثم عودة ومن ثم التوقف في الأسفل : repeat: هو التكرار ، يمكن تشبيه بأنه كالصورة المتحركة عندما ينتهي يعيد من البداية وهكذا (سوف تلاحظ بانه تمت عملية قطع فجائية للـ Animation) UIView.animate(withDuration: 1.0, delay: 0.0, options: .repeat, animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) صورة توضيحية : ماذا اذا ثم دمج النوعين السابقين مع بعض ؟ كما ذكرت سابقا يمكن دمج عدة انواع مع بعض وهنا لاحظ عندما ندمج الـ autorevers مع الـ repeat النتيجه سوف تكون عملية تكرار بدون توقف ، بمعنى تكون Perfect loop UIView.animate(withDuration: 1.0, delay: 0.0, options: [.autoreverse , .repeat], animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) صورة توضيحية : اخر خيار هو allowUserInteraction : كما هو واضح من اسمه ، السماح بتفاعل المستخدم مع الـ view اثناء الـ Animation ! لجعل الامر واضحاً لك ، قمت بإضافة زر في داخل المربع وجعلت ابعداه بكامل المربع ومن ثم قم بتجربة بدون استخدام خيار الـ allowUserInteraction بكتابة الكود التالي : UIView.animate(withDuration: 3.0, delay: 0.0, options: [.autoreverse], animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) شاهد الصورة التالية : ولاحظ باني لم استطع الضغط على الزر اثناء الـ Animation ولكن استطعت فقط الضغط على الزر قبل بداية الـ Animation وبعد انتهائه بعد إضافة خيار allowUserInteraction بكتابة الكود بالشكل التالي : UIView.animate(withDuration: 3.0, delay: 0.0, options: [.autoreverse], animations: { self.squareView.transform = CGAffineTransform(translationX: 0, y: 400) }, completion: nil) شاهد الصورة التالية : سوف تلاحظ باني استطعت الضغط على الزر اثناء الـ Animation الفقرة الثانية : الان سوف نتعلم نوع اخر للـ Uiview.animate النوع هذا يطلق عليه Spring Animation معنى الـ Spring هو النابض بمعنى ارتداد الـ Animation سوف تتضح فكرة الـ Spring Animation مع هذه الصورة : كيف يستخدم ؟ بداية نقوم باختيار هذا الـ Function ، كما في الصورة التالية : الكود النهائي سوف يكون بالشكل التالي : UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.0, options:.curveEaseOut, animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 400) }, completion: nil) لاحظ هناك خيارين جديدة وهما : usingSpringWithDamping : ويعني هذا الخيار بالتذبذب (مقدار الاهتزاز) كل ما كان الرقم قريب من الصفر كان مقدار الاهتزاز اعلى ، بما يعني القيمة تكون اقل من 1 واكبر من 0 وضعنا هنا قيمة 0.3 يمكنك التجربة لاستيعاب الفكرة initialSpringVelocity : هذا الخيار المقصود به هو سرعة الاندفاع في البداية ، آبل تنصح بجعله بقيمة 0 يمكنك تغير قيمته أيضا من بين قيمة اقل من 1 واكبر من 0 صورة توضيحية للنتيجة : مثال لأحدى الاستخدامات للـ Spring Animation لنفترض تريد عمل رسالة تنبيه للمستخدم بإظهارها بشكل الـ popup باستطاعتك عملها بالطريقة التالية : بداية سوف نغير لون وحجم المربع الأزرق ونضعه في المنتصف ونضيف label وزر لجعله كالـ popup مثل الصورة التالية : في الـ Viewdidload() نضيف التالي : override func viewDidLoad() { super.viewDidLoad() squareView.layer.cornerRadius = 20 squareView.layer.borderWidth = 1 squareView.layer.borderColor = UIColor.lightGray.cgColor squareView.alpha = 0.0 } الذي قمنا بكتابته : اضفنا حواف ناعمه في اطرف المربع وجعلنا قيمتها 20 اضفنا حدود بحجم 1 بكسل جعلنا لون الحدود باللون الرصاصي الفاتح اخفينا الـ View عند بداية تشغيل البرنامج في الـ AnimationButton نضيف التالي : @IBAction func AnimationButton(_ sender: Any) { squareView.alpha = 1.0 self.squareView.transform = CGAffineTransform(scaleX: 0.3, y: 1) UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.0, options:.curveEaseOut, animations: { self.squareView.transform = .identity }, completion: nil) } الذي قمنا بكتابته التالي : اظهرنا الـ View بجعل قيمة الـ alpha تساوي 1 قبنا بتصغير قيمة x ولم نغير قيمة y ، لماذا في خارج الـ uiview.animate ؟ لأننا نريد أن نجعل الـ View يرجع الى حجمه الطبيعي اثناء الـ animation اضفنا سطر واحد يقوم بإرجاع الـ View الى حجمه الطبيعية ، ولأننا استخدمنا الـ Spring Animation .. سيظهر الـ View بشكل جميل شاهد الصورة التالية :
  10. السلام عليكم .. كما تعلمون ابل في تحديث ios 10.3 اضافة ميزة تسمح للمطورين من اضافة ايقونات مختلفة لتطبيقاتهم ، بما يجعل لمستخدمين حرية اختيار الايقونة المفضله لديهم بدلاُ من اجبارهم على ايقونة واحده ! الطريقة ليست صعبه ولكن تتطلب بعض الخطوات اولا : نضيف الايقونة الاساسية في ملف appicon في مجلد Assets.xcassets ستوجب عليك اضافة جميع الاحجام ولكن لغرض هذا الدرس اضفت ايقونة بحجمين مختلفين احداهم بحجم شاشة الايفون ٧ والاخر بحجم الايفون ٧ بلس للتسهيل عليك يمكنك عمل ايقونة بحجم 1500 في 1500 ومن ثم رفع الايقونة على هذا الموقع Makeappicon وسوف ينتج لك جميع الايقونة بجميع الاحجام سوا كانت للـ ios او الاندوريد وايضا هناك تطبيق في متجر البرامج يقوم بنفس هذه الوظيفة واكثر باسم Asset Catalog Creator ثانيا : اضيف الايقونات الاخرى خارج مجلد Assets.xcassets يمكنك فعل كما فعلت بجعل كل ايقونة بديله في مجلد ، لاحظ يتوجب عليك تسمية الايقونات باسماء مختلفة وليس ضروري جعل كل ايقونة في مجلد مستقل الاهم هو تسمية الايقونات فالايقونه الاولى تم تسميتها بـ icon 1 والايقونة الثانية تم تسميتها بـ icon 2 ثالثا : تحتاج الى اضافة بعض الاسطر لملف Info.plist ١. قم بفتح ملف Info.plist وانشى سطر جديد عن طريقة الضغط على علامة "+" وقم بتسميته باسم CFBundleIcons سوف يتغير تلقائيا الى Icon files (iOS 5) و اجعله من نوع Dictionary ٢. اضغط على السهم بجانب Icon files (iOS 5) ومن ثم اضغط على علامة "+" لاضافة حقل اسفل Icon files (iOS 5) وباسم CFBundleAlternateIcons وايضا من نوع Dictionary ٣. اضغط على السهم بجانب CFBundleAlternateIcons ومن ثم اضغط على علامة "+" لاضافة حقل جديد هذا الحقل قم بتسميته باسم الايقونة البديله هنا سوف نقومب تسميته بـ "icon 1" واجعل نوعه Dictionary ٤. اضغط على السهم بجانب "icon 1" ومن ثم اضغط على علامة "+" وقم بتسميته باسم CFBundleIconFiles وهذه المره اجعله من نوع Array ٥. اضغط على علامة الـ "+" بجانب "CFBundleIconFiles" سيظهر حقل جديد باسم "item 0" من نوع String اجعل هذا الحقل باسم الايقونه ، نحن قمنا بتسميتها بـ icon 1 فاجعل قيمتها بنفس الاسم كرر الخطوات من ٣ الى ٥ على حسبب عدد الايقونات التي تريدها صورة توضح الخطوات السابقة بعد اضافة ايقونتين واحده باسم icon 1 والاخرى باسم icon 2 رابعا : اذهب الى Main.Storyboard واضيف Segmented Control قمة بتغيير مسمياتها حسب رغبتك ، او اجلعها كما في الصورة خامسا : اضغط على زر control واسحب الـ segmented الى ملف الاكواد وقم بتسميته بـ segmentedControl واجعل النوع Outlet سادسا : اضغط على زر control واسحب الـ segmented الى ملف الاكواد وقم بتسميته بـ indexChanged سابعا : الان بداخل اقواس indexChanged اضيف التالي @IBAction func indexChanged(_ sender: UISegmentedControl) { switch segmentedControl.selectedSegmentIndex { case 0: UIApplication.shared.setAlternateIconName(nil) break case 1: UIApplication.shared.setAlternateIconName("icon1") break case 2: UIApplication.shared.setAlternateIconName("icon2") break default: break } } لاحظ التالي : هذا السطر معناه الايقونة الاصلية UIApplication.shared.setAlternateIconName(nil) بقية الاسطر يتم كتابة اسماء الايقونات بدلا من nil اخيراً : قم بتشغيل البرنامج وسوف تلاحظ امكانية تغيير ايقونة التطبيق ستظهر رسالة تفيد بعملية التغير كهذه : يمكن تحميل المشروع من هنا
  11. قد تكون مبرمجاً بارعاً ومتمكناً ولكن تطبيقاتك لا تجذب المستخدمين ! .. لماذا ؟ بداية تذكر بأن هناك ألاف المطورين حول العالم ، فالمنافسة على اشدها ! هناك امرين مهمين، غير البرمجة وخلو او قلة المشاكل (Bugs) في تطبيقاتك هما : ١. واجهة جميلة وجذابه (اذا كنت لست مصمماً ، تستطيع الاستعانة بمصممين واجهة المستخدم) ٢. تجربة المستخدم ، تجربة المستخدم جدا مهمه في كل شيء بما فيها التطبيقات (أيضا تستطيع الاستعانة بمصممين تجربة المستخدم) بإختصار : واجهة المستخدم UI : هي الواجهة التي تصممها ليتعامل معها المستخدم وهي ظاهرة له . تجربة المستخدم UX : هي تجربة المستخدم مع تلك الواجهة ، اما أن تكون جيدة واما أن تكون سيئة . فكما ذكرت سابقا كود نظيف وعملي + واجهة قبيحة + تجربة مستخدم سيئة = تطبيق سيئ ! يجب أي تتناسب جميع العناصر مع بعض لتجعل التطبيق مميز ومحبب لذا المستخدمين . من الامور المفيدة في تحسين تجربة المستخدم هي الـ Animation كمستخدم ايهما تفضل ؟ - عند كتابة كلمة السر بشكل خاطئ هل تفضل "رسالة كئيبة تفيد بأن كلمة السر التي ادخلتها خاطئة" أم اهتزاز حقل كلمة السر تفيد بأن كلمة السر خاطئة ؟ - هل تفضل ظهور menu بدون تأثير وبشكل فجائي او بتأثير وبصورة سلسه ؟ - عند الحذف هل تفضل الحذف فجأة او بتأثير التلاشي مثلا ؟ بما سبق مجرد امثله بسيطة لكن لها تأثير في تجربة المستخدم للتطبيق ، تم دمج الأمثلة لتعطي تصورا لأهمية الـ Animation ولكن يجب التوضيح بأن تجربة المستخدم أعمق من ذلك وانصحك بالقراءة عنها. أيضا يتوجب من الامور المهمة في تجربة المستخدم لا تبالغ بالـ Animation ! تذكر بأن أي أمر يزيد عن حده يصبح سيئا! هناك نوعين من الـ Animation Animation بدون Auto Layout Animation مع Auto Layout الـ Auto Layout هو باختصار جعل واجهة المستخدم تتوافق مع جميع احجام الشاشات . معلومة : اغلب الـ Animation يعتمد على سطر واحد فقط ! UIView.animate(withDuration: (0.0) animations: { }) withDuration : تعني كم فترة التحريك بالثانية الواحده ؟ بداخل الاقواس تضع الاكواد التي تريدها تنفيذها دعونا نبدأ في الشرح : بداية نضيف زر ونضيف Auto Layout للزر ثم نضع view ونغير لونه لأي لون تريده وأيضا نضيف الـ Auto Layout للـ View من ثم نربط العناصر مع ملف الاكواد الشكل النهائي : import UIKit class ViewController: UIViewController { @IBOutlet weak var squareView: UIView! override func viewDidLoad() { super.viewDidLoad() } @IBAction func AnimationButton(_ sender: Any) { } } تغير اللون : في AnimationButton() نضيف هذا السطر : UIView.animate(withDuration: (1.0) animations: { self.squareView.backgroundColor = UIColor.red }) الظهور من العدم : نضيف هذا السطر في Viewdidload squareView.alpha = 0.0 و في AnimationButton() نضيف هذا السطر UIView.animate(withDuration: (1.0) animations: { squareView.alpha = 1.0 }) قبل العودة لباقي الأمثلة يتوجب عليك فهم امراً مهماً وهو إحداثيات النظام المتبع في الاجهزة وبحكم موضوعنا عن لغة Swift وتحديداً نظام iOS فإحداثيات النظام هي : قيمة x الموجب تكون في اليمين وقيمة y الموجب تكون في الأسفل صورة من موقع آبل : صورة توضيحية : معلومة الشروحات التالية لا تعتمد على الـ Auto Layout سيتم التطرق لطريقة أخرى في فقره قادمة التضخيم : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(scaleX: 2, y: 2) }) كما تلاحظ من الكود السابق هناك قيمة x و y قيمة أكس تضاعف من الجهتين x و –x قيمة واي تضاعف من الجهتين العلوية والسفلية y و –y جعل القيمتين متساوية تعني مضاعفة ابعاد المربع بالتساوي وكيف يعمل ؟ يتم ضرب الطول في قيمة y والعرض في قيمة x التصغير : اذا اردت التصغير فيتم استخدام نفس الكود السابق ولكن يتم إعطاء قيمة اصغر من 1 لأن 1 يساوي القيمة الاصلية زيادته يساوي مضاعفه للحجم ، نقصانه يساوي تصغير الحجم UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8 ) }) التحريك بشكل عمودي : الان سوف تفيدنا المعلومة السابقة المتعلقة بالإحداثيات لأننا نريد تحريك المربع بشكل عمودي للأسفل فإننا نقوم بكتابة الأمر التالي : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 400) }) قيمة x بـ 0 لأننا لا نريد تحريك المربع بشكل افقي قيمة y بـ 400 لأننا نريد تحريكه للأسفل بمقدار 400 التحريك بشكل أفقي : الأن نريد تحريك المربع بشكل افقي الى اليسار نقوم بكتابة الأمر التالي : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(translationX:-122, y: 0) }) تم إعطاء قيمة x بـ -122 لأننا نريد تحريك المربع لليسار اذا تم إعطاء قيمة موجبة سيتحرك المربع لليمين ، تذكر صورة الإحداثيات وتم إعطاء قيمة y بـ 0 لأننا لا نريد تحريكه بشكل عمودي ماذا اذا اعطينا قيمة لـ x وقيمة أخرى لـ y ؟ سوف يتحرك المربع بشكل مائل لنجرب الكود التالي ونرى النتيجة : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(translationX:122, y: 300) }) ماذا اذا اردنا التحريك بشكل حرف L ؟ هنا نحتاج الى تعلم معلومة جديدة ، وهيا : ذكرنا في بداية الموضوع بأن هناك سطر واحد لتنفيذ الـ Animation وفعلياً تسطيع تنفيذ جميع الامور السابقة مع بعض اذا تم اضافتها جميعها في داخل أقواس كود الـ Animation ولكن ماذا اذا اردنا تنفيذ Animation اخر بعد انتهاء الأول وليس في نفس الوقت ؟ هنا نحتاج استخدام كود مختلف يمكنك ملاحظ الكود الذي تم اختياره في الـ Xcode في هذه الصورة : من الصورة السابقة الخيار المحدد هو الذي تم اختياره في هذه اللحظة الخيار الذي قبله هو الذي كنا نستخدمه منذ بداية الموضوع ومن ثم قمنا إضافة سطر Animation اخر بعد true UIView.animate(withDuration: 1.0, animations: { }) { (true) in UIView.animate(withDuration: 1.0, animations: { }) } للتوضيح : الامر الزائد هو وجود شرط وتم إعطائه قيمة true يمكنك وضع أي شرط بحيث عن انتهاء الـ animation يحدث ما بداخل الشرط ولكن لغرض تنفيذ الـ animation الاخر بعد انتهاء الاول مباشرة لذا تم إعطاء قيمة true وتم إضافة كود animation اخر بداخله الان نعود لسؤالنا السابق ماذا اذا اردنا التحريك بشكل حرف L ؟ نقوم بتنفيذ التالي : UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 300) }) { (true) in UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:-122, y: 300) }) } الذي قمنا به هو تحريك المربع بشكل عمودي في البداية بمقدار 300 وفي الكود التالي بداخل شرط الـ true حافظنا على قيمة الـ y وحركنا قيمة الـ x باتجاه اليمين الان نقو بحذف ما سبق ونعود لسطر الـ animation القديم UIView.animate(withDuration: (1.0) animations: { }) و قبل أن نستكمل الشرح ، حان الوقت لتعلم معلومة جديدة : في الشرح القادم سوف نتعلم كيفية دوران/التفاف المربع وبحكم الغالبية العظمى لا يتذكروا ما تم دراسته في مادة الرياضات يتوجب عليا شرح بعض النقاط لجعل المربع يلتف فإننا نعتمد على الزاوية وبحكم اننا في الغالب سوف نحتاج الى دوران المربع بزاوية 90 او 180 فأننا سنحتاج تعلم هذه المعلومة مجموع زوايا الدائرة هو 360 درجة والذي يساوي 2π بما يعني اذا اردنا زاوية 180 فسوف تكون القيمة π زاوية 180 تعني قلب المربع رأساً على عقب واذا اردنا الزاوية 90 فسوف تكون القيمة π/2 زاوية 90 تعني الالتفاف الى الجهة اليمين بما يعني زاوية -90 تعني الالتفاف الى الجهة اليسار الان نعود للشرح ماذا اذا اردنا التفاف المربع الى اليمين ؟ UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi/2)) }) معلومية : Double.pi تعني π ولأن CGAffineTransform ياخد قيمة من نوع CGFloat فاننا نقوم بعملية casting الى CGFloat ماذا يعني عملية الـ casting ؟ هي عملية تحويل من نوع معين الى نوع اخر فنحن هنا نقوم بعملية تحويل من Double الى CGFloat ماذا اذا اردنا التحريك بشكل عمودي والالتفاف في آنٍ واحد ؟ نقوم بكتابة الكود بهذا الشكل : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 300).rotated(by: CGFloat(Double.pi/2)) }) الان حان الوقت لنتعلم معلومة جديدة : ماذا اذا اردنا تنفيذ كود التحريك بالعكس دون الحاجة الى كتابة الاكواد من جديد ؟ بما يعني عند الضغط على الزر مره اخرى فإنه يقوم بإعادة المربع الى موقع الاول ! كل ما علينا فعله هو الاستفادة من property في الـ CGAffineTransform تدعى identity تمت اضافتها في iOS10 وظيفتها تقوم بحفظ الاحداتياث قبل تنفيذ الـ animations الان لكيفية الاستخدام نقوم بكتابة الاكود بالشكل التالي: UIView.animate(withDuration: 1.0) { if self.squareView.transform == CGAffineTransform.identity{ self.squareView.transform = CGAffineTransform(translationX:0, y: 300).rotated(by: CGFloat(Double.pi/2)) }else { self.squareView.transform = CGAffineTransform.identity } } الـ if وظيفتها تحفظ الموقع قبل عمل الـ animations وفي الـ else نقوم بكتابة نفس السطر بدون الـ if لعمل الـ animation بشكل عكسي وبالتالي بسطر واحد تقوم على تنفيذ الكود الـ animations بشكل عكسي . عمل animations مع الـ Auto Layout لنبدا الشرح : بداية يتوجب عليك إضافة الـ LayoutConstraint نحن نحتاج الى الـ Top وهو المسؤول عن التحريك بشكل عمودي وأيضا نحتاج الى الـ Leading وهو المسؤول عن التحريك بشكل افقي لمعرفة كيفية اضافتهم الى ملف الاكواد ، شاهد الصورة المتحركة التالية : تحريك المربع بشكل عمودي : لاحظ القيود التي وضعناها في بداية الموضوع وضعنا القيود من الأعلى ومن يمين ومن اليسار فقط ! لم نوضع قيود في الأسفل لذا لتحريك المربع الى الأسفل نقوم بكتابة السطر التالي UIView.animate(withDuration: (1.0) animations: { self.top.constant = 400 self.view.layoutIfNeeded() }) الذي قمنا به هنا هو تغير قيمة الـ top ومن ثم اضفنا هذا السطر self.view.layoutIfNeeded() السطر هذا وظيفته تحديث الـ Auto Layout بشكل متدرج بدون اضافته في النهاية الكود سوف يسبب في تغير موقع المربع بشكل فجائي تحريك المربع بشكل افقي : الان لأننا نريد تحريك المربع بشكل افقي يتوجب علينا تعديل القيود أولا غير القيود الى الشكل التالي : ومن ثم قم بكتابة الكود التالي : UIView.animate(withDuration: (1.0) animations: { self.Leading.constant = 240 self.view.layoutIfNeeded() }) ماذا اذا اردنا تصغير المربع ؟ نحتاج أولا تغيير القيود الى هذا الشكل : ومن ثم إضافة الـ LayoutConstraint الخاص بـ Trailing كما في الصورة التالية : ومن ثم كتابة هذه الأسطر : UIView.animate(withDuration: (1.0) animations: { self.Leading.constant = 150 self.Trailing.constant = 150 self.view.layoutIfNeeded() }) الذي قمنا به هو تغير القيود من الجانب الأيمن والايسر وبالتالي يتم تصغير المربع مع المحافظة على موقعه لذلك لم نغير قيمة الـ top تلميحه اخيره ! كيفية معرفة الموقع النهائي للمربع ؟ بمعنى عندما اريد تحريك المربع بشكل عمودي كيف معرفة كم سوف يتوجب عليا جعل قيمة الـ top في حالة التحريك باستخدام الـ Auto Layout ؟ الطريقة تعتمد على الـ Storyboard - نرى كم قيمة الـ top قبل التحريك - نقوم بتحريك المربع - سيظهر لنا لون برتقالي يوضح مقدار التغير الذي حدث - نضيف القيمة التي نراها مع القيمة السابقة في هذه الصورة المتحركة : قبل التحريك قيمة الـ top كانت 92 وعندما قمنا بالتحريك ظهرت قيمة +261 اذا نقوم بجميع قيمة 92 + 261 = 353 وبتالي قيمة الـ top تصبح 353 الطريقة الثانية اننا نعتمد على قيمة الـ y كما في المثال القادم في حالة اردنا التحريك بدون استخدام الـ Auto Layout فإننا نعتمد على قيمة y كما في الصورة التالية :
  12. قد تكون مبرمجاً بارعاً ومتمكناً ولكن تطبيقاتك لا تجذب المستخدمين ! .. لماذا ؟ بداية تذكر بأن هناك ألاف المطورين حول العالم ، فالمنافسة على اشدها ! هناك امرين مهمين، غير البرمجة وخلوا او قلة المشاكل (Bugs) في تطبيقاتك هما : ١. واجهة جميلة وجذابه (اذا كنت لست مصمماً ، تستطيع الاستعانة بمصممين واجهة المستخدم) ٢. تجربة المستخدم ، تجربة المستخدم جدا مهمه في كل شيء بما فيها التطبيقات (أيضا تستطيع الاستعانة بمصممين تجربة المستخدم) بإختصار : واجهة المستخدم UI : هي الواجهة التي تصممها ليتعامل معها المستخدم وهي ظاهرة له . تجربة المستخدم UX : هي تجربة المستخدم مع تلك الواجهة ، اما أن تكون جيدة واما أن تكون سيئة . فكما ذكرت سابقا كود نظيف وعملي + واجهة قبيحة + تجربة مستخدم سيئة = تطبيق سيئ ! يجب أي تتناسب جميع العناصر مع بعض لتجعل التطبيق مميز ومحبب لذا المستخدمين . من الامور المفيدة في تحسين تجربة المستخدم هي الـ Animation كمستخدم ايهما تفضل ؟ - عند كتابة كلمة السر بشكل خاطئ هل تفضل "رسالة كئيبة تفيد بأن كلمة السر التي ادخلتها خاطئة" أم اهتزاز حقل كلمة السر تفيد بأن كلمة السر خاطئة ؟ - هل تفضل ظهور menu بدون تأثير وبشكل فجائي او بتأثير وبصورة سلسه ؟ - عند الحذف هل تفضل الحذف فجأة او بتأثير التلاشي مثلا ؟ بما سبق مجرد امثله بسيطة لكن لها تأثير في تجربة المستخدم للتطبيق ، تم دمج الأمثلة لتعطي تصورا لأهمية الـ Animation ولكن يجب التوضيح بأن تجربة المستخدم أعمق من ذلك وانصحك بالقراءة عنها. أيضا يتوجب من الامور المهمة في تجربة المستخدم لا تبالغ بالـ Animation ! تذكر بأن أي أمر يزيد عن حده يصبح سيئا! هناك نوعين من الـ Animation Animation بدون Auto Layout Animation مع Auto Layout الـ Auto Layout هو باختصار جعل واجهة المستخدم تتوافق مع جميع احجام الشاشات . معلومة : اغلب الـ Animation يعتمد على سطر واحد فقط ! UIView.animate(withDuration: (0.0) animations: { }) withDuration : تعني كم فترة التحريك بالثانية الواحده ؟ بداخل الاقواس تضع الاكواد التي تريدها تنفيذها دعونا نبدأ في الشرح : بداية نضيف زر ونضيف Auto Layout للزر ثم نضع view ونغير لونه لأي لون تريده وأيضا نضيف الـ Auto Layout للـ View من ثم نربط العناصر مع ملف الاكواد الشكل النهائي : import UIKit class ViewController: UIViewController { @IBOutlet weak var squareView: UIView! override func viewDidLoad() { super.viewDidLoad() } @IBAction func AnimationButton(_ sender: Any) { } } تغير اللون : في AnimationButton() نضيف هذا السطر : UIView.animate(withDuration: (1.0) animations: { self.squareView.backgroundColor = UIColor.red }) الظهور من العدم : نضيف هذا السطر في Viewdidload squareView.alpha = 0.0 و في AnimationButton() نضيف هذا السطر UIView.animate(withDuration: (1.0) animations: { squareView.alpha = 1.0 }) قبل العودة لباقي الأمثلة يتوجب عليك فهم امراً مهماً وهو إحداثيات النظام المتبع في الاجهزة وبحكم موضوعنا عن لغة Swift وتحديداً نظام iOS فإحداثيات النظام هي : قيمة x الموجب تكون في اليمين وقيمة y الموجب تكون في الأسفل صورة من موقع آبل : صورة توضيحية : معلومة الشروحات التالية لا تعتمد على الـ Auto Layout سيتم التطرق لطريقة أخرى في فقره قادمة التضخيم : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(scaleX: 2, y: 2) }) كما تلاحظ من الكود السابق هناك قيمة x و y قيمة أكس تضاعف من الجهتين x و –x قيمة واي تضاعف من الجهتين العلوية والسفلية y و –y جعل القيمتين متساوية تعني مضاعفة ابعاد المربع بالتساوي وكيف يعمل ؟ يتم ضرب الطول في قيمة y والعرض في قيمة x التصغير : اذا اردت التصغير فيتم استخدام نفس الكود السابق ولكن يتم إعطاء قيمة اصغر من 1 لأن 1 يساوي القيمة الاصلية زيادته يساوي مضاعفه للحجم ، نقصانه يساوي تصغير الحجم UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8 ) }) التحريك بشكل عمودي : الان سوف تفيدنا المعلومة السابقة المتعلقة بالإحداثيات لأننا نريد تحريك المربع بشكل عمودي للأسفل فإننا نقوم بكتابة الأمر التالي : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 400) }) قيمة x بـ 0 لأننا لا نريد تحريك المربع بشكل افقي قيمة y بـ 400 لأننا نريد تحريكه للأسفل بمقدار 400 التحريك بشكل أفقي : الأن نريد تحريك المربع بشكل افقي الى اليسار نقوم بكتابة الأمر التالي : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(translationX:-122, y: 0) }) تم إعطاء قيمة x بـ -122 لأننا نريد تحريك المربع لليسار اذا تم إعطاء قيمة موجبة سيتحرك المربع لليمين ، تذكر صورة الإحداثيات وتم إعطاء قيمة y بـ 0 لأننا لا نريد تحريكه بشكل عمودي ماذا اذا اعطينا قيمة لـ x وقيمة أخرى لـ y ؟ سوف يتحرك المربع بشكل مائل لنجرب الكود التالي ونرى النتيجة : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(translationX:122, y: 300) }) ماذا اذا اردنا التحريك بشكل حرف L ؟ هنا نحتاج الى تعلم معلومة جديدة ، وهيا : ذكرنا في بداية الموضوع بأن هناك سطر واحد لتنفيذ الـ Animation وفعلياً تسطيع تنفيذ جميع الامور السابقة مع بعض اذا تم اضافتها جميعها في داخل أقواس كود الـ Animation ولكن ماذا اذا اردنا تنفيذ Animation اخر بعد انتهاء الأول وليس في نفس الوقت ؟ هنا نحتاج استخدام كود مختلف يمكنك ملاحظ الكود الذي تم اختياره في الـ Xcode في هذه الصورة : من الصورة السابقة الخيار المحدد هو الذي تم اختياره في هذه اللحظة الخيار الذي قبله هو الذي كنا نستخدمه منذ بداية الموضوع ومن ثم قمنا إضافة سطر Animation اخر بعد true UIView.animate(withDuration: 1.0, animations: { }) { (true) in UIView.animate(withDuration: 1.0, animations: { }) } للتوضيح : الامر الزائد هو وجود شرط وتم إعطائه قيمة true يمكنك وضع أي شرط بحيث عن انتهاء الـ animation يحدث ما بداخل الشرط ولكن لغرض تنفيذ الـ animation الاخر بعد انتهاء الاول مباشرة لذا تم إعطاء قيمة true وتم إضافة كود animation اخر بداخله الان نعود لسؤالنا السابق ماذا اذا اردنا التحريك بشكل حرف L ؟ نقوم بتنفيذ التالي : UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 300) }) { (true) in UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:-122, y: 300) }) } الذي قمنا به هو تحريك المربع بشكل عمودي في البداية بمقدار 300 وفي الكود التالي بداخل شرط الـ true حافظنا على قيمة الـ y وحركنا قيمة الـ x باتجاه اليمين الان نقو بحذف ما سبق ونعود لسطر الـ animation القديم UIView.animate(withDuration: (1.0) animations: { }) و قبل أن نستكمل الشرح ، حان الوقت لتعلم معلومة جديدة : في الشرح القادم سوف نتعلم كيفية دوران/التفاف المربع وبحكم الغالبية العظمى لا يتذكروا ما تم دراسته في مادة الرياضات يتوجب عليا شرح بعض النقاط لجعل المربع يلتف فإننا نعتمد على الزاوية وبحكم اننا في الغالب سوف نحتاج الى دوران المربع بزاوية 90 او 180 فأننا سنحتاج تعلم هذه المعلومة مجموع زوايا الدائرة هو 360 درجة والذي يساوي 2π بما يعني اذا اردنا زاوية 180 فسوف تكون القيمة π زاوية 180 تعني قلب المربع رأساً على عقب واذا اردنا الزاوية 90 فسوف تكون القيمة π/2 زاوية 90 تعني الالتفاف الى الجهة اليمين بما يعني زاوية -90 تعني الالتفاف الى الجهة اليسار الان نعود للشرح ماذا اذا اردنا التفاف المربع الى اليمين ؟ UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi/2)) }) معلومية : Double.pi تعني π ولأن CGAffineTransform ياخد قيمة من نوع CGFloat فاننا نقوم بعملية casting الى CGFloat ماذا يعني عملية الـ casting ؟ هي عملية تحويل من نوع معين الى نوع اخر فنحن هنا نقوم بعملية تحويل من Double الى CGFloat ماذا اذا اردنا التحريك بشكل عمودي والالتفاف في آنٍ واحد ؟ نقوم بكتابة الكود بهذا الشكل : UIView.animate(withDuration: (1.0) animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 300).rotated(by: CGFloat(Double.pi/2)) }) الان حان الوقت لنتعلم معلومة جديدة : ماذا اذا اردنا تنفيذ كود التحريك بالعكس دون الحاجة الى كتابة الاكواد من جديد ؟ بما يعني عند الضغط على الزر مره اخرى فإنه يقوم بإعادة المربع الى موقع الاول ! كل ما علينا فعله هو الاستفادة من property في الـ CGAffineTransform تدعى identity تمت اضافتها في iOS10 وظيفتها تقوم بحفظ الاحداتياث قبل تنفيذ الـ animations الان لكيفية الاستخدام نقوم بكتابة الاكود بالشكل التالي: UIView.animate(withDuration: 1.0) { if self.squareView.transform == CGAffineTransform.identity{ self.squareView.transform = CGAffineTransform(translationX:0, y: 300).rotated(by: CGFloat(Double.pi/2)) }else { self.squareView.transform = CGAffineTransform.identity } } الـ if وظيفتها تحفظ الموقع قبل عمل الـ animations وفي الـ else نقوم بكتابة نفس السطر بدون الـ if لعمل الـ animation بشكل عكسي وبالتالي بسطر واحد تقوم على تنفيذ الكود الـ animations بشكل عكسي . عمل animations مع الـ Auto Layout لنبدا الشرح : بداية يتوجب عليك إضافة الـ LayoutConstraint نحن نحتاج الى الـ Top وهو المسؤول عن التحريك بشكل عمودي وأيضا نحتاج الى الـ Leading وهو المسؤول عن التحريك بشكل افقي لمعرفة كيفية اضافتهم الى ملف الاكواد ، شاهد الصورة المتحركة التالية : تحريك المربع بشكل عمودي : لاحظ القيود التي وضعناها في بداية الموضوع وضعنا القيود من الأعلى ومن يمين ومن اليسار فقط ! لم نوضع قيود في الأسفل لذا لتحريك المربع الى الأسفل نقوم بكتابة السطر التالي UIView.animate(withDuration: (1.0) animations: { self.top.constant = 400 self.view.layoutIfNeeded() }) الذي قمنا به هنا هو تغير قيمة الـ top ومن ثم اضفنا هذا السطر self.view.layoutIfNeeded() السطر هذا وظيفته تحديث الـ Auto Layout بشكل متدرج بدون اضافته في النهاية الكود سوف يسبب في تغير موقع المربع بشكل فجائي تحريك المربع بشكل افقي : الان لأننا نريد تحريك المربع بشكل افقي يتوجب علينا تعديل القيود أولا غير القيود الى الشكل التالي : ومن ثم قم بكتابة الكود التالي : UIView.animate(withDuration: (1.0) animations: { self.Leading.constant = 240 self.view.layoutIfNeeded() }) ماذا اذا اردنا تصغير المربع ؟ نحتاج أولا تغيير القيود الى هذا الشكل : ومن ثم إضافة الـ LayoutConstraint الخاص بـ Trailing كما في الصورة التالية : ومن ثم كتابة هذه الأسطر : UIView.animate(withDuration: (1.0) animations: { self.Leading.constant = 150 self.Trailing.constant = 150 self.view.layoutIfNeeded() }) الذي قمنا به هو تغير القيود من الجانب الأيمن والايسر وبالتالي يتم تصغير المربع مع المحافظة على موقعه لذلك لم نغير قيمة الـ top تلميحه اخيره ! كيفية معرفة الموقع النهائي للمربع ؟ بمعنى عندما اريد تحريك المربع بشكل عمودي كيف معرفة كم سوف يتوجب عليا جعل قيمة الـ top في حالة التحريك باستخدام الـ Auto Layout ؟ الطريقة تعتمد على الـ Storyboard - نرى كم قيمة الـ top قبل التحريك - نقوم بتحريك المربع - سيظهر لنا لون برتقالي يوضح مقدار التغير الذي حدث - نضيف القيمة التي نراها مع القيمة السابقة في هذه الصورة المتحركة : قبل التحريك قيمة الـ top كانت 92 وعندما قمنا بالتحريك ظهرت قيمة +261 اذا نقوم بجميع قيمة 92 + 261 = 353 وبتالي قيمة الـ top تصبح 353 الطريقة الثانية اننا نعتمد على قيمة الـ y كما في المثال القادم في حالة اردنا التحريك بدون استخدام الـ Auto Layout فإننا نعتمد على قيمة y كما في الصورة التالية :
  13. السلام عليكم .. كما تعلمون ابل في تحديث ios 10.3 اضافة ميزة تسمح للمطورين من اضافة ايقونات مختلفة لتطبيقاتهم ، بما يجعل لمستخدمين حرية اختيار الايقونة المفضله لديهم بدلاُ من اجبارهم على ايقونة واحده ! الطريقة ليست صعبه ولكن تتطلب بعض الخطوات اولا : نضيف الايقونة الاساسية في ملف appicon في مجلد Assets.xcassets ستوجب عليك اضافة جميع الاحجام ولكن لغرض هذا الدرس اضفت ايقونة بحجمين مختلفين احداهم بحجم شاشة الايفون ٧ والاخر بحجم الايفون ٧ بلس للتسهيل عليك يمكنك عمل ايقونة بحجم 1500 في 1500 ومن ثم رفع الايقونة على هذا الموقع Makeappicon وسوف ينتج لك جميع الايقونة بجميع الاحجام سوا كانت للـ ios او الاندوريد وايضا هناك تطبيق في متجر البرامج يقوم بنفس هذه الوظيفة واكثر باسم Asset Catalog Creator ثانيا : اضيف الايقونات الاخرى خارج مجلد Assets.xcassets يمكنك فعل كما فعلت بجعل كل ايقونة بديله في مجلد ، لاحظ يتوجب عليك تسمية الايقونات باسماء مختلفة وليس ضروري جعل كل ايقونة في مجلد مستقل الاهم هو تسمية الايقونات فالايقونه الاولى تم تسميتها بـ icon 1 والايقونة الثانية تم تسميتها بـ icon 2 ثالثا : تحتاج الى اضافة بعض الاسطر لملف Info.plist ١. قم بفتح ملف Info.plist وانشى سطر جديد عن طريقة الضغط على علامة "+" وقم بتسميته باسم CFBundleIcons سوف يتغير تلقائيا الى Icon files (iOS 5) و اجعله من نوع Dictionary ٢. اضغط على السهم بجانب Icon files (iOS 5) ومن ثم اضغط على علامة "+" لاضافة حقل اسفل Icon files (iOS 5) وباسم CFBundleAlternateIcons وايضا من نوع Dictionary ٣. اضغط على السهم بجانب CFBundleAlternateIcons ومن ثم اضغط على علامة "+" لاضافة حقل جديد هذا الحقل قم بتسميته باسم الايقونة البديله هنا سوف نقومب تسميته بـ "icon 1" واجعل نوعه Dictionary ٤. اضغط على السهم بجانب "icon 1" ومن ثم اضغط على علامة "+" وقم بتسميته باسم CFBundleIconFiles وهذه المره اجعله من نوع Array ٥. اضغط على علامة الـ "+" بجانب "CFBundleIconFiles" سيظهر حقل جديد باسم "item 0" من نوع String اجعل هذا الحقل باسم الايقونه ، نحن قمنا بتسميتها بـ icon 1 فاجعل قيمتها بنفس الاسم كرر الخطوات من ٣ الى ٥ على حسبب عدد الايقونات التي تريدها صورة توضح الخطوات السابقة بعد اضافة ايقونتين واحده باسم icon 1 والاخرى باسم icon 2 رابعا : اذهب الى Main.Storyboard واضيف Segmented Control قمة بتغيير مسمياتها حسب رغبتك ، او اجلعها كما في الصورة خامسا : اضغط على زر control واسحب الـ segmented الى ملف الاكواد وقم بتسميته بـ segmentedControl واجعل النوع Outlet سادسا : اضغط على زر control واسحب الـ segmented الى ملف الاكواد وقم بتسميته بـ indexChanged سابعا : الان بداخل اقواس indexChanged اضيف التالي @IBAction func indexChanged(_ sender: UISegmentedControl) { switch segmentedControl.selectedSegmentIndex { case 0: UIApplication.shared.setAlternateIconName(nil) break case 1: UIApplication.shared.setAlternateIconName("icon1") break case 2: UIApplication.shared.setAlternateIconName("icon2") break default: break } } لاحظ التالي : هذا السطر معناه الايقونة الاصلية UIApplication.shared.setAlternateIconName(nil) بقية الاسطر يتم كتابة اسماء الايقونات بدلا من nil اخيراً : قم بتشغيل البرنامج وسوف تلاحظ امكانية تغيير ايقونة التطبيق ستظهر رسالة تفيد بعملية التغير كهذه : يمكن تحميل المشروع من هنا

عالم البرمجة

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