1. اللغة المستخدمة : swift3
    البرنامج : xcode 8
     
    السلام عليكم،
    في هذا الدرس سنتحدث عن واحدة من أهم المهارات التي تساعدك في بناء مشروعك في وقت قياسي و بنتائج ممتازة اعتمادا على استخدام مكتبات قام بإنشائها مطورون آخرون لتكون متاحة لجميع المطورين.
    لكن قبل ذلك علينا فهم بعض الأمور الأساسية للبدء  ثم سنتحدث عن طريقة تنزيل المكتبات إلى مشاريعك.
     
    الTermianl : 
    حتى تستطيع التعامل مع الملفات الخارجية يجب أن تتعلم في البداية طريقة التعامل مع الterminal
    Terminal : هي واجهة غير رسومية يمكن استخدامها لتنفيذ الأوامر العادية التي تقوم بها كإنشاء ملف أو حذفه.. الخ.
    للوصول إليها كل ما عليك فعله هو كتابة terminal في شريط البحث للوصول السريع إليها و ستظهر لك كأول نتيجة، قم بفتحها

    كما ذكرنا فإن الterminal هي واجهة غير رسومية يمكن فيها تنفيذ الأوامر عن طريق كتابتها بطريقة معينة و إهم الإوامر التي يجب التعرف عليها لهذا الدرس هي التالية : 
    cd : و هي اختصار ل (change directory ) و هي تستخدم للانتقال إلى أحد المجلدات و هي تشبه فتحك لأي مجلد من المجلدات بالطريقة العادية.
    ls : و هي تستخدم لعرض كل محتويات المجلد الموجود بداخله حاليا.
    الآن قم بفتح الterminal و قم بكتابة : 
    cd Desktop  للانتقال إلى سطح المكتب الخاص بك
    الآن قم بكتابة ls لتظهر محتويات سطح المكتب.
    ستجد أن كل الملفات و المجلدات و التطبيقات الموجودة في سطح المكتب قد تم عرضها لك في الterminal.
    هذه أهم الأمور التي يجب معرفتها فيما يخص وحدة التحكم أو الterminal للمواصلة في هذا الدرس.
     
    الCocoapod :
    و الآن حتى تتمكن من تنزيل المكتبات و الحصول عليها لابد من تنزيل أداة على وحدة التحكم للحصول على إمكانية استخدام أوامر مهمة تمكنك من إضافة المكتبات مباشرة في مشاريعك في الxcode و هذه الأداة تسمى cocoa pod و حتى تقوم بتنزيلها على الterminal الخاص بك قم بزيارة موقعهم:
    https://cocoapods.org/
    و انسخ السطر الموجود في خانة Install
    أو انسخه مباشرة من هنا : 
    sudo gem install cocoapods
    و قم بلصقه في وحدة التحكم (terminal) لديك.
    قد يطلب منك إدخال رقمك سري، قم بإدخاله ثم اضغط زر return أو enter.
    حينها سيبدأ الterminal بتنزيل الcocoa pod.
     
    ملاحظة: قد لا تظهر لك أي إشارة في الterminal تشير إلى أن البرنامج يتم تنزيله حاليا، و لكن يمكنك التأكد إذا ما نظرت إلى الزر الأحمر في الزاوية اليسرى في الشريط العلوي و الخاص بإغلاق البرنامج. حيث يفترض أن تجد بداخله دائرة سوداء و التي تشير إلى أن البرنامج يقوم بتنفيذ أحد العمليات في الوقت الحالي كما في الصورة التالية...

     
    الآن بعد اكتمال التنزيل ستزول النقطة السوداء و ستظهر لك مجموعة من التعليمات تخبرك باكتمال التنزيل كما في الصورة التالية:

    الآن يمكنك أن تتعامل مع المكتبات الموجودة و تنزيلها إلى مشروعك.
     
     
    تنزيل المكتبات
    في الجزء التالي من الدرس سنقوم بتنزيل إحدى المكتبات على مشروع xcode و سنختار JTAppleCalendar و هي تستخدم لإنشاء تقويم و استخدامه في مشروعك، و لكن لن نتحدث عن طريقة إنشاء التقويم و التعامل معه و إنما الغرض شرح طريقة تنزيل المكتبات على مشروعك و استخدام الcocoa pod لذلك يمكنك اختيار أي خيار آخر لتنزيله. و الاختيارات يمكن الوصول إليها من عدة مصادر أهمها و أشهرها الgithup حيث يمكنك البحث هناك عن كل ما تريد.
    و هنا رابط المكتبة التي سنقوم بتنزيلها إلى المشروع :
    https://github.com/patchthecode/JTAppleCalendar
    إذا لنبدأ..
    في البداية قم بإنشاء مشروع xcode جديد و سمه demo مثلا و قم بحفظه على سطح المكتب.
    -- صورة
    الآن قم بفتح الterminal..
    نريد في البداية الوصول إلى موقع المجلد الذي يحوي كل ملفات المشروع لتنزيل المكتبة في ذلك المكان و لذلك يجب علينا استخدام أمر cd ثم إضافة المسار الخاص بالمشروع للوصول إليه من خلال الterminal. ولكن يمكن استخدام طريقة أسهل من عمل ذلك يدويا و هي :
    ١- قم بكتابة cd ثم مسافة
    ٢- قم بسحب المجلد من سطح المكتب و أفلته في الterminal و ستجد أن الterminal قام بإضافة مسار المجلد تلقائيا
    ٣- اضغط enter أو return 
    الآن تم الوصول إلى داخل المجلد و يمكنك التأكد من ذلك عن طريق استعراض الملفات بداخل المجلد بالضغط على ls و ستجد أن الterminal يعرض لك ملفات المشروع الخاصة بك.
    الآن قم بكتابة الأمر التالي على الterminal :
    pod init 
    ثم اضغط enter 
    - هذا الأمر خاص بإنشاء ملف باسم pod file و هو الملف الذي يجب أن يحوي كل المكتبات التي تريد استخدامها في مشروعك. و للتأكد من أن الملف قد تم إنشاؤه قم بإدخال أمر ls مرة أخرى و يجب عليك أن تجد ملف جديد باسم Podfile أو يمكنك فتح المجلد يدويا و ستجد ذلك الملف بالداخل.
    - الآن قم بفتح ملف الpodfile حيث سنقوم بإضافة بعض الأسطر بداخله.
    - الآن عليك إضافة أسماء المكتبات التي ستقوم باستخدامها داخل هذا الملف. لكن من أين نحصل على اسم المكتبات ؟ عليك دائما أن تعود إلى التوجيهات المكتوبة من مصدر التنزيل ذاته للحصول على هذه المعلومات، لذلك قم بالعودة إلى الرابط مرة أخرى
    https://github.com/patchthecode/JTAppleCalendar
    دائما عند تنزيل المكتبات لابد من البحث على خانة الinstallation بطريقة الcocoa pod لذلك توجه إلى خانة installiation via cocoa pod و ستجد أنه يشير إلى أمر تنزيل الcocoa pod حيث قمنا بذلك مسبقا.
    و بعد ذلك يريك المحتويات التي يجب أن تكون بداخل ملف الPodFile لذلك انسخ منها الأشياء التي لا يحويها الملف الموجود عندك و هي التالية : 
    source 'https://github.com/CocoaPods/Specs.git' platform :ios, '10.0' use_frameworks! و هذه التعليمات في الأعلى ضعها في بداية الملف
     
     
    pod 'JTAppleCalendar', '~> 7.0' و هذه ضعها بدلا من : #Pods for demo  
     
    أخيرا ملف الpodFile يجب أن يبدو كالتالي :
    # Uncomment the next line to define a global platform for your project source 'https://github.com/CocoaPods/Specs.git' platform :ios, '10.0' use_frameworks! target 'demo' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! pod 'JTAppleCalendar', '~> 7.0' target 'demoTests' do inherit! :search_paths # Pods for testing end target 'demoUITests' do inherit! :search_paths # Pods for testing end end مع ملاحظة النقاط التالية :
    ١- أي سطر يبدأ ب# فهو كالcomment في البرمجة لا تأثير له و لذلك لا مشكلة في حال وجود سطور إضافية من هذا النوع في ملفي أو ملفك.
    ٢- لا مشكلة إذا لم يكن ملفك يحوي السطور التالية :
    target 'demoTests' do inherit! :search_paths     # Pods for testing   end   target 'demoUITests' do     inherit! :search_paths     # Pods for testing   end  
    ٣- قد يكون هناك اختلاف في الاسم إذا لم تقم بتسمية مشروعك باسم demo و لا مشكلة في ذلك.
     
    - الآن أصبح ملف الpodfile جاهزا كل ما عليك فعله هو حفظ التغييرات التي قمت بإجرائها عليه ثم إغلاقه.
    - الآن تبقت خطوة أخيرة.. قم بالعودة للterminal و تأكد أنك ما زلت في مسار المشروع و قم بإدخال : 
    pod install 
    و اضغط enter 
    سيبدأ الآن الterminal بتحميل المكتبات التي طلبتها و عندما يكتمل التحميل سيشعرك بذلك كما في الصورة..

     
    الآن اذهب إلى ملف الxcode الذي كنت تعمل به و قم بإغلاقه ثم توجه إلى مجلد المشروع الخاص بك و ستجد أن هناك ملف xcode جديد ظهر لديك في المجلد..

    حيث أن هذا هو نفس مشروعك الذي كنت تعمل به و لكن الفرق الوحيد أنه يضم كل المكتبات التي طلبت تنزيلها.
    و الآن لتتأكد أن التنزيل تم بطريقة صحيحة.. قم بفتح الملف الجديد و اذهب إلى أي كلاس و في أعلا الملف قم بكتابة أمر :
    import JTAppleCalender إذا لم يظهر البرنامج أي مشكلة فهذا يعني أنك قمت بتضمين المكتبة في مشروعك بنجاح.
     
    ملاحظة : قد يظهر الxcode خطأ في البداية عند كتابتك لهذا الأمر.. حينها قم بمسحه و قم بعمل build عن طريق cmd+b ثم اكتب الأمر مرة أخرى و يجب أن يعمل هذه المرة
     
    إلى هنا نصل إلى نهاية الدرس و إن كنت تود تجربة عمل الcalender و استخدام المكتبة فقم بمتابعة الفيديوهات الموجودة في رابط تنزيل المكتبة و قراءة التوجيهات الموجودة هناك.
     
    إلى اللقاء في دروس قريبة قادمة بإذن الله

    مستوى المقال: متوسط
  2. بسم الله الرحمن الرحيم
    السلام عليكم ورحمة الله وبركاته
     
    في هذا الدرس سنتعرف على احد أهم الأنواع في الSwift الا وهما Structures and Enumerations.
     
    أولا: Structures أو struct                                                                                                                                                                           
    في Swift او اي لغة برمجة مشابهة، تعتبر الكلاسات حجر الأساس لمبدأ الObject-Oriented Programming.
    بالإضافة الى الكلاسات، لدينا الStructures التي تعطينا بديل مشابه للكلاسات.
    الستركتشر مشابه للكلاس، لأنه يمكن أن يحتوي على methods, properties, initializers تماما كما الكلاس، ولكن الفارق الأساسي هو ان الستركتشر يعتبر من نوع Value، والكلاسات تعتبر من نوع Reference.
     
    ما معنى Value type و Reference type؟

                                                                                      صورة توضيحية للفرق بين الRegerence والValue
    جميع الأنواع في الSwift هي اما عبارة عن Value type او Reference type. المتغيرات من نوع القيمة (Value type) -مثل متغيرات الInt والBool-، تقوم بنسخ قيمتها عندما يتم مساواتها بمتغير آخر
    بينما متغيرات نوع المرجع (Reference type) -مثل أي اوبجكت من كلاس-، تقوم باعارة قيمتها عندما يتم مساواتها بمتغير آخر. 
    لنشرح الفرق بينهم سنطرح هذا المثال البسيط لنوع القيمة:
    var a: Int var b: Int a=5 b=a a=10 print(a) print(b) في المثال السابق، سيتم طباعة قيمتين مختلفتين لكل من a=10 و b=5، لأن قيمة a نسخت في البداية الى b حتى أصبح كل متغير يحمل قيمة خاصة ومستقلة عن المتغير الآخر، وعند تغيير قيمة a لم تتأثر قيمة b بذلك.
    ولنوع المرجع لنرى المثال التالي:
    var a = Car() var b: Car b = a a.startCar() b.printCarStatus() في المثال السابق، اذا طبقنا هذا الكود فسنلاحظ أن متغير السيارة b يعمل بالفعل، أي أن قيمة a اعيرت الى b، بحيث أي تغيير يطرأ على a سيؤثر بb، وأي تغيير يطرأ على b سيؤثر بa.
     
    اذا من المهم جدا أن نتذكر بأن الStructures من نوع القيمة (Value type)، بينما الClasses من نوع المرجع (Reference type).
     
    اختلاف آخر صغير بين الستركتشرز والكلاسات، أن الproperties في الستركتشر لا يمكن تعديلها بطريقة مباشرة من الميثودز، بل يجب استعمال كلمة mutator لكل ميثود كي نستطيع تعديل الproperties.
    مثال:
    struct Circle { var centerX = 0.0, centerY = 0.0, radius = 1.0 mutating func doubleRadius() { radius = radius * 2 } } اذا أردنا مضاعفة قيمة الradius من داخل الفنكشن doubleRadius، فسنضطر لاستخدام كلمة mutator قبل الميثود، دلالة على تغيير المتغير radius بقيمة أخرى دون الحاجة لنسخه.
     
    فيما عدا ذلك، فإن الستركتشرز تعتبر مشابهة تماما للكلاسات.
     
    ثانيا: Enumerations او enum                                                                                                                                                                    
    التعدادات (Enumerations) هي طريقة نستطيع من خلالها أن نجمع العديد من قيم المتغيرات المرتبطة ببعضها. وكما الستركتشرز، فان التعدادات تعتبر من انواع القيمة (Value type).
    لنفترض بأننا نريد أن نعرف مجموعة الكواكب الشمسية، بدون استخدام التعدادات سنقوم بكتابة كود مشابه للتالي:
    let mercury = 1 let venus = 2 let earth = 3 ... وبهذه الحالة سنضطر لاضاعة أسطر أخرى للتأكد من الادخال الصحيح، فمثلا لو أدخل احدهم الرقم -1:
    var currentPlanet = -1 // not a valid planet! فسيكون هناك خطأ أنتاء عمل البرنامج، ان لم نقم بكتابة if-statements مناسبة
    ولكن في حالة التعدادات، يمكننا كتابتها كالتالي:
    enum Planet { case mercury = 1 case venus = 2 case earth = 3 case mars = 4 case jupiter = 5 case saturn = 6 case uranus = 7 }  او اختصارا:
    enum Planet { case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune } والآن، ان تم ادخال قيمة غير صحيحة:
    var currentPlanet = Planet.mercury currentPlanet = -1 // error! فسنتعرف على الخطأ مباشرة.
     
    في هذا المثال سنرى امكانية تخزين قيم مختلفة لكل case لاحقا، الآن لننشئ مجموعة الألوان الخاصة بنا:
    enum Color { case rgb(Int, Int, Int) case argb(Int, Int, Int, Int) case cmyk(Int, Int, Int, Int) case name(String) } عند كتابة الenum بهذه الطريقة، فكأننا نخبر الSwift بأن كل case ستحصل على قيم خاصة بها لاحقا، مثل:
    var blue = Color.rgb(0, 0, 255) ويمكن استعمال الاختصار التالي عند تغيير قيمة المتغير:
    var blue = Color.rgb(0, 0, 255) blue = .argb(100, 0, 0, 255) blue = .name("blue") فبعد المساواة الأولى، أصبح من المعروف أن المتغير blue لن يخرج عن أحد خيارات المتعدد Color.
     
     
    وهنا نصل الى ختام موضوعنا، تقبل الله منا ومنكم صالح الأعمال
    والسلام عليكم ورحمة الله
    مستوى المقال: متوسط
  3. تعلمنا في ما سبق عن  نوع من  الـ 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)              }
    صورة توضيحية بالنتيجة :



     
    مستوى المقال: متوسط


  4. البعض اخبرني بأنه يرغب بأمثله في الـ 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

    فقم باتباع الخطوات التالية :
     




    يمكن تحميل الكود من هنا 
    مستوى المقال: محترف

  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 عن نوعين من الـ 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 بشكل جميل  
    شاهد الصورة التالية :



     
    مستوى المقال: محترف
  7. السلام عليكم ..

    كما تعلمون ابل في تحديث 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 

    اخيراً :
    قم بتشغيل البرنامج وسوف تلاحظ امكانية تغيير ايقونة التطبيق

    ستظهر رسالة تفيد بعملية التغير كهذه :


     

    يمكن تحميل المشروع من هنا 
    مستوى المقال: محترف
  8. البرنامج المستخدم : xcode8
    اللغة المستخدمة : swift3
     
    السلام عليكم..
    في هذا الدرس سنتكلم عن الoptional في لغة سويفت. ماهي؟ و كيف و تستخدم ؟ و لماذا ؟ 
    و للتطبيق على هذا الدرس لن تحتاج سوى للplayground و لن تحتاج لإنشاء مشروع جديد..
    لذلك قم بفتح برنامج الإكس كود و قم باختيار : get started with a playground
     
    ما هو الoptional ؟
    لنتعرف أكثر على ماهية الoptional قم بإضافة السطر التالي إلى الplayground :
    var x : Int = 5 لقد قمت بتعريف متغير باسم x و من نوع Int و قمت بإسناد القيمة 5 إليه..
    لكن هل أنت بحاجة لإسناد قيمة للمتغير بمجرد إنشائه ؟ ماذا إن كنت لاتريد وضع قيمة ابتدائية للمتغير ؟ ماذا إن كنت غير متأكد من أن المتغير x يجب أن يحوي قيمة أصلا؟؟ 
    مثلا: تخيل أنك تقوم بعمل موقع و حتى يقوم المستخدم بالتسجيل فيه عليه أن يضيف بياناته، هناك بعض البيانات المهمة التي يجب أن يضعها المستخدم كالاسم و الإيميل.. الخ لكن لنفرض أن أحد هذه البيانات هو رقم الجوال، عادة و في أغلب المواقع رقم الجوال ليس إجباريا و يمكن للمستخدم أن يضعه باختياره، و بالتالي أن كانت القيمة x تمثل رقم الجوال فليس من الضروري أن تحوي قيمة فبعض المستخدمين سيقومون بإضافتها و الآخرون لا..
    و هنا تكمن أهمية الoptional حيث تتيح فرصة للمتغير أن يحوي قيمة معينة أو أن يكون بلا قيمة ( تكون له قيمة nil )
    الآن عد إلى الكود الذي كتبته و جرب أن تمسح القيمة التي أسندتها للمتغير ليبدو بهذا الشكل :
    var x : Int ثم قم بكتابة x السطر الذي يليه لتعرض قيمتها على الشريط الجانبي 
    ستجد أن البرنامج يعرض لك خطأ نصه : variable 'x' used before being initialized
    و هذا يعني أن المتغير x يتم استخدامه قبل أن يتم إنشاء قيمة خاصة فيه حيث أن من شروط لغة سويفت في أي متغير هو أن يحوي قيمة قبل أن يتم استخدامه أصلا لذا لابد من وجود قيمة تعني عدم وجود قيمة للمتغير كحل لهذه الحالة  
    و يمكن استبدال هذا القيمة مستقبلا بقيمة من نوع المتغير ( Int في هذه الحالة ) 
    لعل الأمور تبدو معقدة بعض الشيء لكن ستتضح مع إكمال الدرس..
    أحد القيم التي يمكن إسنادها للمتغيرات بأنواعها المختلفة هي قيمة nil و التي تعني ( لا شيء ) و هي التي تقابلها null في لغات أخرى كالجافا مثلا
    و هذه القيمة تكمن أهميتها في:
    -أنها تعطي قيمة ابتدائية للمتغير و بالتالي يمكن استخدام المتغير دون أخطاء.
    -أن هذه القيمة هي ليست Int و لا String و لا Double... الخ بل هي قيمة مميزة تعطينا إشارة أن المتغير الذي يحملها لا يحوي قيمة.
    - أن هذه القيمة يمكن استبدالها مستقبلا بقيمة أخرى حسب نوع المتغير. 
     
    الآن قم بتحويل تعريف المتغير x ليبدو كالتالي :
    var x : Int = nil لاحظ أن البرنامج سيظهر لك خطأ مرة أخرى لكن لمَ هذا الخطأ ؟ ألم نذكر أن هذه القيمة يمكن أن يحويها متغير من أي نوع ؟ إذا مالمشكلة ؟
    صحيح أن أي متغير من أي نوع يمكن أن يحوي هذه القيمة، و لكن لن تكون له إمكانية الحصول على هذه القيمة إلا عندما تعطيه هذه الإمكانية عند تعريف هذا المتغير بإضافة (؟) بجانب نوعه عند تعريفه ليبدو كالتالي..
    var x : Int? = nil و الآن يمكنك أن تطلق على المتغير x اسم optional و هي تعني اختياري أي بمعنى آخر : لك الخيار في تحديد ما إذا كان الx يساوي قيمة حقيقية من نوع Int أو لا يحوي قيمة أصلا( يساوي nil ) و هذا بفضل علامة الاستفهام التي قمنا بإضافتها بجانب المتغير.
    و الآن نوع المتغير x لم يعد Int و إنما أصبح Int? أو optional Int و يمكنك التأكد من النوع عن طريق الضغط على alt و اسم المتغير وسيظهر لك نوعه
    و الآن: قم بتغيير x إلى قيمة 5 مثلا في السطر الذي يليه ثم إلى قيمة nil في السطر الذي يليه ثم إلى قيمة 10 مثلا و لاحظ أن ذلك يتم دون مشاكل و دون أخطاء  
    x = 5  x = nil  x = 10  x = nil و الآن قم بجعل x = أحد قيم الint و لتكن ١٠ على سبيل المثال ثم قم بطباعة قيمة المتغير x
    x = 10 print(x) لاحظ أن البرنامج لم يقم بطباعة قيمة x و إنما قام بطباعتها بداخل كلمة Optional بالشكل التالي :
    Optional(10)  صحيح أن x هي الآن تساوي القيمة ١٠ لكنها ما زالت قيمة من optinal Int و ما زال طابع الoptional يغطي هذا المتغير
    و حتى تقوم بتخليصها من غطاء الoptional الذي يحويها يمكنك أن تستخدم ما يعرف ب : force unwrapping 
    و هو تخليص المتغير من غطاء الoptional في مرحلة معينة أو في سطر محدد من البرنامج و ذلك عن طريق استخدام علامة التعجب بجانب اسم المتغير، قم بالرجوع إلى أمر الطباعة و قم بكتابته بإضافة علامة التعجب للمتغير كالتالي :
    print(x!) الآن بخلاف المرة السابقة سيقوم البرنامج بطباعة 10.
    لماذا؟ لأنك قمت برفع غطاء الoptional من x و هو ما يعرف بال(unwrapping) فأصبحت تحوي قيمة (Int) بدلا من (Int?) أو (optional Int) فأصبحت كمتغير Int عادي في هذا السطر من الكود.
    إن رفع الغطاء الذي قمت بعمله لن يطبق على x إلا في ذلك السطر و أما في باقي السطور فأن x ستبقى optional Int. جرب أن تطبع x في السطر التالي لتتأكد من ذلك  
    و الآن لنفرض أنك ستقوم باستخدام x عدة مرات خلال برنامجك و في كل هذه المرات لا تريد أن تعامل x معاملة الoptional بل تريد أن تقوم بعمل unwrapping لها في كل مرة دون أي استثناء فمالحل ؟
    قبل الإجابة قد تتساءل: مالفائدة من جعل x لتكون optional عند تعريفها إذا كنا سنرفع عنها غطاء الoptional في كل مرة نستخدمها فيه في الكود ؟
    و الإجابة على ذلك: قد تحتاج في بعض الحالات إلى البدء بقيمة nil للx في البداية فقط و لكن بعد ذلك ستكون متأكدا أن الx لن تساوي nil أبدا بعد هذه المرحلة و لذلك ستحتاج لعمل unwrapping في كل مرة.
    أو لتفادي عمل unwrapping و كتابة ! في كل مرة يمكنك استخدام علامة التعجب عند تعريف المتغير كالتالي :
    var x : Int! = nil و هي تعني : أن المتغير x سيكون optional و سيساوي nil في البداية و لكن متى ما تم استخدامه فسيرفع عنه غطاء الoptional و سيعامل كمتغير Int يحوي قيمة دون الحاجة لكتابة علامة التعجب.
    و للتأكد من ذلك قم بإسناد قيمة لx و من ثم قيم بطباعة القيمة
     
    x = 4 print(x) و ستجد أن البرنامج يقوم بطباعة 4 دون ظهور كلمة optional
     
     
    في الحقيقة..
    إن استخدام رفع الغطاء عن متغيرات الoptional بهذه الطريقة يعتبر طريقة سيئة و خطيرة في كتابة الكود و سنوضح خطر كتابة الكود بهذه الطريقة و الحل المستخدم لتفادي ذلك في الدرس القادم باذن الله
    إلى ذلك الحين استودعكم الله    
     
    مستوى المقال: متوسط
  9. قد تكون مبرمجاً بارعاً ومتمكناً ولكن تطبيقاتك لا تجذب المستخدمين ! .. لماذا ؟
    بداية تذكر بأن هناك ألاف المطورين حول العالم ، فالمنافسة على اشدها !
     
    هناك امرين مهمين، غير البرمجة وخلو او قلة المشاكل (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

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

     
    مستوى المقال: محترف
  10. البرنامج المستخدم : xcode 8
    اللغة المستخدمة : swift 3 
    هذا الدرس هو الجزء الثاني من درس الtableview مع الـ Swift 
    إذا كنت لم تشاهد الجزء الأول فقم بمشاهدته الآن من خلال الرابط التالي :
    في الدرس السابق وصلنا في النهاية إلى هذه النتيجة..

    و هي تمثل جدول أو tableview يحوي أسماء الطلاب، و في هذا الدرس سنقوم بتطوير هذا الجدول و تحسينه ليحوي بيانات أكثر عن كل طالب و بالشكل الذي تختاره أنت كمطور. 
    إذا لنبدأ..
    الآن إضافةً لعرض أسماء الطلاب نريد عرض بيانات خاصة بهم كالعمر و رقم الجوال و سنقوم بعرض هذه البيانات في الجدول.
    و بالتالي فأن كل طالب سيكون له ٣ بيانات مختلفة متصلة به و خاصة به و هي الاسم و العمر و رقم الجوال بدلا من الاسم وحده.
    و بالتالي كيف يمكن أن نربط مجموعة من البيانات لشيء واحد فقط ؟
    لو عدنا إلى الarray التي تمثل مصدر البيانات أو الdata source للtableview و التي تحمل اسم students فسنجد أنها تحتوي على أسماء الطلاب فقط و بالتالي هي array من نوع string الآن نحن بحاجة إلى أن نربط كل اسم من هذه الأسماء مع العمر و رقم الهاتف لكل عنصر من العناصر و كأن هذه العناصر ستستبدل بمجموعة تحوي هذه البيانات معا؟
    لو فكرنا قليلا.. ما هي الوسيلة المتاحة في swift أو في معظم لغات البرمجة التي تمكنك من ربط عدة بيانات في مجموعة واحدة ؟
    الحل ببساطة هو الclass سنقوم بإنشاء كلاس خاص باسم student و سنقوم بوضع الخصائص الخاصة به معا في كلاس واحد :
    ١- قم بالذهاب إلى file ثم new ثم file   
    ٢- اختر swift file و اضغط على next 
    ٣- قم بتسميته Student و اختر create 
    ملاحظة : من المعتاد دائما البدء بحرف capital لجميع أسماء الكلاسات في المشروع
    الآن قم بأضافة الكود التالي إلى الملف : 
    //1: class Student { //2: var name:String! var age:Int! var phoneNum:Int! //3: init(name:String,age:Int,phoneNum:Int) { self.name = name self.age = age self.phoneNum = phoneNum } } في الخطوة الأولى قمنا بإنشاء الكلاس Student 
    و في الخطوة الثانية قمنا بإضافة المتغيرات الخاصة بهذا الكلاس و هي الاسم و العمر و رقم الجوال
    و أخيرا في الخطوة الثالثة قمنا بعمل الinitializer و هي الدالة التي ستستخدم لعمل كائنات أو objects من هذا الكلاس حيث ستستقبل الاسم و العمر و رقم الهاتف لتقوم بعمل أوبجكت جديد يحوي هذه المتغيرات.
     
    و الآن قم بالعودة إلى ViewController و قم بتغيير نوع الarray من String إلى Student و قم ببناء العناصر الجديدة لتكون من Students و لتبدو الarray بهذه الطريقة :
    //1 //var students : [String] = ["محمد","خالد","عمر","أحمد","سامي"] //2 var students : [Student] = [Student(name:"محمد",age:14,phoneNum:054050433), Student(name:"خالد",age:15,phoneNum:054384953), Student(name:"عمر",age:14,phoneNum:054837495), Student(name:"أحمد",age:17,phoneNum:054333874), Student(name:"سامي",age:16,phoneNum:0542030245) ] في الخطوة الأولى قمنا بتحويل الشكل القديم للarray ليكون على شكل comment ( بإمكانك مسحها نهائيا كذلك ) 
    و في الخطوة الثانية قمنا بعمل الarray الجديدة و التي تحوي objects من الStudent class و كل أوبجكت فيها له خصائص الاسم و العمر و رقم الجوال.
     
    و الآن نحن بحاجة لقراءة هذه البيانات و وضعها في الtableview.
    في الشكل السابق للarray كنا فقط بحاجة المتغير الذي يحمل اسم textLabel و الموجود بداخل الcell الخاصة بالtable view حيث نقوم بإسناد قيمة اسم الطالب لهذا المتغير.. لكن المشكلة أن هذا مجرد متغير واحد و نحن بحاجة إلى ٣ متغيرات في كل cell لتحوي الاسم و العمر و رقم الهاتف إذا مالحل ؟
    إن الcell أو الخلية التي قمنا باستخدامها هي في الحقيقة الخلية الأساسية في البرنامج و التي لا توفر لك كثير من الخيارات لذا نحن بحاجة إلى عمل cell خاصة بنا و تحوي المتغيرات التي تناسب البرنامج الذي نعمل عليه و سنقوم باستخدامها بدلا من الcell الأساسية.
    و حتى تقوم بإنشائها :
    ١- اذهب إلى file ثم new ثم file
    ٢- هذه المرة سنقوم بعمل cell خاصة بنا لكنها في النهاية ليست إلا cell ترث الخصائص الأساسية من TableViewCell و هو يمثل الكلاس الخاص بالcell الأساسية في الxcode و التي قمنا باستخدامها سابقا ( و بما أننا سنحتاج إلى الوراثة هنا فلن نقوم باختيار swift file و إنشاء كلاس عادي و إنما سنختار cocoa touch class ) ثم اضغط next 
    ٣- في مربع subclass of : قم بتحديد UITableviewCell و قم بتسميتها StudentCell و اختر next ثم create
    ٤- الآن هذا هو الكلاس الذي سيمثل الcell الجديدة و الذي سيمثل الأكواد التي ستكتب فيها.. لكن أين هي هذه الcell الجديدة في الواجهة ؟
    توجه إلى Main storyboard و قم بتحديد الخلية الموجودة في الtableview
    ملاحطة : إذا لم تكن تظهر لك أي خلية في الtableview فقم بتحديد الtableview و إضافة واحدة من شريط الخصائص كما هو موضح في الصورة التالية :

    و اجعل هذه الخلية ترث من ال class ( انظر إلى الصورة ) الذي قمنا بعمله للتو و هو StudentCell و ذلك من خلال اختيار شكل المربع ( show the identity inspector ) و من ثم كتابة الاسم في خانة class و بذلك ستمثل هذه الخلية نموذج لشكل كل الخلايا التي سيتم إنشاؤها في الtableview و ما سيجري على شكلها و تصميمها سيجري على كل الخلايا الأخرى في الtableview

    و لكن حتى تستطيع الوصول إلى هذه الخلية دون غيرها في الكود الخاص بك فأنت بحاجة لإضافة ID خاص بهذه الخلية ليميزها عن غيرها و هذا يتم من خلال تحديد الخلية ثم توجه إلى شريط الخصائص في الجانب الأيمن ستجد خاصية باسم : identifier قم بكتابة الid الذي تجده مناسبا لك مثل : studentCellId على سبيل المثال و لكن تذكره جيدا لأنك ستحتاجه وقت كتابة الكود الخاص بهذه الخلية

     
    و الآن قم بتصميم العناصر التي ستحمل بيانات الطلاب و هي تتم بكل بساطة عن طريق إضافة 3 labels للخلية و كل منها سيمثل أحد البيانات
    ملاحظة : قم بإضافة الأشكال ( labels ) و توزيعها بالطريقة التي تناسبك و لا تنس إضافة الconstraints و إذا أردت إضافتها بسرعة دون الدخول في تفاصيل الconstraints فقم بتحديد العناصر ثم قم بإضافتها كما هو موضح في الصورة :
     
     
     
    الآن نحن بحاجة لربط الlabels الموجودة في الخلية مع الكلاس الخاص بها حتى تكون لنا قدرة التحكم بها و بقيمها لذلك قم بفتح الكود في نصف الشاشة عن طريق الضغط على أيقونة الدائرتين الموجودة في الزاوية اليمنى من الشريط العلوي
    تأكد أن الملف المفتوح في النصف الخاص بالكود هو كلاس الStudentCell و إذا لم يكن فتوجه إلى الكلاس من العمود الأيسر الخاص بالملفات و اضغط على alt ثم اسم الكلاس و هو StudentCell  
    توجه الآن إلى أول label و هو الخاص بالاسم و قم بالضغط على ctrl و السحب من الlabel إلى داخل الclass و ستظهر لك نافذة تسأل عن الاسم كما في الصورة 
    قم بتسميته nameLabel و اضغط على connect 
    قم بتكرار نفس العملية للlabel الخاص بالعمر و رقم الهاتف و قم بتسميتهم ageLabel و phoneNumLabel على التوالي.
    و سيكون الشكل و الكود بهذا الشكل : 

     
    أخيرا تبقت خطوة أخيرة و هي إضافة القيم المناسبة لكل label في كل خلية و هذا سيتم من خلال الfunction التي تحمل اسم cellForRowAtIndexPath و الموجودة في الكلاس ViewController
    لذلك توجه إليها و قم بمسح الكود السابق الموجود بداخلها و استبدله بالكود التالي :
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //1 let cell = tableView.dequeueReusableCell(withIdentifier: "studentCellId") as! StudentCell //2 cell.nameLabel.text = students[indexPath.row].name cell.ageLabel.text = String(students[indexPath.row].age) cell.phoneNumLabel.text = String(students[indexPath.row].phoneNum) return cell } في الخطوة الأولى : قمنا باستخدام الtableview و هو المتغير الذي يمثل الtableview الموجود في الstoryboard و الذي يحوي الخلية و الذي قمنا بإضافته في الدرس السابق و قمنا من خلاله باستدعاء function اسمها dequeueReusableCell  و التي تستخدم لجلب الcell من خلال الid الخاص بها و لذلك قمنا بإضافة الid الذي قمنا بإنشائه سابقا إلى الخانة الخاصة به كparameter للfunction ثم أضفنا : as! StudentCell حتى نقوم بعمل casting لهذه الcell لتصبح من نوع StudentCell 
    ملاحظة : إذا كنت لا تعلم ما هو الcasting فيمكنك تجاهله الآن و إضافة الكود كما هو و تعلمه لاحقا 
    و الأن أصبح لدينا cell من نوع StudentCell بمعنى آخر : يمكننا الآن الوصول إلى الlabels الموجودة في الStudentCell و تغيير قيمها اعتمادا على قيم العنصر في الarray و هذا ما قمنا بفعله في الخطوة الثانية و هذا الكود شبيه جدا للكود السابق الذي كان خاصا بإضافة الاسم فقط في كل indexPath من الarray باستثناء إضافة الخصائص الخاصة بكل عنصر للarray و ذلك لأن الarray الآن تحتوي على objects و أنت تريد الوصول إلى متغيرات هذا الobjects فتضيف .name للوصول للاسم مثلا و كذلك الأمر للعمر و رقم الهاتف
    لاحظ كذلك أننا قمنا بتحويل العمر و رقم الهاتف إلى قيم من نوع String و ذلك لأن القيم الأساسية لها هي في الحقيقة من نوع Int و لابد من تحويلها إلى String لتناسب نوعية المتغير text بداخل الLabel
    الآن قم بتشغيل البرنامج لتظهر لك بهذا الشكل حيث يظهر الخلية و هوي تحوي عدة بيانات متعلقة بالطالب بدلا من الاسم فقط  :

     
    إلى هنا نصل معكم إلى نهاية الدرس و أتمنى أن يكون ذا فائدة و نفع للقارئ الكريم
    و إلى اللقاء في دروس أخرى   
     
    مستوى المقال: متوسط
  11. في هذا الدرس سنتعلم كيفية التعامل مع الtableview مع الـ Swift و كيفية إنشائه و بنائه داخل تطبيقك.
    في البداية ما هو الtableview ؟ 
    الtableview هو أداة من أدوات واجهة المستخدم التي تستخدم لعرض البيانات من خلالها. و تحديدا تستخدم في عرض البيانات التي تكون على شكل مجموعة عناصر مثل قائمة بمحتويات مطعم أو أسماء لطلاب في مدرسة..الخ 
    و الحقيقة أنه لا يكاد يتواجد تطبيق في متجر البرامج دون وجود tableview بما في ذلك التطبيقات الشهيرة كتطبيق facebook فكل البوستات الموجودة في الfacebook و التي تشاهدها في الصفحة الرئيسية عند فتح التطبيق ما هي إلا شرائح من هذا الtableview و كذلك في twitter و instegram... الخ.
    و لذلك يعتبر الtableview أحد أهم الأدوات الموجودة في برنامج الxcode  و من أكثر الأدوات استخداما في بناء التطبيقات إن لم يكن أكثرها لذلك من المهم لمبرمج التطبيقات أن يتعلم طريقة إنشائه و التعامل معه في التطبيق و ما سنقوم بعمله في هذا الدرس هو عرض قائمة لأسماء طلاب في الtableview و لذلك فلنبدأ...
     
    في البداية قم بفتح الإكسكود و قم بعمل باختيار create new xcode project ثم single view application و قم بتسميته و حفظه.

     

     
     
    الآن قم بالتوجه إلى الMain.storyboard لإضافة الtableview إلى تطبيقك.
    لإدراج الtableview توجه إلى مكتبة العناصر الموجودة على اليسار و قم بالبحث عن tableview كما ثم اسحبه إلى الشاشة كما هو موضح في الصور.
    ملاحظة: تأكد من سحبك لعنصر الtableview و ليس لعنصر الtableview controller  

     
     
     
    الآن قم بتعديل حجم الtableview ليملأ الشاشة بأكملها 

     
    قم بإضافة الconstraints كما هو موضح في الصورة
    إذا كنت لا تعلم ما هي الconstraints فيمكنك تجاهلها الآن، و لكنها باختصار طريقة لإظهار الui أو عناصر واجهة المستخدم كالtableview بشكل مناسب في جميع الشاشات المختلفة الحجم.

     
    الآن تم إضافة الtableview إلي التطبيق يمكنك عمل run و تشغيل التطبيق الآن،
    ستلاحظ وجود جدول محتويات بخلايا فارغة و الآن سنقوم بإكمال العملية لملء هذه الخانات ببيانات معينة: 

     
    إن الview controller الموجودة في الstoryboard و التي قمنا بإضافة الtableview إليها تمتلك ملف برمجي متصل بها يمكنك من كتابة الأكواد الخاصة بهذا الview controller بداخله و حتى تعرف ما هو هذا الملف البرمجي قم بالضغط على الدائرة الصفراء في زاوية الشريط العلوي للview controller ثم قم باختيار المربع من الشريط الأيمن في البرنامج ثم لاحظ خانة الcustom claass ستجد أن الclass أو ملف الكود المرتبط بهذا الview controller هو ملف ViewController و الذي تم إنشاؤه تلقائيا، لاحظ الصورة:


     
    الآن من الشريط العلوي تماما في البرنامج قم باختيار رمز الدائرتين لعرض ملف الكود ViewController بجانب الmain story board 

     
    الآن قم بالضغط على زر ctrl و قم بالسحب من الtableview الذي قمت بإضافته إلى داخل الكلاس على الجانب الآخر و قم بتسميته studentsTebleview أو namesTableview 

     

    الآن أصبح لدينا متغير بداخل الviewController باسم studentsTableview ليمثل الtableview على شكل كود.
    حتى نملأ هذا الtableview بالمحتويات فإننا نحتاج أن نخبر البرنامج أن مصدر هذه المحتويات سيكون هذا الViewController الذي قمنا بإضافة الstudentsTableview إليه، لكن كيف يتم ذلك ؟
    اذهب إلى الfunction التي تحمل اسم viewDidLoad و التي يتم استدعاؤها تلقائيا و يتم تنفيذ الكود الذي بداخلها تلقائيا بمجرد تشغيل البرنامج و اكتب بداخلها :
    override func viewDidLoad() {         super.viewDidLoad()         // Do any additional setup after loading the view, typically from a nib.         studentsTableview.dataSource = self }    
     
    و بهذا تكون قد عرّفت أن مصدر البيانات سيكون هذا الاوبجكت من الكلاس ViewController
    لكن ستلاحظ ظهور خطأ  و هذا الخطأ يظهر بسبب أن نوع المتغير الذي يجب أن يكون مصدرا لبيانات الtableview يجب أن يكون من نوع UITableViewDataSource بينما نلاحظ أن الكلاس ViewController يرث من الكلاس UIViewController و ليس من UITableViewDataSource لذلك لابد أن يرث كذلك من UITableViewDataSource و بالتالي قم بإضافة هذا إلى الكلاس ViewController ليظهر كما هو موضح في الصورة :

    كما تلاحظ ما زال هناك خطأ و هذا الخطأ بسبب عدم كتابة الfunctions التي تتطلبها الوراثة من UITableViewDataSource و عدد الfunctions التي يجب كتابتها هو ٣ دوال رئيسية
    و هم :
    - numberOfSections : و هي التي ستقوم فيها بإرجاع عدد المجموعات لتحوي التصنيفات المختلفة ( و غالبا ما تستخدم مجموعة واحدة فقط )
    - numberOfRowsInSection : و هي التي تستخدم لتحديد عدد الصفوف أو الخلايا في كل مجموعة 
    - cellForRowAtIndexPath : و هي التي تستخدم لتحديد بيانات كل خلية في الtableview 
    و لكتابة هذه الfunctions قم بكتابة العناوين الموضحة في الأعلى و سيقوم الإكمال التلقائي في البرنامج باقتراح خيارات لـإكمالها ثم اضغط على enter حالما تجد الfunction الصحيحة التي تريدها..
     
    سيكون شكل الكود بعد كتابة الfunctions كالتالي :

     
    في الfunction الأولى قم بكتابة 
    return 1 
    لتقوم بعمل مجموعة واحدة للخلايا
    في الfunction الثانية قم بكتابة
    return 7 كعدد مبدئي لإعادة ٧ صفوف في الtableview
    و في الfunction الثالثة كما هو موضح من الfunction أن هذه الfunction يجب أن تقوم بإرجاع قيمة من UITAbleviewCell و هي الشريحة التي سيتم عرضها في الtableview لذلك مبدئيا قم بكتابة  :
    let cell = UITableViewCell()         cell.textLabel?.text = "hello"         return cell  
    و ما قمنا بفعله هنا هو إنشاء cell من نوع UITableviewCell و قمنا بجعل الtext الخاص بها ليحوي كلمة hello و هذا سيطبق على كل الخلايا السبعة في الtableview و بالتالي سيكون شكل الكود كالتالي : 

    الآن قم بتشغيل البرنامج لاختبار الكود
    سيظهر لك ٧ خلايا في جدول محتويات و كلها تحتوي على كلمة hello كما يبدو في الصورة..

    أخيرا! قمنا بعمل tableview يقوم بعرض محتويات قمنا بتحديدها على الcell و لكن تبقى المشكلة في أن هذه المحتويات كلها متشابهة و لا تحوي مجموعة لأسماء الطلاب كما نريد فكيف نقوم بذلك ؟
    عادة عند التعامل مع الtableview لابد أن يكون هناك مصدر للبيانات لأخذ البيانات منه و عادة ما يكون هذا المصدر على شكل array 
    لذلك قم بعمل array من نوع [String] لتحوي أسماء الطلاب و قم بوضع الأسماء كما في الصورة أو كما تريد :
     
    الآن قم بالتوجه إلى الfunction التي تحمل الاسم numberOfRowsInSection و قم بإعادة عدد عناصر الarray بدلا من عدد ٧ العشوائي.
    return students.count
    الآن توجه إلى الfunction التي تحمل اسم cellForRowAtIndexPath و لاحظ أن هذه الfunction تحتوي على بارامتر باسم indexPath.
    في الحقيقة.. هذه الfunction يتم استدعؤها عدة مرات و ليس مرة واحدة و عدد المرات التي يتم استدعاؤها فيه يساوي عدد الصفوف التي قمت بإرجاعها في الfunction ذات اسم numberOfRowsInSection و بالتالي عدد مرات استدعائها سيكون مساويا لعدد عناصر الstudents array في هذه الحالة 
    حيث يقوم البرنامج بكل مرة بإعادة خلية لتمثل البيانات التي سيتم عرضها في ذلك الصف.
    المهم في الأمر أن البارامتر indexPath يحتوي على خاصية باسم row من نوع Int.
    و هذه الخاصية لاتحمل قيمة ثابتة في كل استدعاء للcellForRowAtIndex function و أنما تبدأ بقيمة 0 و في الاستدعاء التالي تزيد بمقدار ١ فتصبح قيمتها ١ ثم تنتقل إلى الاستدعاء التالي لتصبح قيمتها ٢ ثم ٣ ثم ٤... الخ
    حتى تنتهي عند آخر رقم و الذي يساوي حسب ما فهمنا في الأعلى عدد عناصر الstudents array ناقص واحد ( لأن العد سيبدأ من 0 )...
    لعلك بدأت تفهم الفكرة..
    يمكننا أن نعتمد على هذا المتغير لأخذ البيانات من الarray بالترتيب و إسنادها إلى الخلايا... لذلك بداخل الcellForRowAtIndex function قم بتعديل الكود ليكون كالتالي..
    let cell = UITableViewCell()         cell.textLabel?.text = students[indexPath.row]         return cell  
    و شكل الكود كاملا سيبدو كالتالي...

     
    الآن قم بتشغيل البرنامج و انظر إلى النتيجة النهائية
     

     
    إلى هنا نصل إلى نهاية الدرس و سنتناول الموضوع بطريقة متقدمة مستقبلا إن شاء الله.
     



    مستوى المقال: متوسط
  12. اللغة المستخدمة : swift 3
    البرنامج المستخدم : xcode 8
     
    في هذا الدرس سوف نتعلم طريقة تطبيق الsingleton design pattern في برنامج الxcode و في لغة برمجة swift 
    لكن في البداية لمأخذ مقدمة بسيطة عن الdesign pattern ، إذا ما هو الdesign pattern ؟
    الdesign pattern هي طريقة معينة أو نمط معين يستخدمه المبرمج لكتابة الكود البرمجي. لكن لماذا يستخدم هذه الطرق عوضا عن أي طريقة أخرى؟
    -في البرمجة وارد جدا أن تتعرض للمشكلات و أنا لا أتحدث هنا عن مشكلات الoutput فقط و إنما قد تواجه مشاكل معينة متعلقة بالطريقة التي قمت ببناء الكود عليها و هذه المشكلات غالبا ما تتواجد فيها خاصيتين رئيسيتين : 
     أن هذه المشكلات تحدث مع المبرمجين بشكل متكرر .  غالبا عند التعرض لأحد هذه المشكلات فإن إصلاحها سيكون مكلفا بمعنى أن المبرمج سيحتاج إلى إعادة كتابة كمية ليست بالقليلة من الأكواد لإصلاح المشكلة و الوصول إلى حل.  
    و لذلك يتم اسخدام طرق معينة لبناء الكود البرمجي تسمى design patterns.
    لنفرض أنك تعمل على تطبيق آيفون خاص بإدارة حسابات بنكية و لديك class باسم bankManager حيث يستخدم هذا الكلاس لإدارة كل العمليات المتعلقة بالبنك فعلى سبيل المثال يوجد به function خاصة بإيداع الأموال في حساب ما.. التي تتلقى باراميتر للid الخاص بالحساب و تتلقى بارامتر آخر لكمية المبلغ الذي سيتم إيداعه كذلك يحوي هذا الحساب function لسحب المبلغ و أخرى لتحويل المبلغ لحساب آخر.. الخ و بالتالي فإن هذا الclass سيحوي كل هذه الأوامر المهة التي ستستخدم في التطبيق.
    و هذه الأوامر سستستخدمها في كل الclasses التي ستقوم بإنشائها في تطبيقك بمعنى آخر.. ستحتاج لأن يكون لديك object من الكلاس bankManager في كل الclasses لديك في التطبيق لأنك ستحتاج حتما لاستخدام الأوامر المهمة في يحويها هذا الclass في أغلب الclasses في تطبيقك.
    لكن هل إنشاء object من الbankManager في كل الكلاسات التي لديك هي طريقة جيدة للعمل مع هذا الكلاس ؟
    بمعنى آخر هل من المناسب إنشاء عدد من الobjects من كلاس الbankManager أم يجب أن يكون لدينا فقط object واحد مشترك بين كل الكلاسات ؟
    الحقيقة أن إنشاء object واحد فقط مشترك بين كل الكلاسات هي الطريقة المثلى للتعامل مع ال objects من هذا الكلاس و لذلك فإنك بطريقة ما تحتاج لأن تجبر الكلاس على إنشاء object واحد فقط و إرجاع نفس الobject في كل مرة يتم فيها محاولة عمل object جديد من هذا الكلاس. لكن كيف يتم ذلك ؟ ذلك يتم من خلال ما يعرف بالsingleton object أو الsingleton design pattern
    إذا لنبدأ تطبيق ذلك عمليا :
    -في البداية افتح الxcode  قم بإنشاء مشروع جديد (create new xcode project > single view application )   و قم بتسميته Singleton.

     
    - الآن قم بعمل كلاس جديد و قم بتسميته bankManager.
    - في البداية و كما تعلم نحن بحاجة إلى استخدام object مشترك بين كل الكلاسات و لعل كلمة مشترك ستقودك الى التفكير ب static variable، بالفعل سنحتاج إلى عمل static variable لذلك توجه إلى الbankManager و قم بإضافة object من نوع الكلاس نفسه و هو الbankManager و قم بجعله static و لأن هذا الobject سيبدأ بقيمة nil ثم سيكون ذا قيمة من نوعBankManager عندما يتم استدعاؤه لذلك يجب أن يكون هذا الobject من نوع optional لذلك لا تنس إضافة ( ؟ ) في نهاية تعريفه.

     

    - الآن أصبح لدينا أوبجكت مشترك أو يمكن أن يكون مشتركا بين كل الكلاسات لأنه من نوع static. هل هذا يكفي ؟ بالطبع لا.
    من الذي سيحدد فيما إذا كان الobject قد تم إنشاؤه و بالتالي إعادته نفسه أو إنشاء object جديد في حال أن قيمة الobject في الحقيقة تساوي nil لذا هل يمكنك التفكير بالخطوة القادمة ؟ 
    - الحل هو عمل function تقوم بالمطلوب حيث ستقوم هذه الfunction بالتأكد أن الobject قد تم إنشاؤه و في حال كان الobject قد تم إنشاؤه بالفعل فإنها ستعيد هذا الobject bankManager الذي قمنا بإنشائه في الأعلى و إلا فإنها ستقوم بإنشائه إولا ثم إرجاعه بعد إنشائه. ولأن هذه الfunction سوف تقوم باستخدام object من نوع static إذا هي بحاجة أن تكون static كذلك و بالتالي ستكون الfunction كالتالي :
    -ملاحظة: من الشائع في برمجة - بعض الأحيان -  الآيفون تسمية هذه الfunction اسم مشابه لsharedObject أو sharedManager 

     
    - قد يرد إلى ذهنك الآن أننا سنستخدم هذه الfunction دائما إذا ما أردنا الوصول إلى الobject مما يعني أننا لن نصل إلى الobject مباشرة عن طريق استدعائه بشكل مباشر باستخدام BankManager.bankManager و الحقيقة أن استدعاءه بهذه الطريقة قد يسبب بعض المشاكل، مثلا: قد تقوم باستدعائه في أحد الكلاسات بهذه الطريقة و قيمته في ذلك التوقيت تساوي nil و فور محاولتك استخدام اي متغير أو function بداخل الكلاس BankManager فأن البرنامج سيتوقف لمحاولتك استدعاء function لعنصر قيمته في الحقيقة تساوي nil و لحل هذه المشكلة و حتى تقطع أي مجال لاستدعاء الobject بهذه الطريقة المباشرة قم بعمل الstatic object ليكون private و بالتالي سيظهر الكود بهذه الطريقة 

     
    - هل انتهى الأمر تماما؟ في الحقيقة ليس بعد. ماذا إن قمت خطأً بعمل object من كلاس الBankManager بالطريقة العادية لأنشاء Object من الكلاسات في سويفت ؟ أو افرض أنك تعمل في فريق من المبرمجين و قام مبرمج آخر باستكمال العمل من بعدك حينها ستحتاج إلى منعه من عمل object بالطريقة العادية حتى لا تسبب مشاكل أثناء العمل؟ لكن كيف يتم ذلك ؟ كل ما عليك فعله هو أن تجعل الfunction التي تثوم بعمل الobject بطريقة عادية لتكون من نوع private و بالتالي لن يكون بالإمكان استدعاءها خارج الكلاس و سيكون المبرمج مجبرا على استخدام الfunction المسماة sharedObject 
     
    و الآن هل انتهى الأمر ؟ 
    نعم انتهى الأمر هنا و أصبح الsingleton object جاهزا للاستخدام أخيرا.
    بقي الإشارة إلى نقطة مهمة، إن كنت من مبرمجيswift المتمرسين، فلعلك لاحظت أن بعض الclasses التي تقوم باستخدامها هي في الحقيقة تطبق الsingleton pattern و من هذه الكلاسات : 
    NSUserDefaults ---->  var userDefaults = NSUserDefaults.standardUserDefaults()
    UIScreen ----> var mainSecreen = UIScreen.mainScreen
    UIApplication ----> var application = UIApplication.sharedApplication
    -- لاحظ أن الوصول للshared object يمكن أن يكون عن طريق static method كما شرحنا في هذا الدرس و قد يكون عن طريق static variable 
     
    ختاما.. أتمنى أن أكون وفقت في الشرح و إيصال الفكرة و أسأل الله أن يجعله من العلم الذي يُنتفع به  .

    مستوى المقال: متوسط

عالم البرمجة

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