جميع الأنشطة

يتم تحديث محتوى هذا السجل تلقائيا   

  1. اليوم
  2. مقدمة: (LAMP (Linux, Apache, MySQL and PHP هو باكيج لتركيب Apache server, MySQL database and PHP performed on a Linux machine اذاً، لتثبيت برنامج مثل ووردبريس WordPress يحتاج الى سيرفر على جهازك (your local machine) للبدأ بالتطبيق نحتاج اولاً الى تثبيت الLAMP على أجهزتنا ثم ال WordPress كما في الخطوتين التاليتين: أولاً تثبيت LAMP ١. تثبيت Apache 1 sudo apt-get install apache2 ٢. تثبيت MySQL 1 sudo apt-get install mysql-server ٣. تثبيت PHP 1 sudo apt-get install php5 libapache2-mod-php5 ٤. ريستارت Apache Server 1 sudo service apache2 restart الآن قم بفتح متصفحك على URL http://localhost وستظهر لك رسالة ‘It Works’. ثانياً: تثبيت WordPress الآن ال Apache server يعمل في أجهزتنا، سنقوم 1. بتثبيت ال WordPress على ال localhost كالتالي: ١. حمل النسخة الأخيرة من WordPress 1 wget http://wordpress.org/latest.tar.gz ٢. فك الضغط عن ملفاتها 1 tar -xzvf latest.tar.gz ملاحظة: فولدر الWordpress لازم يكون موجود في دايركتري الUbuntu ٢. انشأ قاعدة البيانات والمستخدم ١. لتثبيت الوورد بريس يجب انشاء MySQL ومستخدم admin لديه كافة الصلاحيات للدخول ل MySQL واضافة الاوامر لها 1 mysql -u root -p ٢. قم بتنفيذ الأوامر التالية واحداً تلو الآخر تتبعاً: 1 CREATE DATABASE wordpress; 1 CREATE USER wordpressuser@localhost; 1 SET PASSWORD FOR wordpressuser@localhost= 2 PASSWORD("password"); 1 GRANT ALL PRIVILEGES ON wordpress.* TO 2 wordpressuser@localhost 3 IDENTIFIED BY 'password'; 1 FLUSH PRIVILEGES; 1 exit الآن انت خارج حدود MySQL ، ٣. سنقوم بإنشاء ملف الكونفقريشن wp-config.php - Configuration File - باكيج وردبريس يأتي مع wp-config-sample.php وعن طريق تحرير محتوى هذا الملف، يمكننا إنشاء wp-config.php سنتمكن من ذلك بالخطوات التالية: الخطوة الأولى: 1 cp ~/wordpress/wp-config-sample.php ~/wordpress/wp-config.php نقوم بفتح wp-config.php للتعديل 1 sudo nano ~/wordpress/wp-config.php حدّث قاعدة البيانات ، اسم المستخدم والرقم السري في ملف wp-config.php ثم قم بحفظه واغلاقه. الخطوة الثانية: ١. ننقل فولدر WordPress الى فولدر /var/www/ عن طريق الكوماند التالي: 1 sudo rsync -avP ~/wordpress/ /var/www/ ٢. نقوم بتغيير الpermission في فولدر WordPress كالتالي: 1 cd /var/www/ ٣. نعطي read-write permission to the Apache user ل مستخدم ال Apache 1 sudo chown username:www-data /var/www -R 2 sudo chmod g+w /var/www -R انتهينا الآن.... قم بفتح هذا الرابط على متصفح الانترنت http://localhost/wordpress/wp-admin اذا، وجدت رسالة تنص على خطأ كالتالي : “Your PHP installation appears to be missing the MySQL extension which is required by WordPress“ عد من جديد واضف هذا الأمر 1 sudo apt-get install php5-mysql ريستارت Apache server 1 sudo service apache2 restart اعد فتح الرابط على متصفحك URL http://localhost/wordpress/wp-admin لمشاهدة ما اذا ظهرت صفحة تثبيت ال WordPress. ثالثاً: تثبيت GD Library 1 sudo apt-get install php5-gd أخيراً: للتأكد من ان كل شيء على مايرام ١. تثبيت phpMyAdmin لإدارة الداتا بيس 1 sudo apt-get install phpMyAdmin ٢. نحتاج لتعديل apache2.conf الموجود في فولدر /etc/apache2/ 1 sudo gedit /etc/apache2/apache2.conf ٣. اضف الان هذا السطر في نهاية ملف apache2.conf 1 Include /etc/phpmyadmin/apache.conf ٤. ريستارت ال Apache server 1 sudo service apache2 restart الآن .. لدينا WordPress مثبت على سيرفر LAMP يعمل في اجهزتنا ، ويمكننا تصفح قاعدة البيانات على http://localhost/phpmyadmin شكراً لكم لمياء الشمري
  3. الاسبوع الماضي
  4. بسم الله الرحمن الرحيم مقدمة استعرضنا في الجزء الأول طريقة ارسال البيانات بين الViews عن طريق انشاء Reference للView المستقبلة للبيانات وتغيير قيم متغيراتها. في هذه المقالة، سنتعرف على ماهية البروتوكولات في سويفت وطريقة استخدامها لنقل البينات من أي View الى View اخر. أولا: ماهو البروتوكول لتفهم بالبروتوكول بشكل صحيح، فكر به على أنه طريقة لتعريف ما يمكن للمتغير أن يفعله، عكس الكلاسات التي تعرف ماهية المتغير. ثانيا: نقل البيانات بواسطة البروتوكول في الجزء الأول رأينا كيف نقوم بنقل البيانات بين الView Controllers الى الأمام، والآن سنستعمل البروتوكول لنقل البيانات للView السابقة سنقوم أولا بعمل السيت اب في الStoryboard كالتالي (سنسمي الView الأولى InitialVC والثانية FinalVC): والآن ضمًن الView الاولى بداخل Navigation Controller لتصبح كالتالي: سنقوم بانشاء Segue بين الView الأولى والثانية: قم باضافة زر لكل View كالتالي: لا تنسى اضافة الSegue identifier لأننا سنستعمله لاحقا والآن لننهي السيت أب، أضف الIBActions في الViewControllers لتصبح كالتالي: كل ماسبق كان مجرد تجهيز للتالي سنقوم الآن بتعريف البروتوكول والمتطلبات وانت في كلاس الFinalVC: قم باضافة تعريف للبروتوكول باعلى الصفحة يحتوي على ميثود finishPassing بباراميتر String قم بتعريف متغير delegate من نفس نوع البروتوكول في ميثود الIBAction، قم باستخدام المتغير لنقل البيانات التي تريد نقلها الى الView الأولى (في هذا المثال نقلت سترينق) سيصبح كلاس الFinalVC يبدو كالتالي: 1) أولا قمنا بانشاء بروتوكول (أو blueprint)، يحتوي على الميثود التي سنستعملها لاستقبال البيانات مستقبلا في الكلاس الأول 2) قمنا بانشاء متغير من نفس نوع البروتوكول ليحمل البيانات التي نريد ارسالها 3) عند الضغط على زر العودة، سنزود المتغير بالبيانات التي نريد نقلها الى الView الأولى الى الآن، المتغير delegate لا يعلم الى اي كلاس سيقوم بنقل البيانات التي يحملها معه سنقوم الآن بتبني البروتوكول في الكلاس الأول وتحديده كنقطة الوصول بالنسبة للمتغير وانت في كلاس الInitialVC: اضف البروتوكول الى الكلاس الأول لتبنيه اضف الميثود prepareForSegue واستخدمها لتحديد الكلاس الحالي كنقطة الوصول للمتغير delegate من الكلاس الأخير وأخيرا لتبني البروتوكول بشكل كامل وصحيح، أضف الميثود finishPassing واستخدمها للتعامل مع البيانات المنقولة من المتغير delegate سيصبح كلاس InitialVC يبدو كالتالي: 1) قمنا بتبني البروتوكول مبدئيا في الكلاس الأول 2) في الميثود prepareForSegue (التي سيتم استدعاءها قبل الانتقال الى الView الأخيرة)، قمنا باخبار المتغير delegate بان نقطة الوصول له هو الكلاس الحالي self 3) الى الآن ستلاحظ وجود error على الكلاس، لأنه يتوقع منك اضافة الميثود finishPassing التي ستتعامل مع البيانات التي ستصل من المتغير delegate، أضفناها وبداخلها طريقة للتعامل مع البيانات المنقولة عن طريق الparameter والآن أصبحنا جاهزين للتجربة لاحظ طباعة السترينق من الكلاس الأول كما حددنا في الميثود finishPassing، يمكنك أنت أن تقوم بالتعامل مع البيانات كما تشاء. وصلنا الى نهاية موضوعنا اليوم، أترككم في حفظ الله ورعايته.
  5. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته ما هو موضح مسبقًا في الدرس العاشر Image هو وسم لإستعراض الصور بداخلة ( يدعم استعمال صور من رابط خارجي ) وActivityIndicator هو عبارة عن علامة Loading ( مؤشر انتظار ) خصائص الملحق ActivityIndicator busy اظهار مؤشر التحميل يقبل قيمتين true | false busyChange تشغل دالة عند تغيير قيمة busy color لون المؤشر خصائص الملحق Image src مصدر الصورة اما برابط محلي مثل "~/3alampro/logo.png" او برابط من مجلد resources مثل "res://3alampro/logo" او رابط خارجي مثل "http://www.3alampro.com/logo.png" width لعرض الصورة height لطول الصورة مثال على الملحقان ملف main-page.xml <Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo"> <Page.actionBar> <ActionBar title="عالم البرمجة - الصورة ومؤشر الإنتظار"></ActionBar> </Page.actionBar> <StackLayout style="padding: 20px"> <Image src="http://www.3alampro.com/uploads/monthly_2016_03/stamp.png.2809a48c070e3d284c9dc2d6d8b46b60.png" width="240" height="120" /> <ActivityIndicator busy="true" color="gray" /> </StackLayout> </Page>
  6. Earlier
  7. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته مقدمة سأتحدث في هذا الموضوع عن دورة حياة المشروع البرمجي ولن يكون موضوع واحد فقط وانما عبارة عن سلسلة مواضيع سأتحدث بها عن SPLC -وتعني Software Project Life Cycle- للمستقلين، وأعني بالمستقلين المبرمجين الذين يعملون بشكل مُستقل وليس مع فريق، والسبب الرئيسي لتحدثي عن هذا الموضوع هو انعدام جودة أغلب المشاريع البرمجية التي تتم بشكل فردي أو يتوقف تطويرها في مرحلةٍ ما بسبب عدم فهم الكثير من الأمور التي تتعلق بالمشروع البرمجي وقد تسبق كتابة الكود ايضاً. ملاحظة: SPLC هو مصطلح غير علمي والمصطلح المعروف SDLC -وتعني System Development Life Cycle- ولكن في هذه الدروس لن أتحدث عن دورة حياة تطوير المشروع بشكل خاص وانما عن دورة حياة المشروع البرمجي بشكل عام. جدول المصطلحات الهامة المستخدمة تمت الإشارة للمصطلحات الهامة باللون الأخضر المصطلح الوصف Ad Hoc Approach الطريقة الخاطئة التي يتبعها المستقلين حالياً. SDLC System Development Life Cycle دورة حياة تطوير البرمجيات. SPLC Software Project Life cycle دورة حياة المشروع البرمجي. وهو مصطلح غير علمي كما ذكرنا. Standard هي عبارة عن معايير ونماذج تحددها شركات كبيرة، وهذه النماذج مختبرة، ومجربة، وتم اثبات جدارتها، وتمنح الشركات المطبقة لهذه النماذج او المعايير بالشكل الصحيح شهادات اعتماد. Phases مراحل. Models نماذج. Build اصدار من البرنامج يكون مكتمل ويكون محدد باصدار؛ مثال: Buld 1، Build2...الخ. GUI Graphical User Interface واجهة المستخدم. AI Artificial intelligence الذكاء الاصطناعي. اولاً ماهو المشروع البرمجي؟ وما الهدف منه اصلاً؟ يوجد الكثير من الأشخاص الراغبين في تعلم البرمجة او لنكون دقيقين يرغبون في تعلم لغة برمجة، وذلك بهدف برمجة تطبيق او موقع على الانترنت، او حتى برنامج سطح مكتب، او لعبة...الخ ولكن للرغبة نوعان: نوع قد يكون بهدف، والأخر قد لا يكون بهدف؛ ومن هذا المنطلق نستطيع القول ان المشروع البرمجي هو هدف مبني على رغبة، وليس رغبة بحد ذاتها، وهذا الهدف لا يكون الا لحل مشكلة انسانية او تسريع اعمال او للقيام بالامور الحسابية بشكل فاعل وأسرع، أو حتى بالمجال الطبي أو بالمجال الاقتصادي، وبالغالب كل مجالات الحياة الانسانية، وبسبب هذه الأهداف أصبحت الرغبة بتسريع الأمور وتسهيلها هاجس انساني بحت، حتى وصلنا اليوم الى الـ AI وتقدمنا فيه بشكل كبير. خلاصة: اذاً فالمشروع البرمجي هو عبارة عن برنامج مبني على الحاسوب والهدف منه هو حل المشكلات الإنسانية او تسهيل الحياة او تسريع أداء المهام، أو حتى للمتعة. ثانياً ماهي المشكلة بالطريقة المتبعة حالياً من قبل المستقلين؟ للأسف فالكثير من المبرمجين المستقلين حالياً يعملون على الكثير من المشاريع البرمجية سواءً مشاريع شخصية لمسعىً ربحي او مشاريع لأفراد وجميع هذه المشاريع تتم بشكل عشوائي وبطريقة نسميها علمياً Ad Hoc Approach. تعريف Ad Hoc Approach: هي الحلول المتبعة او الطرائق التي يتخذها المبرمج لإنجاز مشروعه، وهذه الحلول او الطرائق تكون غالباً مبنية على رؤية المبرمج -وليس بطرق مُنظمة بُنيت على الكثير من التجارب التي تم اثباتها-، وغالباً هذه الطرق التي يتخذها المبرمج غير قابلة للاستخدام في مشاريع او اهداف اخرى وذلك لعدم كفائتها فهي غالباً وإن لم تكن دائماً تُنتج مُخرجات ذات جودة رديئة تكون غير قابلة للإختبار، أو التطوير، او قد تكون غير قابلة للتعديل لاحقاً. وهذه العادة المؤسفة والمتبعة من قبل الكثير من المستقلين قد تطفىء الضوء عن كثير من المشاريع سواءً (مواقع الكترونية، تطبيقات للهواتف الذكية، أو ألعاب) مُبكراً وذلك لرداءة جودتها كما ذكرنا سابقاً، وقد ينتهي المشروع الى قضايا قانونية يرفعها صاحب المشروع على المستقل المنفذ لهذا المشروع بسبب رداءة المنتج او عدم تطبيق طلب الطرف الأول بشكل صحيح. ثالثاً ماهي دورة حياة تطوير البرمجيات (SDLC): SDLC او System Development Life Cycle هي دورة حياة تطوير البرمجيات، وتعتبر هذه الدورة بكل مراحلها من ضمن مجالات هندسة البرمجيات، والتي تهدف لتفصيل الإجراءات والأساليب المتبعة لحوكمة وضبط عمل فريق تطوير البرمجيات، والتي أصبحت أساس من أساسيات بناء اي مشروع برمجي، وقد تختلف المراحل من مشروع الى أخر حسب حجم واحتياجات المشروع، وتطبيق جميع المراحل يجعل من المشروع ذا جودة عالية جداً، وموافق للمعايير القياسية (Standard). مراحل دورة حياة تطوير البرمجيات (SDLC): نظراً لأن الأخطاء التي يتم اكتشافها مؤخراً تكون مكلفة وصعبة المعالجة؛ لذا فتصور دورة الحياة يُسهل لنا التنبؤ بالأخطاء مُبكراً ويسمح للمبرمجين بالتركيز على جودة التطبيق والوقت المحدد لتنفيذ البرنامج، وايضاً وضع التكلفة المطلوبة في الاعتبار؛ وتتضمن دورة الحياة التي يمر بها تطوير البرمجيات المراحل الأتية: 1 - Feasibility Study دراسة الجدوى. 2 - Analysis التحليل. 3 - Design التصميم. 4 - Implementation التنفيذ. 5 - Testing الاختبار. 6 - Maintenance الصيانة. 7 - Evaluation التقدير. المراحل الأساسية والمتعارف عليها: 1 - Planning التخطيط. 2 - Analysis التحليل. Requirement المتطلبات Definition المفاهيم 4 - Design التصميم. 5 - Implementation التنفيذ. 6 - Testing الاختبار. 7 - Deployment التنصيب. 8 - Maintenance الصيانة. 9 - Evaluation التقدير. وهذه المراحل Phases هي مراحل مهمة جداً وأساسية في بناء المشاريع البرمجية، وكما ذكرنا سابقاً أن هذه المراحل قد لا تدخل جميعها في مرحلة بناء المشروع البرمجي وانما يتم انتقائها حسب الاحتياج، ولكن نؤكد لكم أن بعضاً من هذه المراحل الزامية ويجب ان تدخل في كُل مشروع برمجي احترافي وذو جودة عالية وقد تم تحديدها باللون الأحمر. ملاحظة: أساس من أساسيات بدأ او تنفيذ اي مشروع برمجي هو اختيار طريقة تنفيذ SDLC وفي الشطر التالي من هذا الموضوع سنتحدث عن هذه الطرائق او النماذج. ملاحظة: بشكل عام جميع المراحل أعلاه قد تكون تسلسلية فلا تبدأ مرحلة حتى تنتهي المرحلة التي تسبقها. ملاحظة: قد تختلف مسميات بعض المراحل فمثلاً Implementation يطلق عليها احيانا Development. أ. مرحلة التخطيط: في هذه المرحلة يتم عمل دراسة لجدوى المشروع، والتواصل مع العميل لفهم المشروع وبناء صورة مبدئية له، والتحقق من الامكانيات لتنفيذ هذا المشروع، فهي تعتبر مرحلة اتخاذ قرار مبدئي. ب. التحليل: تعتبر من أهم مراحل دورة حياة المشروع البرمجي، وهي البادئة الفعلية لل SDLC حيث يتم في هذه المرحلة الاقتراب أكثر من العملاء لفهم المشروع، وتحديد مُتطلباته Requirement، وتحديد المفاهيم والأهداف وايطار المشكلة، وحذف المفاهيم الغير منطقية واستبيان وفهم المشروع بشكل أكبر، فهي تعتبر خطوة تحديد الطلب، وعلى أساسها يتم تنفيذ المراحل التالية. Requirement المتطلبات: هي رغبات العميل، وتطلعاته وهي مهمة جداً في فهم المشروع وتحديد مشكلة العميل وماذا يريد بالضبط. ج. مرحلة التصميم: هي المرحلة التي يتم من خلالها عمل التصميم المبدئي للمشروع، ولا نعني بالتصميم ال GUI وانما مخططات المشروع تماماً مثل المهندس المعماري عندما يرسم مخططات المنزل قبل بنائه، فهذه الخطوة اساسية جداً ولاحقاً سأشرح التصميمات التي يحتاج المستقل ان يعملها لبناء مشروعه بشكل احترافي. د. مرحلة الاختبار: في هذه المرحلة يتم اختبار المشروع، والتحقق من أنه يعمل بشكل جيد ويوجد نوعين من الاختبارات الأساسية Black Box Testing و White Box Testing وسنتعرف على هذه الانواع لاحقاً. ك. التنصيب: وهي عملية نقل المشروع من بيئة التطوير الى بيئة التشغيل في مكان العميل. ص. الصيانة: أهم مراحل SDLC وقد تكون الأكثر ثمناً والأطول في المدة. رابعاً كيف ابدأ المشروع البرمجي؟ هناك الكثير من الطرق أو النماذج التي يتبعها اغلب المستقلين المحترفين في المجال البرمجي، وتكون من بداية المشروع حتى مرحلة التسليم ثم التطوير، وهذه النماذج لا استطيع القول انها جيدة ولكنها مستخدمة، وهي أفضل بكثير من Ad Hoc، ونطلق على هذه النماذج مسمى Models، وسأذكر في هذا القسم البعض من هذه الطرائق. ملاحظة: سأطلق على Models لاحقاً بالطرائق أو النماذج. خلاصة: مما تم ذكره سابقاً نستطيع القول أنه لا توجد طريقة واحدة لبدء المشروع البرمجي، وانما هنالك الكثير من الطرق المعترف بها والمجربة تطبيقياً، وجميع هذه الطرق لم تجرب في مشروع واحد وانما لها تاريخ من الفشل والتطوير، ثم الفشل والتطوير، ...، وسلسلة طويلة من قصص فشل وتطوير حتى توصلنا الى Standard عام لهذه الطرائق، وليومنا هذا مازالت اغلب الطرق قيد التطوير. والهدف من هذه الطرق او النماذج هو تطبيق SDLC بالشكل الأمثل. ومن هذه النماذج (الطرق) نذكر: 1 - Code and Fix: وهذه الطريقة مستخدمة كثيراً من قبل أغلب المستقلين وهي سيئة جداً في حال تم تطبيقها لمشروع كبير او متوسط الحجم، وهي بكل بساطة نبدأ بكتابة الشفرة البرمجية حتى ننتهي، في حال ظهور مشاكل نصلحها او حتى نحصل على رضا المستخدم، ثم نبدأ بكتابة بقية الشفرة حتى ننتهي، وفي حال ظهور مشاكل نصلحها او حتى نحصل على رضا المستخدم، ونستمر بهذه الدورة حتى يكتمل المشروع. لاحظ في هذا النموذج استخدمنا مرحلة ال التخطيط، والتنفيذ فقط. وهذه الطريقة مناسبة جداً للمشاريع الصغيرة والتي لا تكون حساسة، مثل المواقع الشخصية الصغيرة، أو التطبيقات المصغرة جداً مثل تطبيقات عرض الأخبار أو الألعاب الصغيرة. 2 - Incremental: هذه الطريقة تستخدم كثيراً في التطبيقات العامة التي يتم تطويرها وبيعها للمستخدمين، وهذه الطريقة جيدة ولكن لها سلبياتها للأسف، وهي بكل بساطة أن نعمل اصدارات للمشروع البرمجي وكل اصدار يسمى Build وكل Build أثناء تنفيذه يتم تطبيق جميع مراحل SDLC تقريباً بشكل تكراري Itrative، وسنتحدث عن هذه المراحل في موضوع أخر ان شاء الله. 3 - Agail: هذه الطريقة هي دمج مابين Incremental و Itrative وتركز على تنفيذ المشروع بالشكل الذي يحقق رضا العملاء، عن طريق التسليم السريع للمنتج، وهذه الطريقة تعتبر سلسة جداً من ناحية الاستخدام وهي من الطرق التي يستطيع استخدامها المستقلين. ملاحظة: هذه بعض النماذج التي احببت ذكرها، وهي نماذج جيدة جداً، ويستطيع المستقل استخدامها بسلاسة نوعاً ما. خلاصة: اذاً اجابتاً على السؤال: كيف ابدأ المشروع البرمجي؟ الاجابة تكمن في اختيار الطريقة او النموذج الصحيح؛ واختيار نموذج او طريقة العمل ليس سهل وانما يتطلب خبرة عالية وفهم عميق للمشروع، ولكن لا تقلق حتى لو كنت مبتدأ فمعرفتك البسيطة بهذه النماذج سوف يساعدك كثيراً على اختيار الصحيح -تقريباً- منها. على كُل حال فهذه النماذج قد لا تكون واضحة للبعض ولكن لاحقاً في موضوع أخر سنشرح هذا النماذج بشكل أفضل ونشرح طرق الاختيار. خامساً تحليل وتلخيص المعلومات: اذا لنربط المعلومات ونوضح الأفكار ونضمن فهمك للموضوع بشكل جيد سنضع نقاط وسيتم تلخيص كل نقطة. ماهي المشكلة مع الطريقة التي يتبعها المستقلين في مشاريعهم البرمجية. ماهي المخاطر الممكنة او المتوقعة التي تسببها الطرق الخاطئة في تنفيذ المشاريع البرمجية. كيف ابدأ ببناء مشروع برمجي ذو جودة عالية. SDLC و SDLC Models. أ. ماهي المشكلة مع الطريقة التي يتبعها المستقلين في مشاريعهم البرمجية؟: المشكلة باختصار تكمن في جودة المنتج، فتنفيذ المشاريع البرمجية لا يكون فقط بكتابة الكود، وانما التخطيط الصحيح والتصميم الأمثل، واتباع الطريقة الأكثر فعالية لضمان بناء المشروع بجودة عالية، وسهولة اختباره، وامكانية صيانته وتطويره لاحقاً. ب. ماهي المخاطر الممكنة أو المتوقعة التي تسببها الطرق الخاطئة في تنفيذ المشاريع البرمجية؟: الجودة، صعوبة الاختبار والتحقق، صعوبة الصيانة، و القضايا القانونية. ج. كيف ابدأ ببناء مشروع برمجي ذو جودة عالية؟: لبناء مشروع برمجي ذو جودة عالية، يجب ان تبدأ اولاً بفهم المشروع بشكل صحيح، ثم كتابة متطلبات المشروع، ثم عمل التصميمات التوضيحية وتأكيد فهم المشروع، ثم التنفيذ، ثم الاختبار، ولاحقاً الصيانة، وكل ماتم ذكره سابقاً من مراحل SDLC والتي يتم تنفيذها بشكل فعال من خلال SDLC Models. د. SDLC و SDLC Models: دورة حياة تطوير المشروع البرمجي SDLC هي مجموعة مراحل يجب تنفيذها لبناء المشروع البرمجي بشكل صحيح، وطريقة تنفيذ هذه المراحل يكمن من خلال نماذج دورة حياة تطوير المشروع البرمجي SDLC Models. خاتمة من المتوقع كثيراً خصوصاً مع كثرة المُصطلحات والأفكار أن يكون فهمك لأهمية SPLC غير كامل وقد يكون مُبهم، لذلك لا تقلق ففي المواضيع القادمة والتي ستكون تحت عنوان دورة حياة تطوير البرمجيات: X -حيث أن X يمثل العنوان الفرعي- سنتحدث بالتفصيل عن مراحل SDLC، وعن الأدوات، والنماذج التي سوف تساعدك كمستقل على ادارة مشروعك البرمجي بشكل احترافي لتضمن جودته. الموضوع التالي دورة حياة تطوير البرمجيات: المرحلة الأولى التخطيط هذا وصلى الله وسلم على نبينا محمد وعلى آله وصحبه اجمعين
  8. رغم انه المفترض في هذا الموضوع اتحدث عن طرق الاستخدام الخاطئ للـ Segue وتركيزه ينصب في شرح الـ Unwind Segue الا اني سوف أقوم بشرح الامرين لجعل الموضوع اشمل ما هو الـ Segue ؟ الـ Segue هو عملية الانتقال بين صفحة (View Controller) الى اخر ويتم عن طريق ربط زر او Action موجود في الصفحة الاولى الى الصفحة الأخرى اهم قاعده هنا الانتقال يكون من صفحة 1 الى صفحة 2 ولا يحصل انتقال بشكل عكسي أيضا ! هذا من اكثر الاخطاء شيوعاً عند المطورين الجدد ! لفهم الـ Segue بشكل صحيح سوف اشرحه على مراحل او طرق بالتدرج الطريقة الاولى : في هذه الطريقة فقط نقوم بالربط بين صفحة وأخرى وتستخدم الطريقة هذه بشكل نادر فقط لغرض عرض صفحة أخرى دون الحاجة لاستخدام اكواد او نقل معلومات من صفحة الاولى الى الثانية والطريقة تكمن في التالي : أولا : نقوم اول بإضافة 2 من View Controller ثانيا : نقوم بإضافة Button في صفحة 1 ثالثا : نقوم بالضغط الـ Button + زر control ونسحب الـ Button الى الـ View Controller الاخر رابعا : نختار Show من امثله استخدام الطريقة هذه اذا اردت أن تعرض صفحة اتصل بنا او عن التطبيق, في هذه الصفحات فقط تعرض معلومات ولا تحتاج الا نقل معلومة من صفحة الى أخرى او تحتاج أن تقوم ببعض الامور قبل عملية الانتقال, لذا استخدام الطريقة هذه طريقة نادره الطريقة الثانية : الطريقة هذه تعتمد على موضوع الانتقال للصفحة الثانية بعد تنفيذ امر معين وبدون الحاجة الى نقل معلومات من الصفحه 1 الى الصفحة 2 لكي تفهم الفكرة بشكل صحيح ، لنفترض بأنك تعمل على صفحة "التسجيل حساب جديد" راح تحتاج تكتب التالي في Function الـ Button تتأكد بأن المستخدم كتب جميع الحقول من اسم المستخدم والايميل والباسورد وإعادة كتابة الباسورد تتأكد بأن هناك تطابق في حقل باسورد مع حقل إعادة كتابة الباسورد تتأكد من تلبية شروط كتابة الباسورد على سبيل المثال بأن تم كتابة ٨ احرف تتصل بالسيرفر في حال تم الاتصال وحفظ بيانات المستخدم بنجاح هنا يتم الانتقال الي الصفحة الثانية ! هل لاحظت الفرق ؟ في الطريقة الاولى الانتقال مباشرة الى الصفحة الثانية ولكن في هذه الطريقة هناك أمور كثيره تحدث بمجرد ضغط المستخدم على الـ Button وتتم الانتقال اثناء حدوث امراُ معيناً وليس بشكل مباشر ! والطريقة تكمن في التالي : نفس خطوات الطريقة الاولى بالضبط ولكن هنا يتم إضافة مُعرف (Identifier) لاحظ الصورة التالية : وفي داخل Function الـ Button نكتب التالي : performSegue(withIdentifier: "toView2", sender: nil) لاحظ في السطر السابق كتبنا نفس المُعرف بالضبط الذي كتبناه في حقل الـ Identifier في الـ Storyboard وأقصد هنا بالضبط ، المسافة تفرق ، الحرف الكبتل والسمول أيضا يفرق ! بما يعني الأفضل أن تقوم بنسخه ومن ثم لصقه بدلاً من كتابته . قد تتسأل ما الفائده من المُعرف ؟ المُعرف هو المسؤول عن توجهيه الـ View Controller الى الـ View Controller الصحيح. في تطوير التطبيقات سوف تحتاج الي عملية انتقال مختلفة من نفس الـ View Controller قد يحتوي تطبيقك مثلا على زرين كل زر يوجه الى صفحه مختلفة فهنا المُعرف يجعل الـ Xcode يوجهك الى الصفحة الصحيحة. الطريقة الثالثة : في هذه الطريقة اذا اردت بأن تقوم بنقل معلومات من الصفحة 1 الى صفحة 2 الطريقة هذه أيضا نفس الطريقة السابقة من حيث انك تعطي مُعرف (Identifier) الفرق بأنه سوف تحتاج الى استخدام Function معين يقوم بوظيفة نقل المعلومات اثناء عملية الـ Segue. هذا هو الـ Functionn الذي سوف تحتاج الى استخدامه: override func prepare(for segue: UIStoryboardSegue, sender: Any?) { } اين تضعه ؟ تضعه في الصفحة الحالية, فانت تريد نقل معلومة من صفحة 1 الى صفحة 2 فتضعه في صفحة 1 هنا تحتاج تعرف معلومة : لنقل بيانات من صفحة الى اخرى سوف تحتاج الي متغير في الصفحة الأخرى لكن تحفظها به ولكي تتضح الصورة الان في صفحة 1 تريد نقل معلومة معينه الى صفحة 2. المعلومة هذه عباره عن String اذا سوف تقوم بإنشاء متغير من نوع String في صفحة 2 ومن ثم في داخل الـ Function السابق تستدعي المتغير وتحفظ القيمة الموجودة في صفحة1 الي المتغير في صفحة 2. نعود للشرح الان في الـ View Controller الاول نضيف Textfield فوق الـ Button ونربطه بملف الأكواد. ومن ثم في الـ View Controller الثاني نضيف Lable لكن قبل ذلك نضيف Class من نوع View Controller ومن ثم نحدد الـ View controller الثاني في الـ Stroryboard ونختار الـ Class الذي أنشأناه بعدها نربط الـ Label بملف الاكواد اصبح لدينا في الـ View Controller الاول فقط Button و Textfield وفي الـ View Controller الثاني لدينا Label قبل العودة الى Function الذي اضفناه في ملف اكواد الـ View Controller الاول يتوجب علينا إضافة متغير من نوع String لذا نقوم باضافته فيصبح شكل الـ StroyBoard بالشكل التالي : ملف اكواد الـ View Controller الاول بالشكل التالي : import UIKit class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { } } ملف اكواد الـ View Controller الثاني بالشكل التالي : import UIKit class ViewController2: UIViewController { var text:String? @IBOutlet weak var label: UILabel! override func viewDidLoad() { super.viewDidLoad() label.text = text } } كما تلاحظ في الـ viewDidLoad اضفت السطر التالي label.text = text لجعل النص الذي سوف نجلبه من View Controller الاول يتم طباعته في View Controller الثاني نعودة الان الى Function الان نعود لملف اكواد الـ View Controller الاول ونقوم بكتابة التالي override func prepare(for segue: UIStoryboardSegue, sender: Any?){ if segue.identifier == "toView2" { if let vc = segue.destination as? ViewController2 { vc.text = textField.text! } } } لاحظ التالي اول سطر segue.identifier نكتب فيه مُعرف الـ segue كما قمنا بكتابته في الـ Stroyboard السطر الثاني نقوم بكتابة اسمك الكلاس الـ View Controller الثاني في حالتي اسمه ViewController2 السطر الثالث نستدعي المتغير الذي أنشأناه في الـ View Controller الثاني ونربطه بمحتوى الـ Textfield لذا اثناء عملية الـ Segue سوف ينتقل النص الذي سوف نكتبه في حقل الـ Textfield الى الـ String الموجود في الصفحة الثانية ومن ثم في الـ View Controller الثاني سوف يتم طباعة الـ String في الـ Label نقوم بتشغيل التطبيق ونرى النتيجة : هذه الطريقة الصحيحة لنقل البيانات من View Controller الى اخر وهيا الطريقة الأكثر استخداماً الطريقة الرابعة : الطريقة هذه هيا عباره عن دمج طريقة الثانية مع الثالثة هناك أوقات تحتاج الى الانتقال الي View Controller اخر بعد حدث معين وفي نفس الوقت تريد فيها نقل البيانات الى الـ View Controller اخر في هذه اللحظة سوف تحتاج الى الطرقتين بحيث تستخدم performSegue(withIdentifier: "", sender: nil) وفي نفس الوقت تستخدم override func prepare(for segue: UIStoryboardSegue, sender: Any?) { } الـ Xcode ذكي بما فيه الكفايه اثناء تنفيذ سطر performSegue(withIdentifier: "", sender: nil) سوف يلاحظ وجود Function override func prepare(for segue: UIStoryboardSegue, sender: Any?) وبالتالي سوف ينتقل اليه أولا قبل تنفيذ الـ Segue وفقط للمعلومية prepare تعني تجهيز وبالتالي الـ Function يستعد لعملية الـ segue فيتم تنفيذ ما بداخله أولا ! الان انتهينا من جزء الـ Segue -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- حان الوقت الانتقال الي موضوع الـ Unwind Segue وهو الموضوع الذي اردت التحدث عنه ! كل شخص جديد يتعلم برمجة تطبيقات الـ iOS وتحديدا عند تعلمه الـ Segue وطريقة نقل البيانات من صفحة الى أخرى يخطئه هذا الخطأ !! اغلبية المبرمجين الجدد يخطئوا نفس الخطأ ! ماهو الخطأ الذي يقع به الكثيرون ؟ استخدام الـ Segue عند الرغبة في الرجوع الى الصفحة السابقة ! في هذه النقطة سوف أوضح لك الخطأ ولماذا يعتبر مصيبه عند عمله !! افتراضاً نملك 4 صفحات و ٣ Button كل Button متصل بـ Segue ينقلك الى الصفحه الأخرى كما في الصورة التالية : قد تتسأل ماهي المشكلة في الصورة السابقة ؟ الحقيقة هيا لحد الان لا توجد مشكلة ! لكن في الصورة التالية تكمن المشكلة ! المشكلة تكمن هنا عندما ينشأ المبرمج زر للرجوع الى الصفحة الرئيسية كما في الصورة التالية : ماذا فعل هنا ؟ قام بربط زر الرجوع الى صفحة رقم 0 عن طريق الـ Segue !! هذا هو الخطأ الذي اردت التحدث عنه ! هناك 3 مشاكل تسببه هذه المشكلة! أولا : كثرة الـ Segue ذهابا وعوده تسبب مشكله لذا المطور في انه لا يعلم هذا الـ Segue مرتبط بأي View Controller ؟ ثانيا : مشاكل في الأداء واستهلاك موارد الجهاز بما يسبب بطئه عند استخدام التطبيق !! ثالثا : مشاكل في Auto Layout ! ، من ضمن المشاكل التي تسببها ، مشاكل في الـ Auto Layout من الامور التي سوف تلاحظها رغم وضعك القيود بشكل صحيح الا انه لايزال يظهر خطأ في القيود ! لستُ مقتنعاً ؟ سوف أقوم الان بتشغيل التطبيق واستخدامه ذهاباً وعودة شاهد الصورة التالية : الان قم بالتالي ، لا تقوم بإيقاف تشغيل المشروع واذهب لهذه الخانة : وحرك باستخدام الماوس لملاحظة هيكل المشروع لاحظ الصورة التالية : نقوم الان بعمل Debug لهيكل المشروع من ناحية واجهة المستخدم هل لاحظت كثرت الطبقات ؟ المفترض أن تظهر فقط 5 صفحات 4 صفحات خاصة بالـ View Controller والصفحه 5 للزر الموجود في اخر صفحة بحكم اني قمت بالتوقف عندها ! ولكن الذي يظهر هو 13 طبقة ! ما السبب ؟؟ السبب هنا بسبب طريقة ربط الـ Segue ذكرت في بداية الموضوع الـ Segue يذهب من الصفحة 1 الى الصفحة 2 وليس العكس ! عند عمل العكس هذا ما سوف يحدث ! السبب عند عمل هذا الخطأ يقوم النظام على انشاء الصفحة مره أخرى وأخرى وأخرى كل مره يتم الضغط على زر الرجوع يتم انشاء الصفحة مره أخرى والنتيجة هي استهلاك موارد الجهاز !! الان قبل أن اشرح الطريقة سوف انفذ الطريقة الصحيحة ومن ثم سوف اشرحها بعد تغير الطريقة الى الطريقة الصحيحة لاحظ الصورة التالية في الـ Stroyboard: هل لاحظت ؟ لايوجد Segue ، للعودة لصفحة الرئيسية ! الان لاحظ الصورة التالية : ومن ثم لاحظ هيكل المشروع : اذا عملت مقارنه بين الصورتين هذه والصورتين السابقة سوف تلاحظ امرين أولاً : عند الضغط على زر العودة الصفحة الحالية تنزل الى الأسفل وتظهر الصفحة الرئيسية ! ، في حين عند عمل الطريقة الخاطئة تظهر الصفحة الرئيسية من الأسفل الى الأعلى !! ثانياً : هيكل المشروع اصبح 5 فقط ! لم يتغير ولم يزداد عدده ! لماذا ؟ عند عمل الطريقة الخاطئة ، تقوم على انشاء الصفحة من جديد في كل مره فكل ما زاد وقت استخدامك التطبيق زادت عدد الصفحات وزاد استهلاك موارد الجهاز في حين عند عمل الطريقة الصحيحة ، تقوم على اغلاق الصفحة الحالية والعودة للصفحة التي تريدها ، وبالتالي لا يوجد أي زياده ! بعد فهم الفكرة واتضاح الفرق نعود الى شرح الطريقة الصحيحة : لكن قبلها اريد توضيح نقطه معينه اذا اردا الرجوع الصفحة السابقة السابقة هناك 3 طرق الطريقة الاولى : استخدم هذا السطر بداخل اقواس الـ Button dismiss(animated: true, completion: nil) الزر ذا سوف يرجعك الى الصفحة السابقة فقط بما يعني اذا كنت في صفحة 3 سوف يرجعك الى صفحة 2 لا يمكنك العودة الى صفحة 0 او أي صفحة أخرى الطريقة الثانية : عند استخدام الـ Navigation Bar سوف يظهر زر العودة للصفحة السابقة بشكل تلقائي وأيضا مثل الطريقة الاولى سوف تعود فقط الى صفحة السابقة يمكن ملاحظتها في جميع تطبيقات النظام مثال تطبيق الاعدادات عندما تذهب لقسم عام او General سوف تلاحظ وجود زر في الأعلى يرجعك الى صفحة الرئيسية للإعدادات ، هذه تتم بشكل تلقائي بدون تدخل منك معلومة : لاحظ الصورة التالي : سوف تلاحظ عند استخدام الـ Navigation Bar واختيار نوع Show سوف تكون عملية الانتقال الـ Segue من اليمين الى اليسار والعوده سوف تكون العكس في حين اذا اخترت Present Modally سوف يظهر من الأسفل الى الأعلى والعكس عندها يعود للصفحة السابقة ! كما الحال في تطبيقنا لكن بدون استخدام الـ Navigation Bar الـ Show يظهر كالـ Present Modally الطريقة الثالثة : الطريقة هذه تدعى Unwind Segue ما الذي يميزها ؟ الذي يميزها هو التالي : أولا : يمكنك العودة الى أي صفحة تريدها وليس ملزماً بالعودة الى الصفحة السابقة فقط ! بما يعني كما هو حال مثالنا سوف يمكنك العودة من صفحة 3 الى صفحة 0 بشكل مباشر ثانيا: يمكنك ارجاع بيانات من الصفحة الحالي الى الصفحة السابقة ! بما يعني سوف تستطيع ارجاع بيانات موجوده في صفحة 3 الى صفحة 0 او أي صفحة تريدها !! معلومة : الـ Unwind Segue تعني فك الـ Segue وبالتالي يجب أن يكون هناك Segue لتستخدم هذه الطريقة ! اذا استخدمتها بدون عمل Segue مسبقاً بين 2 من الـ View Controller او اكثر ، سوف يسبب Crash للتطبيق الان نبدأ في شرح الطريقة: كما شرحت الـ Segue سوف اقسم الـ Unwind Segue الى عدة طرق طريقة الأولى : ترغب فقط بالعوده الى صفحة معينه بدون ارجاع أي بيانات وبالتالي تحتاج تفهم هذه النقطة : اذا رغبت بالرجوع الى صفحة معينه سوف تحتاج الى كتابة كود في صفحة التي تريد الرجوع لها هنا نحن نريد العوده من أي صفحة الى الصفحة الرئيسية View Controller 0 فسوف نقوم بكتابة الكود التالي : @IBAction func unwindToHome(segue:UIStoryboardSegue) { } في ملف اكواد View Controller 0 ملاحظة : -لا تحتاج الى كتابة أي اكواد في الداخل الاقواس ! على الأقل في الوقت الحالي ! - تستطيع تسمية الـFunction بأي اسم تريده ، انا قمت بتسميته unwindToHome ومن ثم ننتقل الى الـ Storyboard ونفعل التالي مع صفحات 1 و 2و 3 شاهد الصورة : فقط هذا كل ما نحتاج الى فعله ! قم بتشغيل التطبيق وسوف تجده يعمل بمجرد الضغط على زر Return to Home سوف تجده يعود الى الصفحة الرئيسية وهيا View Controller 0 الطريقة الثانية : ماذا اذا اردت تنفيذ امراً معيناً قبل ان يحدث الـ Unwind Segue ؟ تحتاج الطريقة دي في بعض الحالات ، مثلا صفحة Login المستخدم بعد ما يكتب اسم المستخدم وكلمة السر ويضغط زر Login سوف تحتاج الى الاتصال بالسيرفر وتتأكد انه المستخدم موجود في قاعدة بياناته وبعد التأكد ، تغلق الصفحة باستخدام Unwind Segue وتحوله الى الصفحة الرئيسية معلومة : ما سبق ذكره مجرد مثال ، لأنه تستطيع أيضا استخدام سطر dismiss(animated: true, completion: nil) لأغلاق الصفحة فالمثال السابق يعتبر كطريقة أخرى لتنفيذ نفس الأمر, على أي حال الطريقة مشابه لطريقة الـ Segue من حيث تحتاج الى إعطاء مُعرف. نعود للشرح : اهم نقطه هنا هو فصل الربط الذي عملته في الطريقة الاولى بين الـ Button و Exit ولكن لا تحذف الـ Function الذي كتبناه في View Controller 0 @IBAction func unwindToHome(segue:UIStoryboardSegue) {} في هذه الطريقة سوف نربط الـ Viewcontroller نفسه مع الـ Exit شاهد الصورة التالية : ومن ثم سوف نعطي للـ Unwind Segue مُعرف شاهد الصورة التالية : قمت باعطاء مُعرف home الان قم بربط الـ button مع ملف الاكواد ومن ثم استخدم نفس السطر الذي استخدمناه في الـ Segue performSegue(withIdentifier: "home", sender: nil) بالطريقة هذه تستطيع عمل أي امر تريده قبل حدوث الـ Unwind Segue الطريقة الثالثة : في هذه الطريقة الامر عائد اليك ، يمكنك عمل الطريقة الاولى وتنفذ الطريقة هذه معها او استخدام الطريقة الثانية مع هذه الطريقة. فالطريقة الثالثة تشرح طريقة نقل البيانات من الصفحة 3 الى صفحة 1 بنفس درجة حاجتك الى استخدام الى الـ Segue لنقل البيانات أيضا سوف تحتاج استخدام Unwind Segue لنقل البيانات من امثله استخدام الـ Unwind Segue لنقل البيانات, تطبيقات المحادثه مثل الـ Whatsapp عند استخدامك للتطبيق لأول مره يطلب منك اختيار رمز دولتك فعند الضغط عليه يوجهك لصفحة تختار فيها دولتك ومن ثم عند اختيار دولتك يعود الى صفحة كتابة رقم جوالك هنا حدث Unwind Segue بحيث تم نقل رمز الدولة من صفحة أخرى الى صفحة السابقة نعود للشرح : هل تذكر ماذا فعلنا عند نقل معلومات او بيانات من صفحة 1 الى الصفحة 2 باستخدام الـ Segue ؟ الطريقة مشابهه لحد ما ! نحن نريد نقل بيانات من صفحة 3 الى صفحة 0 لذا قبل أنا نبدأ سوف نفعل التالي: سوف نضيف Textfield في الصفحة 3 ونضيف Label في صفحة 0 الي هيا الصفحة الرئيسية الذي نريد الرجوع اليها ونقوم بربطهم بملف الاكواد شاهد الصورة التالية : الان حان الوقت للكتابة في Function @IBAction func unwindToHome(segue:UIStoryboardSegue) { } الان كما قلنا سابقا نريد نقل النص من الـ Textfield الموجود في View Controller 3 الى الـ Label الموجود في View Controller 0 لذا نقوم بكتابة التالي : @IBAction func unwindToHome(segue:UIStoryboardSegue) { if segue.identifier == "home" { let vc = segue.source as! ViewController3 label1.text = vc.text1.text } } اول شيء نقوم بالتأكد من اسم المُعرف في حال كنت تتبع الطريقة الاولى لن يكون هناك اسم معرف الا اذا وضعته باختيارك لذا يمكنك حذف هذا السطر if segue.identifier == "home" {} وحتى أيضا اذا اتبعت الطريقة الثانية لن تحتاج هذا السطر! الا اذا كنت تريد ارجاع بيانات من صفحتين فأكثر فهنا يتوجب عليك التمييز بينهم عن طريق التأكد من اسم المُعرف على أي حال اذا لاحظت فالكود مشابه جدا من طريقة الـ Segue . في الـ Segue نقوم بكتابته بداخل Function يسمى prepare وهنا بداخل Function الـ Unwind وأيضا في الـ Segue نكتب segue.destination as اسم الكلاس الذي نريد الانتقال اليه وفي الـ Unwind Segue نكتب segue.source as اسم الكلاس الذي سوف نعود منه لذا تقريبا نفس الفكرة الاختلاف في كلمة destination و source اخيراً قم بتشغيل التطبيق وشاهد النتيجة في الصورة التالية : وبكذا انتهينا من هذا الموضوع في هذا الموضوع فهمت الطريقة الصحيحة لنقل البيانات بين الـ View Controllers وماهي الاستخدامات الخاطئة والفريق بين Segue و Unwind Segue. قد تتسأل هل هذه هيا الطرق الوحيدة لنقل البيانات ؟ الاجابه هيا لا ، ما تم شرحه في الموضوع هو طرقتين لنقل البيانات Segue و Unwind Segue وهما اشهر واكثر طرقتين لنقل البيانات بين الـ View Controllers استخداماً لكن هناك طرق أخرى أيضا !
  9. بسم الله الرحمن الرحيم سنتعرف في هذا الدرس على طريقة انشاء تطبيق يدعم تعدد اللغات بحسب لغة الجهاز، سنعتمد اللغتين العربية والانجليزية في هذا التطبيق. سنبدأ أولا باضافة جميع اللغات التي نريد دعمها في تطبيقنا نذهب الى Project Navigator ثم نقوم باضافة اللغات المطلوبة من الأسفل نلاحظ تقسيم الملفات الى لغتين كما في الصورة االآن سنقوم بانشاء ملف من نوع String لنخزن فيه جميع الStrings باللغتين العربية والانجليزية اضغط على File > New > File واختر النوع String كما في الصورة تأكد من تسمية الملف باسم "Localizable" تماما كما هو ليتعرف عليه الxCode بشكل صحيح الآن نختار الملف الجديد من اليسار، ومن قائمة الFile Inspector نضغط على Localize ونختار اللغة الانجليزية مبدئيا الآن نختار اللغة العربية أيضا لنقوم بتقسيم ملف الLocalizable.strings الى لغتين قم باضافة الStrings المطلوبة الى الملفين بالطريقة التالية ليقوم الxCode بعمل Build تأكد من: كتابة الkey بشكل موحد بين اللغتين العربية والانجليزية وضع علامة ال= بين الkey والvalue كتابة الvalue بين علامتي تنصيص "" انهاء السطر بSemi-Colon ; لنرى طريقة استدعاء الStrings من ملف Localizable قمت بعمل الواجهة البسيطة التالية: وقمت بربط الOutlets والActions كالتالي: الآن اضف الكود التالي في زر اللغة الانجليزية: let path = Bundle.main.path(forResource: "en", ofType: "lproj") let bundle = Bundle.init(path: path!)! as Bundle nameLbl.text = bundle.localizedString(forKey: "WebsiteName", value: nil, table: nil) والتالي لزر اللغة العربية: let path = Bundle.main.path(forResource: "ar", ofType: "lproj") let bundle = Bundle.init(path: path!)! as Bundle nameLbl.text = bundle.localizedString(forKey: "WebsiteName", value: nil, table: nil) في السطر الأول: قمنا بتعريف الpath الذي سيوصلنا لكل من ملفات اللغة العربية والانجليزية (يمكنك معرفة الResource والType من قائمة الFile Inspector عند الضغط على ملفي العربية والانجليزية) في السطر الثاني قمنا بتعريف كل من ملفات اللغتين كVariable يحمل الاسم bundle في السطر الثالث قمنا باستدعاء الString المطلوب باستخدام الميثود localizedString(_:String,_:String,_:Strint( والآن عند تجربة البرنامج نرى النتيجة التالية (صورة GIF): حتى الآن كنا نحدد ملف اللغة الذي نريد استدعاء الString منه والآن سنقوم بتغيير النص اعتمادا على لغة الجهاز باستخدام زر واحد فقط لنرى ذلك قمت بتغيير الStoryboard ليحتوي على زر واحد فقط، وقمت باضافة الoutlets والactions كالتالي: والآن قم باضافة السطرين التاليين للميثود viewDidLoad() let btnTitle: String = NSLocalizedString("ButtonText", comment: "") languageBtn.setTitle(btnTitle, for: .normal) والسطر التالي للميثود الخاصة بالbutton action nameLbl.text = NSLocalizedString("WebsiteName", comment: "") في هذه الاكواد، قمنا باستعمال الميثود NSLocalizedString لاستدعاء الString المناسب بحسب لغة الجهاز لتجربة البرنامج سنقوم بتشغيله على جهاز بلغة انجليزية، ثم سنحولها للعربية ونعيد التجربة (صورة GIF): وصلنا الى ختام الدرس، أستودعكم الله الذي لا تضيع ودائعه.
  10. رغم انه المفترض في هذا الموضوع اتحدث عن طرق الاستخدام الخاطئ للـ Segue وتركيزه ينصب في شرح الـ Unwind Segue الا اني سوف أقوم بشرح الامرين لجعل الموضوع اشمل ما هو الـ Segue ؟ الـ Segue هو عملية الانتقال بين صفحة (View Controller) الى اخر ويتم عن طريق ربط زر او Action موجود في الصفحة الاولى الى الصفحة الأخرى اهم قاعده هنا الانتقال يكون من صفحة 1 الى صفحة 2 ولا يحصل انتقال بشكل عكسي أيضا ! هذا من اكثر الاخطاء شيوعاً لذا المطورين الجدد ! لفهم الـ Segue بشكل صحيح سوف اشرحه على مراحل او طرق بالتدرج الطريقة الاولى : في هذه الطريقة فقط نقوم بالربط بين صفحة وأخرى تستخدم الطريقة هذه بشكل نادر فقط لغرض عرض صفحة أخرى دون الحاجة لاستخدام اكواد او نقل معلومات من صفحة الاولى الى الثانية والطريقة تكمن في التالي : أولا : نقوم اول بإضافة 2 من View Controller ثانيا : نقوم بإضافة Button في صفحة 1 ثالثا : نقوم بالضغط الـ Button + زر control ونسحب الـ Button الى الـ View Controller الاخر رابعا : نختار Show من امثله استخدام الطريقة هذه اذا اردت أن تعرض صفحة اتصل بنا او عن التطبيق في هذه الصفحات فقط تعرض معلومات ولا تحتاج الا نقل معلومة من صفحة الى أخرى او تحتاج أن تقوم ببعض الامور قبل عملية الانتقال لذا استخدام الطريقة هذه طريقة نادره الطريقة الثانية : الطريقة هذه تعتمد على موضوع الانتقال للصفحة الثانية بعد تنفيذ امر معين وبدون الحاجة الى نقل معلومات من الصفحه 1 الى الصفحة 2 لكي تفهم الفكرة بشكل صحيح ، لنفترض بأنك تعمل على صفحة "التسجيل حساب جديد" راح تحتاج تكتب التالي في Function الـ Button تتأكد بأن المستخدم كتب جميع الحقول من اسم المستخدم والايميل والباسورد وإعادة كتابة الباسورد تتأكد بأن هناك تطابق في حقل باسورد مع حقل إعادة كتابة الباسورد تتأكد من تلبية شروط كتابة الباسورد على سبيل المثال بأن تم كتابة ٨ احرف تتصل بالسيرفر في حال تم الاتصال وحفظ بيانات المستخدم بنجاح هنا يتم الانتقال الي الصفحة الثانية ! هل لاحظت الفرق ؟ في الطريقة الاولى الانتقال مباشرة الى الصفحة الثانية ولكن في هذه الطريقة هناك أمور كثيره تحدث بمجرد ضغط المستخدم على الـ Button وتتم الانتقال اثناء حدوث امراُ معيناً وليس بشكل مباشر ! والطريقة تكمن في التالي : نفس خطوات الطريقة الاولى بالضبط ولكن هنا يتم إضافة مُعرف (Identifier) لاحظ الصورة التالية : وفي داخل Function الـ Button نكتب التالي : performSegue(withIdentifier: "toView2", sender: nil) لاحظ في السطر السابق كتبنا نفس المُعرف بالضبط الذي كتبناه في حقل الـ Identifier في الـ Storyboard وأقصد هنا بالضبط ، المسافة تفرق ، الحرف الكبتل والسمول أيضا يفرق ! بما يعني الأفضل أن تقوم بنسخه ومن ثم لصقه بدلاً من كتابته . قد تتسأل ما الفائده من المُعرف ؟ المُعرف هو المسؤول عن توجهيه الـ View Controller الى الـ View Controller الصحيح في تطوير التطبيقات سوف تحتاج الي عملية انتقال مختلفة من نفس الـ View Controller قد يحتوي تطبيقك مثلا على زرين كل زر يوجه الى صفحه مختلفة فهنا المُعرف يجعل الـ Xcode يوجهك الى الصفحة الصحيحة الطريقة الثالثة : في هذه الطريقة اذا اردت بأن تقوم بنقل معلومات من الصفحة 1 الى صفحة 2 الطريقة هذه أيضا نفس الطريقة السابقة من حيث انك تعطي مُعرف (Identifier) الفرق بأنه سوف تحتاج الى استخدام Function معين يقوم بوظيفة نقل المعلومات اثناء عملية الـ Segue هذا هو الـ Functionn الذي سوف تحتاج الى استخدامه override func prepare(for segue: UIStoryboardSegue, sender: Any?) { } اين تضعه ؟ تضعه في الصفحة الحالية فانت تريد نقل معلومة من صفحة 1 الى صفحة 2 فتضعه في صفحة 1 هنا تحتاج تعرف معلومة : لنقل بيانات من صفحة الى اخرى سوف تحتاج الي متغير في الصفحة الأخرى لكن تحفظها به لكي تتضح الصورة الان في صفحة 1 تريد نقل معلومة معينه الى صفحة 2 المعلومة هذه عباره عن String اذا سوف تقوم بإنشاء متغير من نوع String في صفحة 2 ومن ثم في داخل الـ Function السابق تستدعي المتغير وتحفظ القيمة الموجودة في صفحة1 الي المتغير في صفحة 2 نعود للشرح الان في الـ View Controller الاول نضيف Textfield فوق الـ Button ونربطه بملف الأكواد شاهد الصورة التالية : ومن ثم في الـ View Controller الثاني نضيف Lable لكن قبل ذلك نضيف Class من نوع View Controller ومن ثم نحدد الـ View controller الثاني في الـ Stroryboard ونختار الـ Class الذي أنشأناه بعدها نربط الـ Label بملف الاكواد اصبح لدينا في الـ View Controller الاول فقط Button و Textfield وفي الـ View Controller الثاني لدينا Label قبل العودة الى Function الذي اضفناه في ملف اكواد الـ View Controller الاول يتوجب علينا إضافة متغير من نوع String لذا نقوم باضافته يصبح شكل الـ StroyBoard بالشكل التالي : ملف اكواد الـ View Controller الاول بالشكل التالي : import UIKit class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { } } ملف اكواد الـ View Controller الثاني بالشكل التالي : import UIKit class ViewController2: UIViewController { var text:String? @IBOutlet weak var label: UILabel! override func viewDidLoad() { super.viewDidLoad() label.text = text } } كما تلاحظ في الـ viewDidLoad اضفت السطر التالي label.text = text لجعل النص الذي سوف نجلبه من View Controller الاول يتم طباعته في View Controller الثاني نعودة الان الى Function الان نعود لملف اكواد الـ View Controller الاول ونقوم بكتابة التالي override func prepare(for segue: UIStoryboardSegue, sender: Any?){ if segue.identifier == "toView2" { if let vc = segue.destination as? ViewController2 { vc.text = textField.text! } } } لاحظ التالي اول سطر segue.identifier نكتب فيه مُعرف الـ segue كما قمنا بكتابته في الـ Stroyboard السطر الثاني نقوم بكتابة اسمك الكلاس الـ View Controller الثاني في حالتي اسمه ViewController2 السطر الثالث نستدعي المتغير الذي أنشأناه في الـ View Controller الثاني ونربطه بمحتوى الـ Textfield لذا اثناء عملية الـ Segue سوف ينتقل النص الذي سوف نكتبه في حقل الـ Textfield الى الـ String الموجود في الصفحة الثانية ومن ثم في الـ View Controller الثاني سوف يتم طباعة الـ String في الـ Label نقوم بتشغيل التطبيق ونرى النتيجة : هذه الطريقة الصحيحة لنقل البيانات من View Controller الى اخر وهيا الطريقة الأكثر استخداماً الطريقة الرابعة : الطريقة هذه هيا عباره عن دمج طريقة الثانية مع الثالثة هناك أوقات تحتاج الى الانتقال الي View Controller اخر بعد حدث معين وفي نفس الوقت تريد فيها نقل البيانات الى الـ View Controller اخر في هذه اللحظة سوف تحتاج الى الطرقتين بحيث تستخدم performSegue(withIdentifier: "", sender: nil) وفي نفس الوقت تستخدم override func prepare(for segue: UIStoryboardSegue, sender: Any?) { } الـ Xcode ذكي بما فيه الكفايه اثناء تنفيذ سطر performSegue(withIdentifier: "", sender: nil) سوف يلاحظ وجود Function override func prepare(for segue: UIStoryboardSegue, sender: Any?) وبالتالي سوف ينتقل اليه أولا قبل تنفيذ الـ Segue وفقط للمعلومية prepare تعني تجهيز وبالتالي الـ Function يستعد لعملية الـ segue فيتم تنفيذ ما بداخله أولا ! الان انتهينا من جزء الـ Segue -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- حان الوقت الانتقال الي موضوع الـ Unwind Segue وهو الموضوع الذي اردت التحدث عنه ! كل شخص جديد يتعلم برمجة تطبيقات الـ iOS وتحديدا عند تعلمه الـ Segue وطريقة نقل البيانات من صفحة الى أخرى يخطئه هذا الخطأ !! اغلبية المبرمجين الجدد يخطئوا نفس الخطأ ! ماهو الخطأ الذي يقع به الكثيرون ؟ استخدام الـ Segue عند الرغبة في الرجوع الى الصفحة السابقة ! في هذه النقطة سوف أوضح لك الخطأ ولماذا يعتبر مصيبه عند عمله !! افتراضاً نملك 4 صفحات و ٣ Button كل Button متصل بـ Segue ينقلك الى الصفحه الأخرى كما في الصورة التالية : قد تتسأل ماهي المشكلة في الصورة السابقة ؟ الحقيقة هيا لحد الان لا توجد مشكلة ! لكن في الصورة التالية تكمن المشكلة ! المشكلة تكمن هنا عندما ينشأ المبرمج زر للرجوع الى الصفحة الرئيسية كما في الصورة التالية : ماذا فعل هنا ؟ قام بربط زر الرجوع الى صفحة رقم 0 عن طريق الـ Segue !! هذا هو الخطأ الذي اردت التحدث عنه ! هناك 3 مشاكل تسببه هذه المشكلة! أولا : كثرة الـ Segue ذهابا وعوده تسبب مشكله لذا المطور في انه لا يعلم هذا الـ Segue مرتبط بأي View Controller ؟ ثانيا : مشاكل في الأداء واستهلاك موارد الجهاز بما يسبب بطئه عند استخدام التطبيق !! ثالثا : مشاكل في Auto Layout ! ، من ضمن المشاكل التي تسببها ، مشاكل في الـ Auto Layout من الامور التي سوف تلاحظها رغم وضعك القيود بشكل صحيح الا انه لايزال يظهر خطأ في القيود ! لستُ مقتنعاً ؟ سوف أقوم الان بتشغيل التطبيق واستخدامه ذهاباً وعودة شاهد الصورة التالية : الان قم بالتالي ، لا تقوم بإيقاف تشغيل المشروع واذهب لهذه الخانة : وحرك باستخدام الماوس لملاحظة هيكل المشروع لاحظ الصورة التالية : نقوم الان بعمل Debug لهيكل المشروع من ناحية واجهة المستخدم هل لاحظت كثرت الطبقات ؟ المفترض أن تظهر فقط 5 صفحات 4 صفحات خاصة بالـ View Controller والصفحه 5 للزر الموجود في اخر صفحة بحكم اني قمت بالتوقف عندها ! ولكن الذي يظهر هو 13 طبقة ! ما السبب ؟؟ السبب هنا بسبب طريقة ربط الـ Segue ذكرت في بداية الموضوع الـ Segue يذهب من الصفحة 1 الى الصفحة 2 وليس العكس ! عند عمل العكس هذا ما سوف يحدث ! السبب عند عمل هذا الخطأ يقوم النظام على انشاء الصفحة مره أخرى وأخرى وأخرى كل مره يتم الضغط على زر الرجوع يتم انشاء الصفحة مره أخرى والنتيجة هي استهلاك موارد الجهاز !! الان قبل أن اشرح الطريقة سوف انفذ الطريقة الصحيحة ومن ثم سوف اشرحها بعد تغير الطريقة الى الطريقة الصحيحة لاحظ الصورة التالية في الـ Stroyboard: هل لاحظت ؟ لايوجد Segue ، للعودة لصفحة الرئيسية ! الان لاحظ الصورة التالية : ومن ثم لاحظ هيكل المشروع : اذا عملت مقارنه بين الصورتين هذه والصورتين السابقة سوف تلاحظ امرين أولاً : عند الضغط على زر العودة الصفحة الحالية تنزل الى الأسفل وتظهر الصفحة الرئيسية ! ، في حين عند عمل الطريقة الخاطئة تظهر الصفحة الرئيسية من الأسفل الى الأعلى !! ثانياً : هيكل المشروع اصبح 5 فقط ! لم يتغير ولم يزداد عدده ! لماذا ؟ عند عمل الطريقة الخاطئة ، تقوم على انشاء الصفحة من جديد في كل مره فكل ما زاد وقت استخدامك التطبيق زادت عدد الصفحات وزاد استهلاك موارد الجهاز في حين عند عمل الطريقة الصحيحة ، تقوم على اغلاق الصفحة الحالية والعودة للصفحة التي تريدها ، وبالتالي لا يوجد أي زياده ! بعد فهم الفكرة واتضاح الفرق نعود الى شرح الطريقة الصحيحة : لكن قبلها اريد توضيح نقطه معينه اذا اردا الرجوع الصفحة السابقة السابقة هناك 3 طرق الطريقة الاولى : استخدم هذا السطر بداخل اقواس الـ Button dismiss(animated: true, completion: nil) الزر ذا سوف يرجعك الى الصفحة السابقة فقط بما يعني اذا كنت في صفحة 3 سوف يرجعك الى صفحة 2 لا يمكنك العودة الى صفحة 0 او أي صفحة أخرى الطريقة الثانية : عند استخدام الـ Navigation Bar سوف يظهر زر العودة للصفحة السابقة بشكل تلقائي وأيضا مثل الطريقة الاولى سوف تعود فقط الى صفحة السابقة يمكن ملاحظتها في جميع تطبيقات النظام مثال تطبيق الاعدادات عندما تذهب لقسم عام او General سوف تلاحظ وجود زر في الأعلى يرجعك الى صفحة الرئيسية للإعدادات ، هذه تتم بشكل تلقائي بدون تدخل منك معلومة : لاحظ الصورة التالي : سوف تلاحظ عند استخدام الـ Navigation Bar واختيار نوع Show سوف تكون عملية الانتقال الـ Segue من اليمين الى اليسار والعوده سوف تكون العكس في حين اذا اخترت Present Modally سوف يظهر من الأسفل الى الأعلى والعكس عندها يعود للصفحة السابقة ! كما الحال في تطبيقنا لكن بدون استخدام الـ Navigation Bar الـ Show يظهر كالـ Present Modally الطريقة الثالثة : الطريقة هذه تدعى Unwind Segue ما الذي يميزها ؟ الذي يميزها هو التالي : أولا : يمكنك العودة الى أي صفحة تريدها وليس ملزماً بالعودة الى الصفحة السابقة فقط ! بما يعني كما هو حال مثالنا سوف يمكنك العودة من صفحة 3 الى صفحة 0 بشكل مباشر ثانيا: يمكنك ارجاع بيانات من الصفحة الحالي الى الصفحة السابقة ! بما يعني سوف تستطيع ارجاع بيانات موجوده في صفحة 3 الى صفحة 0 او أي صفحة تريدها !! معلومة : الـ Unwind Segue تعني فك الـ Segue وبالتالي يجب أن يكون هناك Segue لتستخدم هذه الطريقة ! اذا استخدمتها بدون عمل Segue مسبقاً بين 2 من الـ View Controller او اكثر ، سوف يسبب Crash للتطبيق الان نبدأ في شرح الطريقة كما شرحت الـ Segue سوف اقسم الـ Unwind Segue الى عدة طرق طريقة الأولى : ترغب فقط بالعوده الى صفحة معينه بدون ارجاع أي بيانات وبالتالي تحتاج تفهم هذه النقطة : اذا رغبت بالرجوع الى صفحة معينه سوف تحتاج الى كتابة كود في صفحة التي تريد الرجوع لها هنا نحن نريد العوده من أي صفحة الى الصفحة الرئيسية View Controller 0 فسوف نقوم بكتابة الكود التالي : @IBAction func unwindToHome(segue:UIStoryboardSegue) { } في ملف اكواد View Controller 0 ملاحظة : -لا تحتاج الى كتابة أي اكواد في الداخل الاقواس ! على الأقل في الوقت الحالي ! - تستطيع تسمية الـFunction بأي اسم تريده ، انا قمت بتسميته unwindToHome ومن ثم ننتقل الى الـ Storyboard ونفعل التالي مع صفحات 1 و 2و 3 شاهد الصورة : فقط هذا كل ما نحتاج الى فعله ! قم بتشغيل التطبيق وسوف تجده يعمل بمجرد الضغط على زر Return to Home سوف تجده يعود الى الصفحة الرئيسية وهيا View Controller 0 الطريقة الثانية : ماذا اذا اردت تنفيذ امراً معيناً قبل ان يحدث الـ Unwind Segue ؟ تحتاج الطريقة دي في بعض الحالات ، مثلا صفحة Login المستخدم بعد ما يكتب اسم المستخدم وكلمة السر ويضغط زر Login سوف تحتاج الى الاتصال بالسيرفر وتتأكد انه المستخدم موجود في قاعدة بياناته وبعد التأكد ، تغلق الصفحة باستخدام Unwind Segue وتحوله الى الصفحة الرئيسية معلومة : ما سبق ذكره مجرد مثال ، لأنه تستطيع أيضا استخدام سطر dismiss(animated: true, completion: nil) لأغلاق الصفحة فالمثال السابق يعتبر كطريقة أخرى لتنفيذ نفس الأمر على أي حال الطريقة مشابه لطريقة الـ Segue من حيث تحتاج الى إعطاء مُعرف نعود للشرح : اهم نقطه هنا هو فصل الربط الذي عملته في الطريقة الاولى بين الـ Button و Exit ولكن لا تحذف الـ Function الذي كتبناه في View Controller 0 @IBAction func unwindToHome(segue:UIStoryboardSegue) {} في هذه الطريقة سوف نربط الـ Viewcontroller نفسه مع الـ Exit شاهد الصورة التالية : ومن ثم سوف نعطي للـ Unwind Segue مُعرف شاهد الصورة التالية : قمت باعطاء مُعرف home الان قم بربط الـ button مع ملف الاكواد ومن ثم استخدم نفس السطر الذي استخدمناه في الـ Segue performSegue(withIdentifier: "home", sender: nil) بالطريقة هذه تستطيع عمل أي امر تريده قبل حدوث الـ Unwind Segue الطريقة الثالثة : في هذه الطريقة الامر عائد اليك ، يمكنك عمل الطريقة الاولى وتنفذ الطريقة هذه معها او استخدام الطريقة الثانية مع هذه الطريقة فالطريقة الثالثة تشرح طريقة نقل البيانات من الصفحة 3 الى صفحة 1 بنفس درجة حاجتك الى استخدام الى الـ Segue لنقل البيانات أيضا سوف تحتاج استخدام Unwind Segue لنقل البيانات من امثله استخدام الـ Unwind Segue لنقل البيانات تطبيقات المحادثه مثل الـ Whatsapp عند استخدامك للتطبيق لأول مره يطلب منك اختيار رمز دولتك فعند الضغط عليه يوجهك لصفحة تختار فيها دولتك ومن ثم عند اختيار دولتك يعود الى صفحة كتابة رقم جوالك هنا حدث Unwind Segue بحيث تم نقل رمز الدولة من صفحة أخرى الى صفحة السابقة نعود للشرح : هل تذكر ماذا فعلنا عند نقل معلومات او بيانات من صفحة 1 الى الصفحة 2 باستخدام الـ Segue ؟ الطريقة مشابهه لحد ما ! نحن نريد نقل بيانات من صفحة 3 الى صفحة 0 لذا قبل أنا نبدأ سوف نفعل التالي سوف نضيف Textfield في الصفحة 3 ونضيف Label في صفحة 0 الي هيا الصفحة الرئيسية الذي نريد الرجوع اليها ونقوم بربطهم بملف الاكواد شاهد الصورة التالية : الان حان الوقت للكتابة في Function @IBAction func unwindToHome(segue:UIStoryboardSegue) { } الان كما قلنا سابقا نريد نقل النص من الـ Textfield الموجود في View Controller 3 الى الـ Label الموجود في View Controller 0 لذا نقوم بكتابة التالي : @IBAction func unwindToHome(segue:UIStoryboardSegue) { if segue.identifier == "home" { let vc = segue.source as! ViewController3 label1.text = vc.text1.text } } اول شيء نقوم بالتأكد من اسم المُعرف في حال كنت تتبع الطريقة الاولى لن يكون هناك اسم معرف الا اذا وضعته باختيارك لذا يمكنك حذف هذا السطر if segue.identifier == "home" {} وحتى أيضا اذا اتبعت الطريقة الثانية لن تحتاج هذا السطر! الا اذا كنت تريد ارجاع بيانات من صفحتين فأكثر فهنا يتوجب عليك التمييز بينهم عن طريق التأكد من اسم المُعرف على أي حال اذا لاحظت فالكود مشابه جدا من طريقة الـ Segue في الـ Segue نقوم بكتابته بداخل Function يسمى prepare وهنا بداخل Function الـ Unwind وأيضا في الـ Segue نكتب segue.destination as اسم الكلاس الذي نريد الانتقال اليه وفي الـ Unwind Segue نكتب segue.source as اسم الكلاس الذي سوف نعود منه لذا تقريبا نفس الفكرة الاختلاف في كلمة destination و source اخيراً قم بتشغيل التطبيق وشاهد النتيجة في الصورة التالية : وبكذا انتهينا من هذا الموضوع في هذا الموضوع فهمت الطريقة الصحيحة لنقل البيانات بين الـ View Controllers وماهي الاستخدامات الخاطئة والفريق بين Segue و Unwind Segue قد تتسأل هل هذه هيا الطرق الوحيدة لنقل البيانات ؟ الاجابه هيا لا ، ما تم شرحه في الموضوع هو طرقتين لنقل البيانات Segue و Unwind Segue وهما اشهر واكثر طرقتين لنقل البيانات بين الـ View Controllers استخداماً لكن هناك طرق أخرى أيضا !
  11. ماهو ال Model View Controller (MVC)؟ MVC هو مبدأ او نموذج معماري architectural pattern يستخدم للتعامل مع واجهات المستخدم في تطبيقات iOS. هذا المبدا مهم ان تحاول فهمه لانه اساس برمجة تطبيقات ال iOS . فعندما تبدأ برمجه مشروعك عليك تقسيمه الى ثلاثة اقسام كالتالي Model : عباره عن مجموعة البيانات أو data في تطبيقك . مثلا لو لدينا تطبيق لعرض موديلات السيارات. كل المعلومات عن السياره مثل الماركه, اللون وغيرها تعتبر بيانات ويتم تخزينها في كلاس. View : عباره عن الواجهه الظاهره لمستخدم تطبيقك. في xcode تعتبر ال view هي العناصر المستخدمه في storyboard واللتي نقوم بربطها بالكود مثل UILabel, UIView and UIImage. Controller : هو الرابط او حلقة الوصل بين ال model & view اي بين البيانات والواجهات . فهو يقوم بتزويد ال view بالبيانات اللتي تحتاجها من model. ويقوم بتحديث ال model حين يدخل المستخدم بيانات جديده الى ال view هذا الجزء يعتبر الدوال او method او ال action المستخدمه في برمجه العناصر كالازرار مثلا. الان نستعرض مثال بسيط لشرح الفكره وتعميق فهمها.( من هنا تستطيع البدء والبحث عن المزيد عن هذا المفهوم وتطبيقه). لنفرض ان لدينا مشروع يستعرض ماركة و لون السياره . الان وفقا لهذا المفهوم سنقسم كالتالي Model: ننشئ class نسميه car ونضع فيه بيانات السياره (ماركه brand, لون color). View: هي واجهة المستخدم سننشئها كالتالي: وننشئ outlet لكل من ال labels كالتالي Controller: وهي الاوامر المستخدمه لربط عناصر الواجهه بالبيانات. - انشأنا object اسمه car1 من كلاس car حتى نتمكن من الوصول الى خصائص الكلاس (الماركه و اللون ). - باستخدام ال object المسمى car1 وصلنا الى خاصية brand ووضعنا فيها قيمه lexus . وخاصية color وضعنا فيها قيمه Red - الان مرحله الربط بين عناصر الواجهه والبيانات فالامر Brand.text يشير الى ان نضع في ال label الموجود في الواجهه النص الموجود في خاصية Car1.Brand وهو في هذه الحاله lexus. نفس الامر لعنصر اللون. الشكل النهائي للكود في الصوره التاليه لكن هناك ملاحظه يفضل أن تنشئ ملف سويفت منفصل لتضع فيه كلاسات ال model . الان نقوم بتشغيل التطبيق لرؤيه النتيجه اتمنى أنني وفقت في شرح هذا المفهوم المهم بطريقه سهله وبسيطه ـ شكرا لكم ولعالم البرمجه
  12. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته المنزلق Slider و شريط التقدم Progress الخصائص المشتركة بين الملحقين maxValue اعلى قيمة في المنزلق و شريط التقدم value للحصول على القيمة ( موقع شريط التقدم او المنزلق ) او تعينها valueChange تشغيل دالة عند تغيير قيمة المنزلق او شريط التقدم color لون المزلق و شريط التقدم backgroundColor لون خلفية شريط المنزلق ولون خلفية شريط التقدم خصائص الملحق Slider minValue اقل قيمة في المنزلق مثال على الملحقات ملف main-page.xml <Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo"> <Page.actionBar> <ActionBar title="عالم البرمجة - مفتاح التبديل"></ActionBar> </Page.actionBar> <StackLayout style="padding: 20px"> <Progress maxValue="100" color="orange" backgroundColor="red" value="{{ Value }}" /> <Progress maxValue="100" value="{{ Value }}" /> <Slider minValue="0" color="green" backgroundColor="black" maxValue="100" value="{{ Value }}" /> <Slider minValue="50" maxValue="150" value="{{ Value }}" /> </StackLayout> </Page> ملف main-page.js var Observable = require("data/observable").Observable; var page; // إنشاء الـ Observable var viewModel = new Observable(); function onNavigatingTo(args) { page = args.object; // تعريف الـ Observable ـ Value واسناد له قيمة viewModel.Value = 0; // ربط الـ Observable بالصفحة page.bindingContext = viewModel; } exports.onNavigatingTo = onNavigatingTo;
  13. ماهو الcallback hell، جحيم الكولباك؟ لا يوجد شيء مميز في الجافا سكربت يسمى بجحيم الكولباك ما هو الا تسميه لأمر ما قد يقع فيه المطور وهو طريقة كتابة كود حيث يحتوي على عدة دوال Asynchronous مترابطة مع بعضها، واحدة داخل الاخرى، مثال: getData1(function(x){ getData2(x, function(y){ getData3(y, function(z){ ... }); }); }); والسبب في ذلك يعود لأن ناتج "results" كل دالة يعتمد على ما سبقتها. ملاحظة: كلمة asynchronous تعني أمر يحدث لاحقا << لا تركز على هالشي مو موضوعنا. جحيم الكولباك أمر ليس بجيد للكود، خصوصا في الحالات المعقدة التي تكون فيها دوال كثيرة مربوطه مع بعضها البعض وهنا قد يؤخر وقت انتهاء من التنفيذ و كمطور انت لا تريد ان تقع في هذه المشكلة, لحل مشكلة جحيم الكولباك، يستخدم المطورون إحدى مكتبات البرومس promises "الوعد، الوعود"، فالوعد دالة إما أن يتم العمل به fulfilled مع اظهار نتيجة الوعد value او رده rejected مع إظهار السبب error، وقد يكون الكود يحتوي على أكثر من وعد، واحد تلو الآخر. شخصيا لا اعتمد على الوعود في حل مشكلة جحيم الكولباك، عند التعامل مع قواعد البيانات MySQL رغم أنها الطريقة المثلى ولكن تتطلب الكثير مع الكود "وجهة نظر" و قد تواجهك مشاكل في أداء performance التطبيق بعد ذلك, استخدم طريقة أكثر بدائية و فعالة "تفي بالغرض" ، مبدأ الفكرة و طريقة عملها تتشابه كثيرا مع مبدأ عمل promises ولكن بكود أقل، سأرفق الطريقة مع المثال بالأسفل. للتعرف أكثر على جحيم الكولباك، سأعرض لكم مثال على التعامل مع قاعدة البيانات MySQL بالـ NodeJS وعمل اكثر من query وناتج كل query يعتمد على كل سبقتها. var express = require("express"); var app = express(); var mysql = require('mysql'); var conn = mysql.createConnection({ host: 'localhost', user: 'me', password: 'secret', database: 'my_db' }); conn.connect(); // GET method route app.get('/user/posts', function (req, res) { /* http://www.example.com/user/posts?userId=26 */ var userId = req.query.userId; conn.query('SELECT username FROM users WHERE ID = ?', [userId], function (error, results, fields) { if (error) throw error; if (results.length) { var username = results[0].username; conn.query('SELECT * FROM posts WHERE post_by = ?', [username], function (error, posts, fields) { if (error) throw error; if (posts.length) { conn.query('SELECT * FROM users_addresses WHERE username = ?', [username], function (error, address, fields) { if (error) throw error; if (statistics.length) { res.json(200, { posts: posts, address: address }); } else { res.json(200, { error: 'no statistics found' }); } }); } else { res.json(200, { error: 'no posts found' }); } }); } else { res.json(200, { error: 'no user found by ID 26' }); } }); }); للتخلص على مثل هذه المشاكل في حالة التعامل مع MySQL / MariaDB يكون بإسناد results كل عملية query في دالة مثلا var express = require("express"); var app = express(); var mysql = require('mysql'); var conn = mysql.createConnection({ host: 'localhost', user: 'me', password: 'secret', database: 'my_db' }); conn.connect(); // GET method route app.get('/user/posts', function (req, res) { /* http://www.example.com/user/posts?userId=26 */ var userId = req.query.userId; conn.query('SELECT username FROM users WHERE ID = ?', [userId], function (error, results, fields) { if (error) throw error; if (results.length) { setUsername(results[0].username); } else { res.json(200, { error: 'no user found by ID 26' }); } }); function setUsername(user) { var username = user; conn.query('SELECT * FROM posts WHERE post_by = ?', [username], function (error, posts, fields) { if (error) throw error; if (posts.length) { setPosts(posts, username); } else { res.json(200, { error: 'no posts found' }); } }); } function setPosts(po, user) { var posts = po; // po is the result of the prev query and it is an object it could be length of 1 or more doesn't matter var username = user; conn.query('SELECT * FROM users_addresses WHERE username = ?', [username], function (error, address, fields) { if (error) throw error; if (statistics.length) { res.json(200, { username: username, posts: posts, address: address }); } else { res.json(200, { error: 'no statistics found' }); } }); } }); أو بطريقة اخرى وهي عمل execution لكل query بطريقة parallel او series بإستخدام مكتبة async /* Make Sure To Install async npm install async --save */ var async = require(“async”); // Parallel Method async.parallel([ wait5SecondsAndReturn1, wait5SecondsAndReturn2 ], function (err, results){ //after 5 seconds, results will be [1, 2] }); function wait5SecondsAndReturn1(callback) { setTimeout(function(){ callback(null, 1); }, 5000); } function wait5SecondsAndReturn2(callback) { setTimeout(function(){ callback(null, 2); }, 5000); } //Series Method async.series([ wait5SecondsAndReturn1, wait5SecondsAndReturn2 ], function (err, results){ //after 10 seconds, results will be [1, 2] }); function wait5SecondsAndReturn1(callback) { setTimeout(function(){ callback(null, 1); }, 5000); } function wait5SecondsAndReturn2(callback) { setTimeout(function(){ callback(null, 2); }, 5000); } // Another one async.series([ function(callback) { setTimeout(function() { console.log(“Task 1”); callback(null, 1); }, 300); }, function(callback) { setTimeout(function() { console.log(“Task 2”); callback(null, 2); }, 200); }, function(callback) { setTimeout(function() { console.log(“Task 3“); callback(null, 3); }, 100); } ], function(error, results) { console.log(results); }); وبذلك تستطيع التخلص من جحيم الكولباك بدون promises التي يلقى الكثير من المبرمجين المبتدأئين صعوبة في التعامل مع هذا النوع من الدوال. ملاحظات حول المثال السابق: الأخذ بالاعتبار جميع الشروط من ان هذا المستخدم user لدية احصائية مسجله و بوستات, ان لم يكن تستطيع التخلص من الشرط if(posts.length) لا يهم , بالتالي سيتم تمرير اوبجكت بدون قيم. بعض المبرمجين قد لا يتفق مع العملية, احترم ذلك ولكنها تفي بالغرض مع ES2015 و مايليه .. اسناد اوبجكت الى متغير يعطيه نفس قيمه الاوبجكت بشكل مباشر.
  14. لا يخفى على الجميع تفوق لغة الجافاسكربت في الاونه الاخيره " منذ مدة في الحقيقة " بعد ظهور كمية كبيرة وضخمه جدا من المكتبات و أُطُر العمل libraries/frameworks، هذا التضخم و النمو السريع في لغة الجافاسكربت سببت ارباك لكثير من المبرمجين الذين أصبحوا لا يعرفون من اين البداية لدراسة هذه الفريمووركس واين النهايه خصوصا مع وجود تشابه مابينها بسبب المنافسة، على سبيل المثال: AngularJS VueJS ReactJS حيث يعد الإثنين أطر عمل Frontend، ومع التطور ظهر أطر عمل مساعدة لاطار عمل اساسي (ان صح التعبير بالعربي) "بطتنا بطت بطن بطتكم" مثل فريمورك ExpressJS المساعد لل Runtime Environment NodeJS حيث يعمل الإثنين في السيرفر وليس جهة المستخدم، وامثلة كثيرة اخرى لا يسعنا التطرق لهم ( يخرب بيتهم كل يوم شي جديد يطلع ويصدم اكثر من الي قبله ). في هذا الدرس سنقوم بعرض احد المكتبات المبرمجة بلغة الجافاسكربت "PressureJS" لعمل / لتطبيق فكرة الـ3D Touch الموجودة في اجهزة ابل الجديدة، والـ3D Touch هو عبارة عن الضغط بقوة اكثر من المعتاده على بعض الخيارات لإظهار خيارات اكثر او عمل، تفعيل event (حدث) معين. اولا: قم بتحميل المكتبه من خلال الضغط هنا او بإستخدام npm or bower. npm npm install pressure --save bower bower install pressure --save ثانيا: طرق استخدام المكتبة Example 1: <script src="/js/pressure.js"></script> <script src="/js/jquery.pressure.js"></script> Pressure.set('#id-name', { change: function(force){ this.innerHTML = force; } }); Example 2: var Pressure = require('pressure'); Pressure.set('#id-name', { change: function(force){ this.innerHTML = force; } }); كما هو موضح، التجربة ستكون على عنصر في الصفحة بدلالة الـId وهو id-name، تحتوي الدالة اعلاه على حدث event بإسم change تحمل متغير يسمى force، هذا الحدث change يستخدم لمعرفة التغير في قوة الضغط force على العنصر id-name ويكون المتغير force رقم مابين الصفر الى 1 كحد اقصى. ملاحظة: في المثال الذي سأطرحة حاليا، كلمة this ستنعني العنصر نفسه مثلا this.style.width Pressure.set('#element', { start: function(event){ }, end: function(){ // this is called on force end }, startDeepPress: function(event){ // this is called on "force click" / "deep press", aka once the force is greater than 0.5 }, endDeepPress: function(){ // this is called when the "force click" / "deep press" end }, change: function(force, event){ // this is called every time there is a change in pressure // force will always be a value from 0 to 1 on mobile and desktop }, unsupported: function(){ // NOTE: this is only called if the polyfill option is disabled! // this is called once there is a touch on the element and the device or browser does not support Force or 3D touch } }); او عند استخدامك الـjQuery // Select Element By Id $('#element').pressure({ // events goes here }); // Select Element By Class $('.element').pressure({ // events goes here }); لنستعرض الدالة اعلاة كل جزء على حدى: - يوجد 6 انواع من الأحداث events التي تعتمد عليها / تقدمها هذه المكتبه في كلاس Pressure وهم: حدث start: لمعرفة بدأ الضغط حدث end: لمعرفة انتهاء الضغط. حدث startDeepPress: اذا كانت قوة الضغط force اعلى من 0.5 وهي اعلى من نص القيمة العليا 1. حدث endDeepPress: عند الانتهاء من الضغط على العنصر وقيمة الضغط دخلت ضمن قيمة startDeepPress اي اعلى من 0.5. حدث change: وياخذ متغير force كما شرحناه بالاعلى، هذا الحدث سيستدعى دائما عند تغير قيمه الضغط مابين 0 و 1. حدث unsupported: اذا كان المتصفح او الجهاز لايدعم ال3D Touch. - الخيارات options، بالاضافة الى الاحداث والمتغيرات السابقة هناك ايضا متغير خارج دالة الset وهو عبارة عن اوبجكت اختياري يحتوي على عدة عناصر منها polyfill اما ان يكون true او false "يستخدم اذا كان المتصفح او الجهاز لايدعم ال3D Touch ،يكون true بشكل افتراضي. Pressure.set('#example', { change: function(force, event){ this.innerHTML = force; }, unsupported: function(){ alert("Oh no, this device does not support pressure."); } }, {polyfill: false}); - المتغير الثاني في الoptions هو polyfillSpeedUp عدد ms ويستخدم لتحديد سرعة الوصول من 0 الى 1، كل 1000 تساوي ثانية واحدة. Pressure.set('#example', { change: function(force, event){ this.innerHTML = force; } }, {polyfillSpeedUp: 5000}); // takes 5 seconds to go from a force value of 0 to 1 // only on devices that do not support pressure - المتغير polyfillSpeedDown عكس SpeedUp تماما, الوقت بالميلي سكند ms لرجوع قيمة قوة الضغط من 1 إلى 0. Pressure.set('#polyfill-speed-down', { change: function(force, event){ this.innerHTML = force; } }, {polyfillSpeedDown: 2000}); // takes 2 seconds to go from a force value of 1 to 0 // only on devices that do not support pressure - المتغير only اما ان يكون mouse, touch or pointer وهذا يعني عند الضغط على العنصر المراد تطبيق الـ3D Touch عليه اما ان يكون من خلال الفأرة فقط، الضغط بالاصبع فقط او بالبوينتر فقط. // فقط بالفأرة Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'mouse'}); // فقط عند الضغط بالاصبع, اجهزة ابل Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'touch'}); // فقط بالبوينتر ديفايسس مثل اجهزة تابلت الواكوم Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'pointer'}); - متغير preventSelect يعرفه الكثير بـpreventDefault. اما ان يكون true او false , قيمة المتغير تكون true افتراضيا وتستخدم لمنع العمل الافتراضي للعناصر. Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {preventSelect: false}); الجزء الأخير من PressureJS هو دالة لعمل Configuration عام تتشارك فيه جميع العناصر يحتوي على مجموعة من الخيارات التي تم شرحها في قسم Options مثلا. // These are the default configs set by Pressure Pressure.config({ polyfill: true, polyfillSpeedUp: 1000, polyfillSpeedDown: 0, preventSelect: true, only: null }); ولكن, تستطيع عمل override لهذه الاعدادات من خلال عمل دالة Pressure.set({...}); I وإسنادها إلى مجموعة عناصر او عنصر واحد بإستخدام الـId او الـClass. انتهى .. من لديه اسئلة يتفضل هنا او على حسابي في تويتر @AbdullaScript
  15. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته ماهو ejs ؟ هو لغة قوالب صمم ليعمل من جهة الخادم تم بناءة من قبل Matthew Eernisse يتيح لك إستخدام لغة javascript داخل ملفات html بشكل شبيه جدًا بالـ PHP استخدامة مع إطار Express اول يجب ان تقوم بإضافة ejs لمشروعك بكتابة الأمر التالي npm install ejs من ثم تضيف الشفرة التالية لتطبيق express الخاص بك app.set('view engine', 'ejs'); من ثم إضافة الملفات الى مجلد views ويجب ان ينتهي الملف بالأمتداد ejs ولعمل رسم للملف بإستخدام الشفرة أستخدم .render(اسم الملف نص, المدخلات عنصر) مثال app.get('/', function(req, res) { // مع إستبدال index بإسم الملف res.render('index', {name:"عالم البرمجة"}); }); مثال لمشروع Express ملفات المشروع . ├── app.js ├── package.json ├── package-lock.json ├── node_modules ├── public │ └── images │ ├── normal.png │ └── transparent.png └── views ├── footer.ejs ├── header.ejs └── index.ejs ملف app.js var express = require('express'); var app = express(); app.set('view engine', 'ejs'); app.use(express.static("public")); app.get('/', function(req, res){ var images = [ 'transparent.png','normal.png' ]; res.render('index', { imgs: images, name:"عالم البرمجة" }); }); app.listen(80,function(){ console.log('[info] متصل على المنفذ 80'); }); ملف view/header.ejs <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title><%= title %></title> </head> <body> <p>مرحبًا بالعالم</p> <ul> <li><%= name %></li> </ul> ملف view/index.ejs <%- include('header', {title: "مثال عالم البرمجة"}) %> <ul> <% for(var i=0; i< 5; i++) {%> <li><%= i + 1 %></li> <% } %> </ul> <h1><%= name %></h1> <% imgs.forEach( (img) => { %> <img src="images/<%= img %>"/><br/> <% }); %> <%- include('footer') %> ملف footer.ejs <h5>&copy; عالم البرمجة</h5> </body> </html> بعد تشغيل التطبيق الناتج الأوسمة المخصصة لـ Ejs والدوال <% %> يقوم بتنفيذ شفرة الجافاسكربت التي بداخلة <%= %> يطبع النص الذي بداخلة ( يقوم بعمل html entities encode للأوسمة html ) <%- %> يقوم بطباعة النص الذي بداخلة كا HTML <%% يطبع '<%' بالنص الذي بداخلة متخطي عملية المعالجة %%> يطبع '%>' بالنص الذي بداخلة متخطي عملية المعالجة <%_ يقوم بحذف جميع المسافات الفارغة التي قبلة مع تنفيذ شفرة الجافاسكربت التي بداخلة _%> يقوم بحذف جميع المسافات الفارغة التي بعدة مع تنفيذ شفرة الجافاسكربت التي بداخلة include(اسم الملف نص, المدخلات عنصر) يقوم بجلب ملف ejs بعد عملية المعالجة له باقي الدوال تجدها في ViewHelpers صورة الغلاف منقولة من coligo.io
  16. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته هذا الدرس سوف يشرح لك كيفية بناء مشروع في nativescript وايضًا اضافة المنصة المستهدفة android او ios وكذلك إستعراض التطبيق في الـEmulator والتعديل على تصميم التطبيق بناء مشروع nativescript core بعد الدخول للمجلد المراد اضافة ملفات المشروع اليه بواسطة الطرفية ( للينكس والماك تسمى terminal وللويندوز تسمى cmd ) تكتب الامر التالي tns create HelloWorld ماذا يقصد بهذا الامر ؟ tns هو اختصار لـ nativescript اما t فهو للتفريق بينه وبين NS الخاص بنظام IOS اما Create فاتعني "بناء" او "انشاء" و HelloWorld هو اسم التطبيق اما اذا كنت تريد ان تستخدم nativescript core مع typescript فابإمكانك بان تضيف العلم --tsc للقيام بإستخدام القالب الافتراضي بواسطة typescript tns create HelloWorld --tsc بناء مشروع nativescript with angular لبناء مشروع nativescript مع اطار العمل angular يكفي بان تظيف العلم --ng للأمر وسوف تجده قام ببناء مشروع angular tns create HelloWorld --ng سوف اكمل الشرح بمشروع nativescript core ما الملفات اللتي سوف يقوم بإضافتها المشروع ؟ ├── app // مجلد التطبيق الاساسي │ ├── app.css // ملف css خاص بالتصميم يعمل في كافة صفحات التطبيق │ ├── app.js // ملف app المتحكم بكامل التطبيق ( n تحديد الصفحة الرائيسية للتطبيق وما الى ذلك n) │ ├── bundle-config.js │ ├── main-page.js // ملف جافاسكربت المتحكم بالصفحة main-page │ ├── main-page.xml // ملف xml المخصص لتصميم الواجهه للصفحة main-page │ ├── main-view-model.js │ ├── references.d.ts │ ├── package.json │ ├── App_Resources // سوف تجد هنا الملفات شعار التطبيق و splashscreen وملفات strings والكثير الخاصة بنظام android و ios │ │ ├── Android │ │ │ ├── AndroidManifest.xml │ │ │ ├── app.gradle │ │ │ ├── drawable-hdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-ldpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-mdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-nodpi │ │ │ │ └── splash_screen.xml │ │ │ ├── drawable-xhdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ └── values-v21 │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── iOS │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── icon-29@2x.png │ │ │ │ ├── icon-29@3x.png │ │ │ │ ├── icon-29.png │ │ │ │ ├── icon-40@2x.png │ │ │ │ ├── icon-40@3x.png │ │ │ │ ├── icon-40.png │ │ │ │ ├── icon-60@2x.png │ │ │ │ ├── icon-60@3x.png │ │ │ │ ├── icon-76@2x.png │ │ │ │ ├── icon-76.png │ │ │ │ └── icon-83.5@2x.png │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.launchimage │ │ │ │ ├── Contents.json │ │ │ │ ├── Default@2x.png │ │ │ │ ├── Default-568h@2x.png │ │ │ │ ├── Default-667h@2x.png │ │ │ │ ├── Default-736h@3x.png │ │ │ │ ├── Default-Landscape@2x.png │ │ │ │ ├── Default-Landscape@3x.png │ │ │ │ ├── Default-Landscape.png │ │ │ │ ├── Default.png │ │ │ │ ├── Default-Portrait@2x.png │ │ │ │ └── Default-Portrait.png │ │ │ ├── LaunchScreen.AspectFill.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchScreen-AspectFill@2x.png │ │ │ │ └── LaunchScreen-AspectFill.png │ │ │ └── LaunchScreen.Center.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchScreen-Center@2x.png │ │ │ └── LaunchScreen-Center.png │ │ ├── build.xcconfig │ │ ├── Info.plist │ └ └── LaunchScreen.storyboard ├── platforms // هنا المنصات ├── package.json └── package-lock.json اضافة منصة للمشروع لإضافة منصة للمشروع كل ماعليك هو الدخول لمجلد المشورع cd HelloWorld من ثم كتابة الامر التالي لإضافة منصة android tns platfrom add android او التالي لإضافة منصة ios tns platform add ios بناء المشروع لبناء المشروع لـ android اكتب tns build android او للبناء للـ ios اكتب tns build ios وسوف يبدا بعملية البناء مباشرة تعديل الصفحة الرائيسية لتعديل الصفحة الرائيسية تقوم بالتعديل على الملف main-page.xml و main-page.js المتواجدة في المجلد app <Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo" class="page"> <Page.actionBar> <ActionBar title="تطبيقي" icon="" class="action-bar"> <!-- تعديل هنا --> </ActionBar> </Page.actionBar> <StackLayout class="p-20"> <Label text="اضغط على الزر" class="h1 text-center"/> <!-- تعديل هنا --> <Button text="أضعط" tap="{{ onTap }}" class="btn btn-primary btn-active"/> <!-- تعديل هنا --> <Label text="{{ message }}" class="h2 text-center" textWrap="true"/> </StackLayout> </Page> var createViewModel = require("./main-view-model").createViewModel; // يستخدم الملف main-view-model للتحكم في binding الصفحة function onNavigatingTo(args) { var page = args.object; page.bindingContext = createViewModel(); // الأدراج هنا } exports.onNavigatingTo = onNavigatingTo; كما تلاحظ فهو يتسخدم الملف main-view-model للتحكم في binding الصفحة اذًا لنعدل عليه var Observable = require("data/observable").Observable; function getMessage(counter) { if (counter <= 0) { return "رائع! لقد حصلة على ميدالية الضاغط لـ nativescript!"; } else { return counter + " ضغطه متبقية"; } } function createViewModel() { var viewModel = new Observable(); viewModel.counter = 42; viewModel.message = getMessage(viewModel.counter); viewModel.onTap = function() { this.counter--; this.set("message", getMessage(this.counter)); } return viewModel; } exports.createViewModel = createViewModel; التطبيق بعد التعديل استعراض التطبيق في الـ Emulator او في هاتفك للأستعراض في جهاز قم فقط بتوصيل هاتفك بالحاسوب من ثم اكتب الامر التالي للأستعراض للـ android tns run android ونفس الامر من دون توصيل هاتفك بالجهاز للأستعراض في الـ Emulator ونفس الأمر للـ IOS فقط استبدل android بـ IOS سوف اقوم بشرح الامر tns run بالتفاصيل لاحقًا ان شاء الله
  17. بسم الله الرحمن الرحيم سنتعرف في هذا الدرس على طريقة انشاء تطبيق يدعم تعدد اللغات بحسب لغة الجهاز، سنعتمد اللغتين العربية والانجليزية في هذا التطبيق. سنبدأ أولا باضافة جميع اللغات التي نريد دعمها في تطبيقنا نذهب الى Project Navigator ثم نقوم باضافة اللغات المطلوبة من الأسفل نلاحظ تقسيم الملفات الى لغتين كما في الصورة االآن سنقوم بانشاء ملف من نوع String لنخزن فيه جميع الStrings باللغتين العربية والانجليزية اضغط على File > New > File واختر النوع String كما في الصورة تأكد من تسمية الملف باسم "Localizable" تماما كما هو ليتعرف عليه الxCode بشكل صحيح الآن نختار الملف الجديد من اليسار، ومن قائمة الFile Inspector نضغط على Localize ونختار اللغة الانجليزية مبدئيا الآن نختار اللغة العربية أيضا لنقوم بتقسيم ملف الLocalizable.strings الى لغتين قم باضافة الStrings المطلوبة الى الملفين بالطريقة التالية ليقوم الxCode بعمل Build تأكد من: كتابة الkey بشكل موحد بين اللغتين العربية والانجليزية وضع علامة ال= بين الkey والvalue كتابة الvalue بين علامتي تنصيص "" انهاء السطر بSemi-Colon ; لنرى طريقة استدعاء الStrings من ملف Localizable قمت بعمل الواجهة البسيطة التالية: وقمت بربط الOutlets والActions كالتالي: الآن اضف الكود التالي في زر اللغة الانجليزية: let path = Bundle.main.path(forResource: "en", ofType: "lproj") let bundle = Bundle.init(path: path!)! as Bundle nameLbl.text = bundle.localizedString(forKey: "WebsiteName", value: nil, table: nil) والتالي لزر اللغة العربية: let path = Bundle.main.path(forResource: "ar", ofType: "lproj") let bundle = Bundle.init(path: path!)! as Bundle nameLbl.text = bundle.localizedString(forKey: "WebsiteName", value: nil, table: nil) في السطر الأول: قمنا بتعريف الpath الذي سيوصلنا لكل من ملفات اللغة العربية والانجليزية (يمكنك معرفة الResource والType من قائمة الFile Inspector عند الضغط على ملفي العربية والانجليزية) في السطر الثاني قمنا بتعريف كل من ملفات اللغتين كVariable يحمل الاسم bundle في السطر الثالث قمنا باستدعاء الString المطلوب باستخدام الميثود localizedString(_:String,_:String,_:Strint( والآن عند تجربة البرنامج نرى النتيجة التالية GIF: والآن سنقوم بتغيير النص اعتمادا على لغة الجهاز باستخدام ميثود واحدة فقط لنرى ذلك قمت بتغيير الStoryboard ليحتوي على زر واحد فقط، وقمت باضافة الoutlets والactions كالتالي: والآن قم باضافة السطرين التاليين للميثود viewDidLoad() let btnTitle: String = NSLocalizedString("ButtonText", comment: "") languageBtn.setTitle(btnTitle, for: .normal) والسطر التالي للميثود الخاصة بالbutton action nameLbl.text = NSLocalizedString("WebsiteName", comment: "") في هذه الاكواد، قمنا باستعمال الميثود NSLocalizedString لاستدعاء الString المناسب بحسب لغة الجهاز لتجربة البرنامج سنقوم بتشغيله على جهاز بلغة انجليزية، ثم سنحولها للعربية ونعيد التجربة وصلنا الى ختام الدرس، أستودعكم الله الذي لا تضيع ودائعه. تم ترقية هذا الطرح المميز الى صفحة المقالات
  18. لا يخفى على الجميع تفوق لغة الجافاسكربت في الاونه الاخيره " منذ مدة في الحقيقة " بعد ظهور كمية كبيرة وضخمه جدا من المكتبات و أُطُر العمل libraries/frameworks، هذا التضخم و النمو السريع في لغة الجافاسكربت سببت ارباك لكثير من المبرمجين الذين أصبحوا لا يعرفون من اين البداية لدراسة هذه الفريمووركس واين النهايه خصوصا مع وجود تشابه مابينها بسبب المنافسة، على سبيل المثال: AngularJS VueJS ReactJS حيث يعد الإثنين أطر عمل Frontend، ومع التطور ظهر أطر عمل مساعدة لاطار عمل اساسي (ان صح التعبير بالعربي) "بطتنا بطت بطن بطتكم" مثل فريمورك ExpressJS المساعد لل Runtime Environment NodeJS حيث يعمل الإثنين في السيرفر وليس جهة المستخدم، وامثلة كثيرة اخرى لا يسعنا التطرق لهم ( يخرب بيتهم كل يوم شي جديد يطلع ويصدم اكثر من الي قبله ). في هذا الدرس سنقوم بعرض احد المكتبات المبرمجة بلغة الجافاسكربت "PressureJS" لعمل / لتطبيق فكرة الـ3D Touch الموجودة في اجهزة ابل الجديدة، والـ3D Touch هو عبارة عن الضغط بقوة اكثر من المعتاده على بعض الخيارات لإظهار خيارات اكثر او عمل، تفعيل event (حدث) معين. اولا: قم بتحميل المكتبه من خلال الضغط هنا او بإستخدام npm or bower. ثانيا: طرق استخدام المكتبة Example 1: <script src="/js/pressure.js"></script> <script src="/js/jquery.pressure.js"></script> Pressure.set('#id-name', { change: function(force){ this.innerHTML = force; } }); Example 2: var Pressure = require('pressure'); Pressure.set('#id-name', { change: function(force){ this.innerHTML = force; } }); كما هو موضح، التجربة ستكون على عنصر في الصفحة بدلالة الـId وهو id-name، تحتوي الدالة اعلاه على حدث event بإسم change تحمل متغير يسمى force، هذا الحدث change يستخدم لمعرفة التغير في قوة الضغط force على العنصر id-name ويكون المتغير force رقم مابين الصفر الى 1 كحد اقصى. ملاحظة: في المثال الذي سأطرحة حاليا، كلمة this ستنعني العنصر نفسه مثلا this.style.width Pressure.set('#element', { start: function(event){ }, end: function(){ // this is called on force end }, startDeepPress: function(event){ // this is called on "force click" / "deep press", aka once the force is greater than 0.5 }, endDeepPress: function(){ // this is called when the "force click" / "deep press" end }, change: function(force, event){ // this is called every time there is a change in pressure // force will always be a value from 0 to 1 on mobile and desktop }, unsupported: function(){ // NOTE: this is only called if the polyfill option is disabled! // this is called once there is a touch on the element and the device or browser does not support Force or 3D touch } }); او عند استخدامك الـjQuery // Select Element By Id $('#element').pressure({ // events goes here }); // Select Element By Class $('.element').pressure({ // events goes here }); لنستعرض الدالة اعلاة كل جزء على حدى: - يوجد 6 انواع من الأحداث events التي تعتمد عليها / تقدمها هذه المكتبه في كلاس Pressure وهم: حدث start: لمعرفة بدأ الضغط حدث end: لمعرفة انتهاء الضغط. حدث startDeepPress: اذا كانت قوة الضغط force اعلى من 0.5 وهي اعلى من نص القيمة العليا 1. حدث endDeepPress: عند الانتهاء من الضغط على العنصر وقيمة الضغط دخلت ضمن قيمة startDeepPress اي اعلى من 0.5. حدث change: وياخذ متغير force كما شرحناه بالاعلى، هذا الحدث سيستدعى دائما عند تغير قيمه الضغط مابين 0 و 1. حدث unsupported: اذا كان المتصفح او الجهاز لايدعم ال3D Touch. - الخيارات options، بالاضافة الى الاحداث والمتغيرات السابقة هناك ايضا متغير خارج دالة الset وهو عبارة عن اوبجكت اختياري يحتوي على عدة عناصر منها polyfill اما ان يكون true او false "يستخدم اذا كان المتصفح او الجهاز لايدعم ال3D Touch ،يكون true بشكل افتراضي. Pressure.set('#example', { change: function(force, event){ this.innerHTML = force; }, unsupported: function(){ alert("Oh no, this device does not support pressure."); } }, {polyfill: false}); - المتغير الثاني في الoptions هو polyfillSpeedUp عدد ms ويستخدم لتحديد سرعة الوصول من 0 الى 1، كل 1000 تساوي ثانية واحدة. Pressure.set('#example', { change: function(force, event){ this.innerHTML = force; } }, {polyfillSpeedUp: 5000}); // takes 5 seconds to go from a force value of 0 to 1 // only on devices that do not support pressure - المتغير polyfillSpeedDown عكس SpeedUp تماما, الوقت بالميلي سكند ms لرجوع قيمة قوة الضغط من 1 إلى 0. Pressure.set('#polyfill-speed-down', { change: function(force, event){ this.innerHTML = force; } }, {polyfillSpeedDown: 2000}); // takes 2 seconds to go from a force value of 1 to 0 // only on devices that do not support pressure - المتغير only اما ان يكون mouse, touch or pointer وهذا يعني عند الضغط على العنصر المراد تطبيق الـ3D Touch عليه اما ان يكون من خلال الفأرة فقط، الضغط بالاصبع فقط او بالبوينتر فقط. // فقط بالفأرة Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'mouse'}); // فقط عند الضغط بالاصبع, اجهزة ابل Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'touch'}); // فقط بالبوينتر ديفايسس مثل اجهزة تابلت الواكوم Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'pointer'}); - متغير preventSelect يعرفه الكثير بـpreventDefault. اما ان يكون true او false , قيمة المتغير تكون true افتراضيا وتستخدم لمنع العمل الافتراضي للعناصر. Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {preventSelect: false}); الجزء الأخير من PressureJS هو دالة لعمل Configuration عام تتشارك فيه جميع العناصر يحتوي على مجموعة من الخيارات التي تم شرحها في قسم Options مثلا. // These are the default configs set by Pressure Pressure.config({ polyfill: true, polyfillSpeedUp: 1000, polyfillSpeedDown: 0, preventSelect: true, only: null }); ولكن, تستطيع عمل override لهذه الاعدادات من خلال عمل دالة Pressure.set({...}); I وإسنادها إلى مجموعة عناصر او عنصر واحد بإستخدام الـId او الـClass. انتهى .. من لديه اسئلة يتفضل هنا او على حسابي في تويتر @AbdullaScript
  19. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته الـ Switch او مفتاح التبديل هو مو زر يرجع يقيمتين هما false و true ويستخدم عادتًا في اعدادات التطبيق الخاص بك خصائص الملحق color لون الزر checked يحتوي قيمتين هما true و false true عندما يكون المفتاح مفعل false عندما يكون المفتاح غير مفعل style لتعيين خصائص CSS مخصصة له وحدة id لتعيين تعريف للملحق isEnabled حالة تفعيل المفتاح مثال على الملحق ملف main-page.xml <Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo"> <Page.actionBar> <ActionBar title="عالم البرمجة - مفتاح التبديل"></ActionBar> </Page.actionBar> <StackLayout> <Switch checked="{{ checked }}" /> <Button isEnabled="{{ checked }}" text="الولوج الى عالم البرمجة" /> </StackLayout> </Page> ملف main-page.js var Observable = require("data/observable").Observable; var page; // إنشاء الـ Observable var viewModel = new Observable(); function onNavigatingTo(args) { page = args.object; // تعريف الـ Observable ـ checked واسناد له قيمة viewModel.checked = false; // ربط الـ Observable بالصفحة page.bindingContext = viewModel; } exports.onNavigatingTo = onNavigatingTo;
  20. بسم الله رحمن الرحيم السلام عليكم ورحمة الله وبركاته SearchBar او شريط البحث كما يوحي اسمه هو شريط مخصص للبحث خصائص الملحق text للحصول على النص أو تعيينه hint نص للتوضيح textChange تشغيل دالة عندما يتم ادخال نص submit تشغيل دالة عند النقر على زر بحث clear تشغيل دالة عند افراغ الحقل او الضغط على زر حذف النص مثال على الملحق ملف main-page.xml <Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo"> <Page.actionBar> <ActionBar title="عالم البرمجة - شريط البحث"></ActionBar> </Page.actionBar> <StackLayout> <!-- وسم searchbar لشريط البحث --> <!-- ربط الدالو onClear و onSubmit بالصفحة لتعمل عند الحدث --> <SearchBar id="searchBar" hint="بحث" clear="onClear" submit="onSubmit" /> <!-- وسم label ليظهر ناتج results --> <Label text="{{ results }}" textWrap="true" /> </StackLayout> </Page> ملف main-page.js var Observable = require("data/observable").Observable; var page; // إنشاء الـ Observable var viewModel = new Observable(); function onNavigatingTo(args) { page = args.object; // تعريف الـ Observable ـ results واسناد له قيمة viewModel.results = "الناتج"; // ربط الـ Observable بالصفحة page.bindingContext = viewModel; } function Search(args){ // جلب الـ SearchBar من نقس حدث البحث والحذف var searchbar = args.object; // اسناد النص الذي بداخل الـ SearchBar بالـ results viewModel.set('results', searchbar.text); } // عمل export للدوال لإستخدامها داخل الصفحة exports.onSubmit = Search; exports.onClear = Search; exports.onNavigatingTo = onNavigatingTo;
  21. ماهو ال Model View Controller (MVC)؟ MVC هو مبدأ او نموذج معماري architectural pattern يستخدم للتعامل مع واجهات المستخدم في تطبيقات iOS. هذا المبدا مهم ان تحاول فهمه لانه اساس برمجة تطبيقات ال iOS . فعندما تبدأ برمجه مشروعك عليك تقسيمه الى ثلاثة اقسام كالتالي Model : عباره عن مجموعة البيانات أو data في تطبيقك . مثلا لو لدينا تطبيق لعرض موديلات السيارات. كل المعلومات عن السياره مثل الماركه, اللون وغيرها تعتبر بيانات ويتم تخزينها في كلاس. View : عباره عن الواجهه الظاهره لمستخدم تطبيقك. في xcode تعتبر ال view هي العناصر المستخدمه في storyboard واللتي نقوم بربطها بالكود مثل UILabel, UIView and UIImage. Controller : هو الرابط او حلقة الوصل بين ال model & view اي بين البيانات والواجهات . فهو يقوم بتزويد ال view بالبيانات اللتي تحتاجها من model. ويقوم بتحديث ال model حين يدخل المستخدم بيانات جديده الى ال view هذا الجزء يعتبر الدوال او method او ال action المستخدمه في برمجه العناصر كالازرار مثلا. الان نستعرض مثال بسيط لشرح الفكره وتعميق فهمها.( من هنا تستطيع البدء والبحث عن المزيد عن هذا المفهوم وتطبيقه). لنفرض ان لدينا مشروع يستعرض ماركة و لون السياره . الان وفقا لهذا المفهوم سنقسم كالتالي Model: ننشئ class نسميه car ونضع فيه بيانات السياره (ماركه brand, لون color). View: هي واجهة المستخدم سننشئها كالتالي: وننشئ outlet لكل من ال labels كالتالي Controller: وهي الاوامر المستخدمه لربط عناصر الواجهه بالبيانات. - انشأنا object اسمه car1 من كلاس car حتى نتمكن من الوصول الى خصائص الكلاس (الماركه و اللون ). - باستخدام ال object المسمى car1 وصلنا الى خاصية brand ووضعنا فيها قيمه lexus . وخاصية color وضعنا فيها قيمه Red - الان مرحله الربط بين عناصر الواجهه والبيانات فالامر Brand.text يشير الى ان نضع في ال label الموجود في الواجهه النص الموجود في خاصية Car1.Brand وهو في هذه الحاله lexus. نفس الامر لعنصر اللون. الشكل النهائي للكود في الصوره التاليه لكن هناك ملاحظه يفضل أن تنشئ ملف سويفت منفصل لتضع فيه كلاسات ال model . الان نقوم بتشغيل التطبيق لرؤيه النتيجه اتمنى أنني وفقت في شرح هذا المفهوم المهم بطريقه سهله وبسيطه ـ شكرا لكم ولعالم البرمجه
  22. بسم الله الرحمن الرحيم عندما نريد إنشاء instance من class معين فإننا غالبا ما نقوم بإستخدام public constructor, لكن هنا طريقة أخرى, يجب أن يكون المبرمج ملماً بها, ألا وهي public static factory method. ببساطة شديدة هي دالة static تقوم بإرجاع -return- لـ instance من class, على سبيل المثال: public class Client { public static Client getInstance() { return new Client(); } } كما نلاحظ أن المفهوم في غاية البساطة, لكن متى أحتاج أن أستخدمه ؟ , سنقوم الان بالمقارنة بينه وبين Constructor, ونستعرض المميزات والعيوب. أولا, تتميز static factory method بكونها قابلة لتسمية, فعلى سبيل المثال لو كان لدينا مركز رياضي ونوعين من العملاء, مشترك وزائر, المشترك ندخل أسمه, أما الزائر فنكتب فقط زائر, فسيكون شكل class كالتالي public class Client { private String name; public static Client getVisitorInstance() { Client client = new Client(); client.name = "visitor"; return client; } public static Client getSubscribedInstance(String name) { Client client = new Client(); client.name = name; return client; } } دعنا نقوم بإستدعائها, واستدعاء constructor. Client client1 = Client.getVisitorInstance(); Client client2 = Client.getSubscribedInstance("Abdulaziz"); Client client3 = new Client(); Client client4 = new Client("Abdulziz"); كما نلاحظ, في static factory method من النظرة الأولى, تمكنت من معرفة نوع العميل, بالإضافة إلى أنه لو أراد شخص ما إكمال تطوير المشروع, سيتمكن من التعامل مع class الذي قمت ببنائه دون النظر فيه -Readability-, على عكس public constructor. ثانيا, من خلال static factory method يمكنك أن تجبر مستخدم الـ class على إنشاء instance واحد فقطلهذا الـ class, وهو ما نسميه بـ singleton, كما في المثال public class Client { private static final Client client = new Client(); private Client(){ } public static Client getClient() { return client; } } كما نلاحظ أن constructor private, بمعنى أنه لا يمكنك إنشاء instance عن طريقه, لكن تستخدم static factory method والتي دائما ستعيد لك نفس object الذي هو client, وبهذا لن تحصل إلا على object واحد فقط عن طريق هذا class. ثالثا, يمكن لـ static factory method أن يقوم بعمل return لـ instance من sub-type لـ class, على سبيل المثال لدينا class يقوم بإزالة الزوائد من القيم الداخلة إليه, مثلا لو تلقى قيمة 0010 يقوم بحذف الأصفار, ولو تلقى “محمد ” يقوم بحذف space وهكذا, لنبدأ في الكود. بداية سنقوم بإنشاء Interface بإسم trimmer public interface Trimmer<T> { T trim(); } الان, سنقوم بإنشاء class لتعامل مع String public class StringTrim implements Trimmer<String> { String value; StringTrim(String value) { this.value = value; } @Override public String trim() {...} } وأخر لتعامل مع Integer public class IntegerTrim implements Trimmer<Integer> { Integer value ; IntegerTrim(Integer value) { this.value = value; } @Override public Integer trim() {...} } نلاحظ أن access modifier لـ constructor من نوع package-private أي لا يمكن الوصول إليه من خارج package, ثم نقوم بإنشاء class نستخدمه إذا ما أردنا الإستفادة من classes السابقة public class Trimmers { private Trimmers(){} public static Trimmer<String> getTrimString(String s){ return new StringTrim(s); } public static Trimmer<Integer> getTrimInteger(Integer i){ return new IntegerTrim(i); } } نلاحظ هنا كيف استفدنا من static factory method, بحث أنه قام بإرجاع object, عبارة عن sub-type لـ return type. وفي main نستدعيه بالطريقة التالية: Integer i = Trimmers.getTrimInteger(0150).trim(); String s = Trimmers.getTrimString(" abdulaziz").trim(); وهذا الأسلوب يسمى interface-based API أو interface-based frameworks بعد استعراضات المميزات لـ static factory method, لنستعرض السلبيات, وهما اثنتين. الأولى, لا يمكن الوراثة -inheritance- من class لا يوجد فيه public or protected constructor, ففي هذه الحالة لا يمكن لـ static factory method أن تعوض constructor. الثانية, أن static factory method قد لا يمكن تمييزها بسهولة عن بقية static methods التي بداخل class, لذلك تستخدم عادة بعض التسميات المتعارف عليها, لتحل هذه المشكلة. الان, لنمثل علىstatic factory method في Android, فلو أردنا أن نقوم بإنشاء activity داخلها fragment سيكون الكود كالتالي public class MainActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); FragmentManager manager = getSupportFragmentManager(); Fragment fragment = manager.findFragmentById(R.id.fragment); if (fragment == null){ fragment = new BlankFragment(); manager.beginTransaction().add(R.id.fragment,fragment).commit(); } } } وفي Activity أخرى public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_secound); FragmentManager manager = getSupportFragmentManager(); Fragment fragment = manager.findFragmentById(R.id.fragment); if (fragment == null){ fragment = new BlankFragment(); manager.beginTransaction().add(R.id.fragment,fragment).commit(); } } } لنقوم الان بتحسين هذا الكود, سنقوم بالتالي, أولا بناء static factory method داخل fragment public static BlankFragment newInstance() { BlankFragment fragment = new BlankFragment(); return fragment; } ثم نقوم ببناء BaseActivity كالتالي public abstract class BaseActivity extends AppCompatActivity { protected abstract Fragment createFragment(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity); FragmentManager manager = getSupportFragmentManager(); Fragment fragment = manager.findFragmentById(R.id.fragment); if (fragment == null){ fragment = createFragment(); manager.beginTransaction() .add(R.id.fragment,fragment) .commit(); } } } لاحظ هنا أن هناك دالة ستعيد لنا fragment سنستخدمها داخل oncreate, وستظهر فائدة هذا الشيء عندما نجعل MainActivityb و SecoundActivity ترث منها, فيكون شكل الكود كالتالي public class SecondActivity extends BaseActivity { @Override protected Fragment createFragment() { return BlankFragment.newInstance(); } } public class MainActivity extends BaseActivity { @Override protected Fragment createFragment() { return BlankFragment.newInstance(); } } إلى هنا أتمنى أن أكون وفقت في تبسيط هذا المفهوم, والسلام عليكم ورحمة الله وبركاته.المصادر: Big nerd ranch Android Effective java Static factory methods vs traditional constructors
  23. شرح الـ Interface في جافا ما هو الـ interface؟ الـ interface يشبه كثيرا الـ abstract class في أنه يحدد خصائص مشتركة للكائنات (objects) في كلاسات لها علاقةوراثية ببعضها (Inheratance) بالإضافة إلى أنه يحدد يحدد خصائص مشتركة للكائنات (objects) في كلاسات ليس بالضرورة أن يكون لها علاقة ببعضها وهو ما يميز الـ Interface. لم تضح الصورة لك بعد؟ لا بأس سأشرح لك بمثال لو كان عندي Abstract class اسميته Fruits وهو (parent class (super class لمجموعة من الفواكه هي الموز والبرتقال (child classes) وأنشأت Abstract class آخر أسميته Animals وهو (parent class (super class لمجموعة من الحيوانات الدجاجة والأسد (child classes)، ثم أنشأت Interface أسميته Eddible (قابل للأكل). لاحظ أن الموز والبرتقال بإمكاننا أن نقول أنها فواكه لأن العلاقة هنا وراثية (Inheratance) فأي موزة هي فاكهة وأي برتقال هو فاكهة، وأيضا بإمكاننا أن نقول أن الموز والبرتقال قابلان للأكل. والشئ نفسه ينطبق على الدجاجة والأسد في كونهما حيوانات لأن العلاقة وراثية فأي دجاجة هي حيوان وأي أسد هو حيوان،وأيضا بإمكاننا أن نقول أن الدجاجة قابلة للأكل لكن لا يمكننا القول بأن الأسد قابل للأكل فهنا الـEdible interface سيصلح فقط للدجاجة دون الأسد. إن ما أردت توضيحه هو أن الفواكه والحيوانات ليس لها رابطة واضحة أو خصائص مشتركة لكن الـ Interface أوجد خاصية مشتركة بينها وهي قابلية الأكل وهذا ما يميز الـInterface عن الـabstract class. لماذا نستخدم الـ interface؟ ببساطة لأن Java لايدعم Multiple Inheratance (الوراثة المتعددة)، وفي أغلب الأحيان نحتاج لأكثر من كلاس لنأخذ منه خصائص مشتركة فالحل هو الـInterface. *ملاحظة: بإمكان الكلاس أن يكون له علاقة وراثية واحدة فقط أي أن يكون له Super classs واحد فقط، بينما بإمكانه أن يكون متعدد الـInterfaces وتسمى هذه العلاقة Interface Inheratance. كيف ننشئ الـ interface؟ الصيغة العامة للـInterface هي: مثال: كيف نستخدم الـ interface؟ ليستخدم الكلاس الـInterface المطلوب نستخدم كلمة implements بعد اسم الكلاس وقبل اسم الـ Ineterface كما هو موضح في المثال. وبطبيعة الحال بما أن في الـInterface يوجد Abstarct Method فيجب علينا أن ننفذ Implementation للميثود في الكلاس الذي استدعى الـInterface وإلا ستبقى الميثود abstract وسيتحول الكلاس إلى abstract class. مثال: main class: Output: *المصادر: كتاب Introduction to Java programming, Tenth Edition, Y. Daniel Liang.
  24. بسم الله الرحمن الرحيم كما نعرف فإن لغة السويفت Swift تدعم الأنواع الأساسية من المتغيرات مثل : Int - Float - Double - Bool - String - Character كما تدعم أنواع مختلفة أخرى ، وتدعم ٣ أنواع أساسية تُسمى بـ ( Collection Types ) وهم : المصفوفات Arrays - القواميس Dictionaries - المجموعات Set سأتحدث في هذه المقالة عن ( القواميس Dictionaries ) ، وسأتكلم فيها عن : ماهي القواميس Dictionaries ؟ كيفية تعريف وإنشاء القواميس Dictionaries : إنشاء قواميس فارغة . إنشاء قواميس بقيم إبتدائية . إضافة عناصر جديدة للقواميس . إزالة قيم القواميس . ماهي القواميس Dictionaries ؟ كما قلنا سابقاً فإن القواميس أو ماتعرف بـ Dictionaries هي إحدى أنواع المتغيرات في لغة السويفت Swift ، يُستخدم هذا النوع في تخزين عدد من القيم (غير المرتبة unorderd) في متغير واحد ،ويتم تخزين كل قيمة باستخدام مُعرف فريد يُعرف بـ ( المفتاح Key) وذلك ليسهل الوصول لهذه القيمة عند الحاجة إليها . ولتسهيل فكرة القواميس Dictionaries قمت بعمل رسم توضيحي يشرح فكرتها : في الرسمه السابقة نرى بأن لدينا ( متغير ) من نوع dictionary بحيث احتوى على ( ٣ مفاتيح ) وكل مفتاح يشير إلى ( قيمة ) ، بحيث نستطيع الوصول مثلاً (للقيمة ١) فقط عن طريق ( المفتاح ١) و (القيمة ٢) عن طريق (المفتاح ٢) و (القيمة ٣) عن طريق ( المفتاح ٣) وهكذا . وبالتالي استطعنا في متغير واحد تخزين أكثر من قيمة والوصول لهذه القيم باستخدام المفاتيح Keys . سألخص لك الكلام السابق على شكل نقاط : القواميس Dictionaries إحدى أنواع المتغيرات في لغة السويفت Swift . تقوم بتخزين عدد من القيم غير المرتبة unorderd داخل متغير واحد . يتم الوصول لكل قيمة من قيم القواميس عن طريق معرف فريد يعرف بالمفتاح Key . اتمنى أن تكون فكرة القواميس Dictionaries أصبحت واضحة وإن لم تكن كذلك فبإذن الله ستتضح لك أكثر مع الأمثلة 😊 كيفية تعريف وإنشاء القواميس Dictionaries : قبل أن نقوم بالتعرف على طريقة إنشاء القواميس سأذكر لك عدّة نقاط مهمة جداً يجب عليك أن تضعها في الاعتبار قبل تعريف أي Dictionary : أولاً : أسماء المفاتيح Keys يجب أن تكون فريدة uniqe . ثانياً : في المتغير من نوع Dictionary يجب أن تكون ( كل ) المفاتيح key من نفس النوع ، و ( كل ) القيم من نفس النوع ، مثلاً أن تكون كل المفاتيح من نوع Int والقيم من نوع String فهذا صحيح لأن كل المفاتيح من نفس النوع وهو Int و كل القيم من نفس النوع وهو String . ثالثاً : من المسموح أيضاً أن تكون جميع المفاتيح وجميع القيم من نفس النوع مثلاً أن تكون المفاتيح والقيم من نوع String . والآن فلنبدأ بإنشاء القواميس Dictionaries ! 1. إنشاء قواميس فارغة : لتعريف القواميس نستخدم مايعرف بالأقواس المربعة [ ] لتحديد نوع المفاتيح والقيم للمتغير : مثال (١) : var x = [Int : String] ( ) في المثال السابق قمنا بتعريف المتغير x وعرّفنا المفاتيح من نوع Int والقيم من نوع String ( وليس العكس ) و استخدمنا الأقواس التالية : ( ) لنقول بأن المتغير هذا لايحتوى على أي قيمة إلى الآن . مثال (٢) : var y = [String : String] ( ) قمنا بتعريف المتغيرy وعرّفنا المفاتيح من نوع String والقيم أيضاً من نوع String ،و استخدمنا الأقواس التالية : ( ) لنقول بأن المتغير هذا لايحتوى على أي قيمة إلى الآن . 2. إنشاء قواميس بقيم إبتدائية : ولإضافة قيم للقواميس سنتبع الصيغة التالية : سنقوم الآن بتعديل الأمثلة السابقة ووضع قيم ابتدائية لها، وسيكون ذلك كالتالي : مثال (١) : var x : [Int : String] = [ 1 : "A" , 2: "B" , 3 : "C" ] المتغير x يحتوى على المفاتيح التالية : المفتاح 1 والذي يشير للقيمة A والمفتاح 2 والذي يشير للقيمة B والمفتاح 3 والذي يشير للقيمة C . مثال (٢) : var y : [String : String] = [ "One" : "A" , "Two" : "B" , "Three" : "C" ] المتغير y يحتوي على المفاتيح التالية : المفتاح One والذي يشير للقيمة A والمفتاح Two والذي يشير للقيمة B والمفتاح Three والذي يشير للقيمة C . من الأمثلة السابقة نلاحظ مايلي : لتعيين القيم للمتغير نستخدم ( = ) . نضع القيم بين الأقواس كالتالي : ] المفتاح : القيمة [ ونفصل بينهم بفاصلة ( , ) . المفاتيح والقيم لها نفس النوع الذي تم تعريفه ، غير ذلك سيظهر لنا خطأ error. ( كل ) المفاتيح في المتغير الواحد كانت من نفس النوع و ( كل ) القيم أيضاً كانت من نفس النوع . إضافة عناصر جديدة للقواميس : من المهم معرفة أن القواميس لا تستخدم الأمر insert أو الأمر append لإدراج عناصر جديدة مثل ماتفعل المصفوفات ،والسبب في ذلك كما ذكرنا بداية هذه المقالة بأن القواميس تتميز بأنها غير مرتبة unordered بعكس المصفوفات التي تتميز بأنها مرتبة orderd لذلك لادراج قيم في القواميس سنتبع الطريقة التالية : مثال (١) : x [ 4 ] = "Four" هنا أصبح لدينا في المتغير x مفتاح (4) وقيمته هي . Four مثال (٢) : y ["Four"] = "D" هنا أصبح لدينا في المتغير y مفتاح Four)) وقيمته هي . D إزالة قيم القواميس : كما عرفنا فإن القيم في القواميس مرتبطة بمفتاح key خاص فيها لذلك عند حذف أي قيمه فإننا نتعامل مع المفتاح الخاص فيها وذلك بجعله يساوي nil : Variable-Name [Key] = nil مثال (١) : x [ 4 ] = nil مثال (٢) : y ["Four"] = nil الطريقة الأخرى وهي استخدام الدالة( removeValue (ForKey :Key_name ، هذه الدالة تقوم بحذف القيمة المطلوبة إن كانت موجودة أو تسترجع القيمة nil في حالة لم تكن موجودة . Variable-Name.removeValue (ForKey : Key_name) مثال (١) : x.removeValue (ForKey :4) مثال (٢) : y.removeValue (ForKey :"Four") وصلنا لنهاية المقالة أتمنى أن أكون قد وفقت في إيصال المعلومات بشكل واضح وسهل للجميع ،وبإذن الله سيكون هناك جزء ثاني لهذه المقالة نتعمق فيها أكثر في القواميس .
  25. أبو معتز مطور تطبيقات أندرويد وهو الآن في منتصف بناء تطبيق أندرويد، وصل للميزة ص، برمجها واختبرها وتأكد من أنها تؤدي الغرض المراد منها مبدئياً، ولا مشاكل ظاهرة حتى اللحظة، وأثناء تلك اللحظة -وهو يرتشف الشاي الأحمر مع مسحوق أوراق النعناع الخضراء المجففة- تبادر إلى ذهنه السؤال التالي: لماذا أداء ومظهر هذه الميزة لا يشبه مثيلاتها في تطبيقات المبرمجين الآخرين المُحملة على جوالي؟ ملحوظة جانبية: هذه المقالة تتكون من جزئين: نظري وعملي. الجزء النظري: إذا مررت بالحالة السابقة فهذه المقالة -إن شاء الله- دليلك لاختيار المكتبة (Library) أو المكتبات المناسبة لمشروعك. لذا قبل أن تعيد اختراع العجلة أو تبدأ من الصفر أو تُهدر وقتاً وجهداً، دعنا نرجع إلى الميزة ص وقبل الشروع في برمجتها، اسأل نفسك الأسئلة التالية: هل هذه الميزة موجودة في تطبيقات أخرى؟ هل تقوم بنسخ ولصق كود هذه الميزة بشكل متكرر في تطبيقاتك؟ هل تظن باحتمالية برمجة هذه الميزة من قبل مبرمج آخر؟ إذا كانت الإجابة نعم لأحد أو كل هذه الأسئلة فهذا مؤشر على أن هناك مكتبة توفر هذه الميزة، والسؤال الذي ينبغي أن يُطرح: كيف أبحث عنها؟ وما هي مواصفات أفضل مكتبة مناسبة لتطبيقي؟ أما طريقة البحث فيمكن من خلال المراجع التالية لاستكشاف المكتبات المناسبة: البحث في موقع: stackoverflow صفحة على GitHub بعنوان: Must Have Libraries موقع: android-arsenal.com لديك مصدر آخر؟ أرجو ذكره في التعليقات 😉 لكن كيف أتأكد أن المكتبة التي اخترتها هي المكتبة المناسبة لمشروعي؟ هناك مواصفات ونصائح للمكتبة المناسبة يمكن إجمالها في النقاط التالية: الصيانة Maintenance أندرويد نظام يتطور باستمرار وكذا ينبغي لمكتباتها، فبعض المكتبات تتوقف عن التطور عند إصدار نسخة جديدة من أندرويد، بالتالي تتوقف صيانتها وتُهجر [deprecated]، فإذا كنت تستخدم واحدة من هذه المكتبات المهجورة فسيتعطل تطوير تطبيقك بلا شك. من الذي يقوم بصيانة المكتبة؟ هل هو شخص واحد؟ أو مجموعة أفراد؟ أو شركة مثل: Google أو Square؟ المعنى أنه كلما كان مجتمع المكتبة أكبر كلما كانت صيانة المكتبة مستمرة. هل صيانة المكتبة مستمرة؟ ومتى كان آخر تحديث لها؟ بإمكانك معرفة ذلك من صفحة المكتبة على GitHub والبحث عن آخر الإصدارات [releases] وعن آخر إيداع [Latest commit]، وأيضاً من التبويب insights ثم Graphs. الحذر من سوء التوثيق [documentation] فأنت لا تريد المزيد من صداع الرأس. المكتبة الجيدة ستحتوي على دليل للبدء السريع، أمثلة، لقطات شاشة ،تطبيق فعلي في متجر التطبيقات يستعرض ميزات المكتبة وغير ذلك. أما سوء التوثيق فهو مؤشر على سوء الصيانة أو سوء المكتبة ككل. توثيق المكتبة ستجده في أحد أو كل ما يلي: صفحة المكتبة على GitHup (ملف README.md) صفحة المكتبة على GitHup تحت التبويب: Wiki صفحة أو موقع خاص بالمكتبة. القضايا المعروفة Known issues يمكن الاطلاع عليها من خلال صفحة المكتبة على GitHub من خلال التبويب Issues. تأكد أنه لا توجد مشاكل مفتوحة بإمكانها التأثير على تطبيقك. الميزات Features ابحث عن المكتبة في Google متبوعة بـ vs، مثل: picasso library vs. قارن المكتبات التي تؤدي نفس الغرض ثم اختر المكتبة الأنسب لمشروعك والتي لا تحتوي على ميزات إضافية لا تحتاجها، لأن ميزاتٍ أكثر تعني حجم تطبيق (apk) أكبر. الرخصة License أغلب الرخص تطالب بإدراج نسخة من حقوق النشر [copyright] إلى تطبيقك. بعضها يزيد فيطالب بمتطلبات أخرى، مثلاً Google Maps يطالب بإضافة: "Map data ©2015 Google" كعلامة مائية كما في الصورة التالية أسفل اليمين: وبعض رخص المكتبات يطالب بجعل المشروع مفتوح المصدر [Open Source]. عدم التزامك بالتراخيص قد يؤدي إلى رفض تطبيقك من متجر Google Play بسبب انتهاك حقوق النشر، أو قد يؤدي إلى مشاكل قانونية. زبدة الموضوع اقرأ رخصة المكتبة التي تنوي استخدامها جيداً. مرجع مساعد لمعرفة أنواع الرخص: https://choosealicense.com الحذر من ازدياد حجم التطبيق متأثراً بكثرة المكتبات المستخدمة، حتى لا تواجه مشكلة: 64K Method Limit ماذا إذا لم تجد المكتبة المناسبة؟ وكنت تحتاج هذه الميزة بشكل متكرر في تطبيقاتك؟ الجواب هو: النسخ واللصق وأفضل من ذلك أن تُنشأ مكتبتك الخاصة وهذا: دليل إنشاء مكتبة أندرويد. نقطة أخيرة في هذا الجزء، إذا كنت في بدايات مشوارك لتعلم أساسيات البرمجة لنظام أندرويد وتطوير تطبيقات بغرض التعلم فلا يُنصح باستخدام مكتبات خارجية حتى تزداد فهماً للنظام وجزئياته ومكتباته الأساسية. الجزء العملي: في هذا الجزء سنعتمد على أحد تطبيقات المهام الموجودة هنا والمسمى: todo‑mvp، ولمتابعة التطبيق يلزمك التالي: برنامج Android Studio برنامج إدارة الإصدارات Git خطوات تنزيل مشروع المهام todo‑mvp: انسخ رابط المشروع: https://github.com/googlesamples/android-architecture.git افتح برنامج Git Bash. غير إلى المجلد الذي تريد استنساخ المشروع إليه، على سبيل المثال: cd D:\AndroidStudioProjects استنسخ المشروع بالأمر git clone وألصق الرابط السابق: git clone https://github.com/googlesamples/android-architecture.git انتقل للمشروع: cd android-architecture ثم انتقل للتفريع todo-mvp كالتالي: git checkout todo-mvp افتح أندرويد استديو ثم: Open an existing Android Studio project >> [path to android-architecture folder] >> todoapp شغل التطبيق. إذا سارت الأمور على ما يرام ستظهر لك هذه الواجهة: أضف بعض المهام بالضغط على الأيقونة الحمراء. ثم اضغط على الأيقونة ☰، ثم Statistics، ستظهر لديك إحصائية بالمهام النشطة والمكتملة بشكل نصي كما يلي: مهمتنا هي إضافة جانب رسومي للإحصائية وتحسين شكل (الميزة ص). خطوات البحث عن مكتبة: هل الميزة ص (تمثيل البيانات الإحصائية بطريقة رسومية) موجودة في تطبيقات أخرى؟ بالنسبة لي نعم، رأيت ذلك في تطبيق مصاريف وبعض تطبيقات البنوك، وأتوقع وجود مكتبة لها لكثرة الحاجة إلى تمثيل البيانات الإحصائية بصورة رسومية: سأتجه إلى موقع stackoverflow ، وأبحث عن android charts library. من ضمن النتائج الظاهرة لدي والمتعلقة بموضوعنا والأعلى تقييماً: Charts for Android Android charting libraries وكلاهما يرشحان المكتبة MPAndroidChart في المقام الأول. أيضاً في مرجعنا Must Have Libraries في قسم Drawing تظهر المكتبة في أعلى القائمة، وكذلك في موقع android-arsenal.com تعتبر الأعلى تقييماً في قسم Graphics. لنطبق معايير التي ذكرنها على هذه المكتبة: الصيانة: مجموع المساهمين 57 منهم مؤسس المكتبة PhilJay و4 مساهمين رئيسيين وجه لهم المؤسس شكر خاص أسفل صفحة المكتبة. ما يهمنا أن مجتمع المكتبة كبير وهو مؤشر على استمرارية الصيانة. آخر إصدار للمكتبة بتاريخ Mar 23, 2017 وآخر تحديث للمكتبة كان في Jul 20, 2017 وهو تاريخ قريب، وأنشئت المكتبة بتاريخ Apr 25, 2014. المكتبة موثقة بشكل جيد في التبويب Wiki وتحتوي على أمثلة ونماذج وتطبيق لتجربتها. القضايا المعروفة: نحن سنستخدم [Pie Chart] أو المخطط الدائري ولا توجد أخطاء [bugs] قد تؤثر على عملنا حتى كتابة هذه المقالة. الميزات: ابحث في قوقل عن: MPAndroidChart vs، من ضمن النتائج هذه المقارنة والتي تعطي الأفضلية لهذه المكتبة. الرخصة: Apache License 2.0، ملخصها هنا. خطوات استخدام المكتبة في المشروع: إضافة الاعتماد في ملف build.gradle: على مستوى المشروع [project level]: allprojects { repositories { jcenter() maven { url "https://jitpack.io" } //إضافة هذا السطر } } وعلى مستوى التطبيق [app level]: dependencies { ... compile 'com.github.PhilJay:MPAndroidChart:v3.0.2' } ثم زامن ملفات gradle. انتقل إلى ملف statistics_frag.xml وأضف View من نوع المخطط الدائري: <com.github.mikephil.charting.charts.PieChart android:id="@+id/chart" android:layout_width="match_parent" android:layout_height="match_parent" /> وعدل طول TextView إلى: android:layout_height="wrap_content" سنحصل على النتيجة التالي: في ملف StatisticsFragment.java نضيف متغير من النوع PieChart: private TextView mStatisticsTV; private PieChart mPieChart; ثم في الإجراء [method] المسمى onCreateView نضيف: mStatisticsTV = (TextView) root.findViewById(R.id.statistics); mPieChart = (PieChart) root.findViewById(R.id.chart); // هذا السطر الآن كل ما نحتاجه هو تعديل الإجراء showStatistics في ملف StatisticsFragment.java: ... mStatisticsTV.setText(displayString); // نضيف التالي بعد هذا السطر // لحساب النسبة المئوية للمهام النشطة والمكتملة float total = numberOfIncompleteTasks + numberOfCompletedTasks; float percentOfIncompleteTasks = numberOfIncompleteTasks / total * 100; float percentOfCompletedTasks = numberOfCompletedTasks / total * 100; // مصفوفة المدخلات وكلما أضفنا مُدخل جديد سيزيد عدد الحصص في المخطط الدائري List<PieEntry> entries = new ArrayList<>(); // إذا كان لدينا مهمة أو مهام غير مكتملة if (numberOfIncompleteTasks != 0) { // نضيف حصة إلى المخطط الدائري بعنوان نشط entries.add(new PieEntry(percentOfIncompleteTasks, "نشط")); } else { // وإلا سنضيف كلمة "أحسنت" في منتصف المخطط الدائري لأننا أنجزنا جميع المهام mPieChart.setCenterText("أحسنت"); } // إذا كان لدينا مهمة أو مهام مكتملة if (numberOfCompletedTasks != 0) { // نضيف حصة إلى المخطط الدائري باسم مكتملة entries.add(new PieEntry(percentOfCompletedTasks, "مكتملة")); } // الكائن التالي يحتوي المدخلات السابقة والتي يجب أن تكون من نفس النوع // ويسمح بإضفاء اللمسات التصميمية PieDataSet dataSet = new PieDataSet(entries, ""); dataSet.setColors(ColorTemplate.MATERIAL_COLORS); dataSet.setValueFormatter(new PercentFormatter()); dataSet.setValueTextSize(14); // لإضافة الوصف الذي يظهر أسفل اليمين Description description = new Description(); description.setText("إحصائية المهام"); mPieChart.setDescription(description); // الكائن التالي يحتوي كائن مجموعة البيانات السابق PieData data = new PieData(dataSet); mPieChart.setData(data); // للتحديث mPieChart.invalidate(); سنحصل على النتيجة التالي: هذا والله أعلم وصلى الله وسلم على نبينا محمد. المراجع: أحد دروس دورة Advanced Android App Development (يستلزم التسجيل) Android Architecture Blueprints مكتبة MPAndroidChart
  26. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته مقدمة سأتحدث في هذا الموضوع عن دورة حياة المشروع البرمجي ولن يكون موضوع واحد فقط وانما عبارة عن سلسلة مواضيع سأتحدث بها عن SPLC -وتعني Software Project Life Cycle- للمستقلين، وأعني بالمستقلين المبرمجين الذين يعملون بشكل مُستقل وليس مع فريق، والسبب الرئيسي لتحدثي عن هذا الموضوع هو انعدام جودة أغلب المشاريع البرمجية التي تتم بشكل فردي أو يتوقف تطويرها في مرحلةٍ ما بسبب عدم فهم الكثير من الأمور التي تتعلق بالمشروع البرمجي وقد تسبق كتابة الكود ايضاً. ملاحظة: SPLC هو مصطلح غير علمي والمصطلح المعروف SDLC -وتعني System Development Life Cycle- ولكن في هذه الدروس لن أتحدث عن دورة حياة تطوير المشروع بشكل خاص وانما عن دورة حياة المشروع البرمجي بشكل عام. جدول المصطلحات الهامة المستخدمة تمت الإشارة للمصطلحات الهامة باللون الأخضر المصطلح الوصف Ad Hoc Approach الطريقة الخاطئة التي يتبعها المستقلين حالياً. SDLC System Development Life Cycle دورة حياة تطوير البرمجيات. SPLC Software Project Life cycle دورة حياة المشروع البرمجي. وهو مصطلح غير علمي كما ذكرنا. Standard هي عبارة عن معايير ونماذج تحددها شركات كبيرة، وهذه النماذج مختبرة، ومجربة، وتم اثبات جدارتها، وتمنح الشركات المطبقة لهذه النماذج او المعايير بالشكل الصحيح شهادات اعتماد. Phases مراحل. Models نماذج. Build اصدار من البرنامج يكون مكتمل ويكون محدد باصدار؛ مثال: Buld 1، Build2...الخ. GUI Graphical User Interface واجهة المستخدم. AI Artificial intelligence الذكاء الاصطناعي. اولاً ماهو المشروع البرمجي؟ وما الهدف منه اصلاً؟ يوجد الكثير من الأشخاص الراغبين في تعلم البرمجة او لنكون دقيقين يرغبون في تعلم لغة برمجة، وذلك بهدف برمجة تطبيق او موقع على الانترنت، او حتى برنامج سطح مكتب، او لعبة...الخ ولكن للرغبة نوعان: نوع قد يكون بهدف، والأخر قد لا يكون بهدف؛ ومن هذا المنطلق نستطيع القول ان المشروع البرمجي هو هدف مبني على رغبة، وليس رغبة بحد ذاتها، وهذا الهدف لا يكون الا لحل مشكلة انسانية او تسريع اعمال او للقيام بالامور الحسابية بشكل فاعل وأسرع، أو حتى بالمجال الطبي أو بالمجال الاقتصادي، وبالغالب كل مجالات الحياة الانسانية، وبسبب هذه الأهداف أصبحت الرغبة بتسريع الأمور وتسهيلها هاجس انساني بحت، حتى وصلنا اليوم الى الـ AI وتقدمنا فيه بشكل كبير. خلاصة: اذاً فالمشروع البرمجي هو عبارة عن برنامج مبني على الحاسوب والهدف منه هو حل المشكلات الإنسانية او تسهيل الحياة او تسريع أداء المهام، أو حتى للمتعة. ثانياً ماهي المشكلة بالطريقة المتبعة حالياً من قبل المستقلين؟ للأسف فالكثير من المبرمجين المستقلين حالياً يعملون على الكثير من المشاريع البرمجية سواءً مشاريع شخصية لمسعىً ربحي او مشاريع لأفراد وجميع هذه المشاريع تتم بشكل عشوائي وبطريقة نسميها علمياً Ad Hoc Approach. تعريف Ad Hoc Approach: هي الحلول المتبعة او الطرائق التي يتخذها المبرمج لإنجاز مشروعه، وهذه الحلول او الطرائق تكون غالباً مبنية على رؤية المبرمج -وليس بطرق مُنظمة بُنيت على الكثير من التجارب التي تم اثباتها-، وغالباً هذه الطرق التي يتخذها المبرمج غير قابلة للاستخدام في مشاريع او اهداف اخرى وذلك لعدم كفائتها فهي غالباً وإن لم تكن دائماً تُنتج مُخرجات ذات جودة رديئة تكون غير قابلة للإختبار، أو التطوير، او قد تكون غير قابلة للتعديل لاحقاً. وهذه العادة المؤسفة والمتبعة من قبل الكثير من المستقلين قد تطفىء الضوء عن كثير من المشاريع سواءً (مواقع الكترونية، تطبيقات للهواتف الذكية، أو ألعاب) مُبكراً وذلك لرداءة جودتها كما ذكرنا سابقاً، وقد ينتهي المشروع الى قضايا قانونية يرفعها صاحب المشروع على المستقل المنفذ لهذا المشروع بسبب رداءة المنتج او عدم تطبيق طلب الطرف الأول بشكل صحيح. ثالثاً ماهي دورة حياة تطوير البرمجيات (SDLC): SDLC او System Development Life Cycle هي دورة حياة تطوير البرمجيات، وتعتبر هذه الدورة بكل مراحلها من ضمن مجالات هندسة البرمجيات، والتي تهدف لتفصيل الإجراءات والأساليب المتبعة لحوكمة وضبط عمل فريق تطوير البرمجيات، والتي أصبحت أساس من أساسيات بناء اي مشروع برمجي، وقد تختلف المراحل من مشروع الى أخر حسب حجم واحتياجات المشروع، وتطبيق جميع المراحل يجعل من المشروع ذا جودة عالية جداً، وموافق للمعايير القياسية (Standard). مراحل دورة حياة تطوير البرمجيات (SDLC): نظراً لأن الأخطاء التي يتم اكتشافها مؤخراً تكون مكلفة وصعبة المعالجة؛ لذا فتصور دورة الحياة يُسهل لنا التنبؤ بالأخطاء مُبكراً ويسمح للمبرمجين بالتركيز على جودة التطبيق والوقت المحدد لتنفيذ البرنامج، وايضاً وضع التكلفة المطلوبة في الاعتبار؛ وتتضمن دورة الحياة التي يمر بها تطوير البرمجيات المراحل الأتية: 1 - Feasibility Study دراسة الجدوى. 2 - Analysis التحليل. 3 - Design التصميم. 4 - Implementation التنفيذ. 5 - Testing الاختبار. 6 - Maintenance الصيانة. 7 - Evaluation التقدير. المراحل الأساسية والمتعارف عليها: 1 - Planning التخطيط. 2 - Analysis التحليل. Requirement المتطلبات Definition المفاهيم 4 - Design التصميم. 5 - Implementation التنفيذ. 6 - Testing الاختبار. 7 - Deployment التنصيب. 8 - Maintenance الصيانة. 9 - Evaluation التقدير. وهذه المراحل Phases هي مراحل مهمة جداً وأساسية في بناء المشاريع البرمجية، وكما ذكرنا سابقاً أن هذه المراحل قد لا تدخل جميعها في مرحلة بناء المشروع البرمجي وانما يتم انتقائها حسب الاحتياج، ولكن نؤكد لكم أن بعضاً من هذه المراحل الزامية ويجب ان تدخل في كُل مشروع برمجي احترافي وذو جودة عالية وقد تم تحديدها باللون الأحمر. ملاحظة: أساس من أساسيات بدأ او تنفيذ اي مشروع برمجي هو اختيار طريقة تنفيذ SDLC وفي الشطر التالي من هذا الموضوع سنتحدث عن هذه الطرائق او النماذج. ملاحظة: بشكل عام جميع المراحل أعلاه قد تكون تسلسلية فلا تبدأ مرحلة حتى تنتهي المرحلة التي تسبقها. ملاحظة: قد تختلف مسميات بعض المراحل فمثلاً Implementation يطلق عليها احيانا Development. أ. مرحلة التخطيط: في هذه المرحلة يتم عمل دراسة لجدوى المشروع، والتواصل مع العميل لفهم المشروع وبناء صورة مبدئية له، والتحقق من الامكانيات لتنفيذ هذا المشروع، فهي تعتبر مرحلة اتخاذ قرار مبدئي. ب. التحليل: تعتبر من أهم مراحل دورة حياة المشروع البرمجي، وهي البادئة الفعلية لل SDLC حيث يتم في هذه المرحلة الاقتراب أكثر من العملاء لفهم المشروع، وتحديد مُتطلباته Requirement، وتحديد المفاهيم والأهداف وايطار المشكلة، وحذف المفاهيم الغير منطقية واستبيان وفهم المشروع بشكل أكبر، فهي تعتبر خطوة تحديد الطلب، وعلى أساسها يتم تنفيذ المراحل التالية. Requirement المتطلبات: هي رغبات العميل، وتطلعاته وهي مهمة جداً في فهم المشروع وتحديد مشكلة العميل وماذا يريد بالضبط. ج. مرحلة التصميم: هي المرحلة التي يتم من خلالها عمل التصميم المبدئي للمشروع، ولا نعني بالتصميم ال GUI وانما مخططات المشروع تماماً مثل المهندس المعماري عندما يرسم مخططات المنزل قبل بنائه، فهذه الخطوة اساسية جداً ولاحقاً سأشرح التصميمات التي يحتاج المستقل ان يعملها لبناء مشروعه بشكل احترافي. د. مرحلة الاختبار: في هذه المرحلة يتم اختبار المشروع، والتحقق من أنه يعمل بشكل جيد ويوجد نوعين من الاختبارات الأساسية Black Box Testing و White Box Testing وسنتعرف على هذه الانواع لاحقاً. ك. التنصيب: وهي عملية نقل المشروع من بيئة التطوير الى بيئة التشغيل في مكان العميل. ص. الصيانة: أهم مراحل SDLC وقد تكون الأكثر ثمناً والأطول في المدة. رابعاً كيف ابدأ المشروع البرمجي؟ هناك الكثير من الطرق أو النماذج التي يتبعها اغلب المستقلين المحترفين في المجال البرمجي، وتكون من بداية المشروع حتى مرحلة التسليم ثم التطوير، وهذه النماذج لا استطيع القول انها جيدة ولكنها مستخدمة، وهي أفضل بكثير من Ad Hoc، ونطلق على هذه النماذج مسمى Models، وسأذكر في هذا القسم البعض من هذه الطرائق. ملاحظة: سأطلق على Models لاحقاً بالطرائق أو النماذج. خلاصة: مما تم ذكره سابقاً نستطيع القول أنه لا توجد طريقة واحدة لبدء المشروع البرمجي، وانما هنالك الكثير من الطرق المعترف بها والمجربة تطبيقياً، وجميع هذه الطرق لم تجرب في مشروع واحد وانما لها تاريخ من الفشل والتطوير، ثم الفشل والتطوير، ...، وسلسلة طويلة من قصص فشل وتطوير حتى توصلنا الى Standard عام لهذه الطرائق، وليومنا هذا مازالت اغلب الطرق قيد التطوير. والهدف من هذه الطرق او النماذج هو تطبيق SDLC بالشكل الأمثل. ومن هذه النماذج (الطرق) نذكر: 1 - Code and Fix: وهذه الطريقة مستخدمة كثيراً من قبل أغلب المستقلين وهي سيئة جداً في حال تم تطبيقها لمشروع كبير او متوسط الحجم، وهي بكل بساطة نبدأ بكتابة الشفرة البرمجية حتى ننتهي، في حال ظهور مشاكل نصلحها او حتى نحصل على رضا المستخدم، ثم نبدأ بكتابة بقية الشفرة حتى ننتهي، وفي حال ظهور مشاكل نصلحها او حتى نحصل على رضا المستخدم، ونستمر بهذه الدورة حتى يكتمل المشروع. لاحظ في هذا النموذج استخدمنا مرحلة ال التخطيط، والتنفيذ فقط. وهذه الطريقة مناسبة جداً للمشاريع الصغيرة والتي لا تكون حساسة، مثل المواقع الشخصية الصغيرة، أو التطبيقات المصغرة جداً مثل تطبيقات عرض الأخبار أو الألعاب الصغيرة. 2 - Incremental: هذه الطريقة تستخدم كثيراً في التطبيقات العامة التي يتم تطويرها وبيعها للمستخدمين، وهذه الطريقة جيدة ولكن لها سلبياتها للأسف، وهي بكل بساطة أن نعمل اصدارات للمشروع البرمجي وكل اصدار يسمى Build وكل Build أثناء تنفيذه يتم تطبيق جميع مراحل SDLC تقريباً بشكل تكراري Itrative، وسنتحدث عن هذه المراحل في موضوع أخر ان شاء الله. 3 - Agail: هذه الطريقة هي دمج مابين Incremental و Itrative وتركز على تنفيذ المشروع بالشكل الذي يحقق رضا العملاء، عن طريق التسليم السريع للمنتج، وهذه الطريقة تعتبر سلسة جداً من ناحية الاستخدام وهي من الطرق التي يستطيع استخدامها المستقلين. ملاحظة: هذه بعض النماذج التي احببت ذكرها، وهي نماذج جيدة جداً، ويستطيع المستقل استخدامها بسلاسة نوعاً ما. خلاصة: اذاً اجابتاً على السؤال: كيف ابدأ المشروع البرمجي؟ الاجابة تكمن في اختيار الطريقة او النموذج الصحيح؛ واختيار نموذج او طريقة العمل ليس سهل وانما يتطلب خبرة عالية وفهم عميق للمشروع، ولكن لا تقلق حتى لو كنت مبتدأ فمعرفتك البسيطة بهذه النماذج سوف يساعدك كثيراً على اختيار الصحيح -تقريباً- منها. على كُل حال فهذه النماذج قد لا تكون واضحة للبعض ولكن لاحقاً في موضوع أخر سنشرح هذا النماذج بشكل أفضل ونشرح طرق الاختيار. خامساً تحليل وتلخيص المعلومات: اذا لنربط المعلومات ونوضح الأفكار ونضمن فهمك للموضوع بشكل جيد سنضع نقاط وسيتم تلخيص كل نقطة. ماهي المشكلة مع الطريقة التي يتبعها المستقلين في مشاريعهم البرمجية. ماهي المخاطر الممكنة او المتوقعة التي تسببها الطرق الخاطئة في تنفيذ المشاريع البرمجية. كيف ابدأ ببناء مشروع برمجي ذو جودة عالية. SDLC و SDLC Models. أ. ماهي المشكلة مع الطريقة التي يتبعها المستقلين في مشاريعهم البرمجية؟: المشكلة باختصار تكمن في جودة المنتج، فتنفيذ المشاريع البرمجية لا يكون فقط بكتابة الكود، وانما التخطيط الصحيح والتصميم الأمثل، واتباع الطريقة الأكثر فعالية لضمان بناء المشروع بجودة عالية، وسهولة اختباره، وامكانية صيانته وتطويره لاحقاً. ب. ماهي المخاطر الممكنة أو المتوقعة التي تسببها الطرق الخاطئة في تنفيذ المشاريع البرمجية؟: الجودة، صعوبة الاختبار والتحقق، صعوبة الصيانة، و القضايا القانونية. ج. كيف ابدأ ببناء مشروع برمجي ذو جودة عالية؟: لبناء مشروع برمجي ذو جودة عالية، يجب ان تبدأ اولاً بفهم المشروع بشكل صحيح، ثم كتابة متطلبات المشروع، ثم عمل التصميمات التوضيحية وتأكيد فهم المشروع، ثم التنفيذ، ثم الاختبار، ولاحقاً الصيانة، وكل ماتم ذكره سابقاً من مراحل SDLC والتي يتم تنفيذها بشكل فعال من خلال SDLC Models. د. SDLC و SDLC Models: دورة حياة تطوير المشروع البرمجي SDLC هي مجموعة مراحل يجب تنفيذها لبناء المشروع البرمجي بشكل صحيح، وطريقة تنفيذ هذه المراحل يكمن من خلال نماذج دورة حياة تطوير المشروع البرمجي SDLC Models. خاتمة من المتوقع كثيراً خصوصاً مع كثرة المُصطلحات والأفكار أن يكون فهمك لأهمية SPLC غير كامل وقد يكون مُبهم، لذلك لا تقلق ففي المواضيع القادمة والتي ستكون تحت عنوان دورة حياة تطوير البرمجيات: X -حيث أن X يمثل العنوان الفرعي- سنتحدث بالتفصيل عن مراحل SDLC، وعن الأدوات، والنماذج التي سوف تساعدك كمستقل على ادارة مشروعك البرمجي بشكل احترافي لتضمن جودته. الموضوع التالي دورة حياة تطوير البرمجيات: المرحلة الأولى التخطيط هذا وصلى الله وسلم على نبينا محمد وعلى آله وصحبه اجمعين
  27. أبو معتز مطور تطبيقات أندرويد وهو الآن في منتصف بناء تطبيق أندرويد، وصل للميزة ص، برمجها واختبرها وتأكد من أنها تؤدي الغرض المراد منها مبدئياً، ولا مشاكل ظاهرة حتى اللحظة، وأثناء تلك اللحظة -وهو يرتشف الشاي الأحمر مع مسحوق أوراق النعناع الخضراء المجففة- تبادر إلى ذهنه السؤال التالي: لماذا أداء ومظهر هذه الميزة لا يشبه مثيلاتها في تطبيقات المبرمجين الآخرين المُحملة على جوالي؟ ملحوظة جانبية: هذه المقالة تتكون من جزئين: نظري وعملي. الجزء النظري: إذا مررت بالحالة السابقة فهذه المقالة -إن شاء الله- دليلك لاختيار المكتبة (Library) أو المكتبات المناسبة لمشروعك. لذا قبل أن تعيد اختراع العجلة أو تبدأ من الصفر أو تُهدر وقتاً وجهداً، دعنا نرجع إلى الميزة ص وقبل الشروع في برمجتها، اسأل نفسك الأسئلة التالية: هل هذه الميزة موجودة في تطبيقات أخرى؟ هل تقوم بنسخ ولصق كود هذه الميزة بشكل متكرر في تطبيقاتك؟ هل تظن باحتمالية برمجة هذه الميزة من قبل مبرمج آخر؟ إذا كانت الإجابة نعم لأحد أو كل هذه الأسئلة فهذا مؤشر على أن هناك مكتبة توفر هذه الميزة، والسؤال الذي ينبغي أن يُطرح: كيف أبحث عنها؟ وما هي مواصفات أفضل مكتبة مناسبة لتطبيقي؟ أما طريقة البحث فيمكن من خلال المراجع التالية لاستكشاف المكتبات المناسبة: البحث في موقع: stackoverflow صفحة على GitHub بعنوان: Must Have Libraries موقع: android-arsenal.com لديك مصدر آخر؟ أرجو ذكره في التعليقات 😉 لكن كيف أتأكد أن المكتبة التي اخترتها هي المكتبة المناسبة لمشروعي؟ هناك مواصفات ونصائح للمكتبة المناسبة يمكن إجمالها في النقاط التالية: الصيانة Maintenance أندرويد نظام يتطور باستمرار وكذا ينبغي لمكتباتها، فبعض المكتبات تتوقف عن التطور عند إصدار نسخة جديدة من أندرويد، بالتالي تتوقف صيانتها وتُهجر [deprecated]، فإذا كنت تستخدم واحدة من هذه المكتبات المهجورة فسيتعطل تطوير تطبيقك بلا شك. من الذي يقوم بصيانة المكتبة؟ هل هو شخص واحد؟ أو مجموعة أفراد؟ أو شركة مثل: Google أو Square؟ المعنى أنه كلما كان مجتمع المكتبة أكبر كلما كانت صيانة المكتبة مستمرة. هل صيانة المكتبة مستمرة؟ ومتى كان آخر تحديث لها؟ بإمكانك معرفة ذلك من صفحة المكتبة على GitHub والبحث عن آخر الإصدارات [releases] وعن آخر إيداع [Latest commit]، وأيضاً من التبويب insights ثم Graphs. الحذر من سوء التوثيق [documentation] فأنت لا تريد المزيد من صداع الرأس. المكتبة الجيدة ستحتوي على دليل للبدء السريع، أمثلة، لقطات شاشة ،تطبيق فعلي في متجر التطبيقات يستعرض ميزات المكتبة وغير ذلك. أما سوء التوثيق فهو مؤشر على سوء الصيانة أو سوء المكتبة ككل. توثيق المكتبة ستجده في أحد أو كل ما يلي: صفحة المكتبة على GitHup (ملف README.md) صفحة المكتبة على GitHup تحت التبويب: Wiki صفحة أو موقع خاص بالمكتبة. القضايا المعروفة Known issues يمكن الاطلاع عليها من خلال صفحة المكتبة على GitHub من خلال التبويب Issues. تأكد أنه لا توجد مشاكل مفتوحة بإمكانها التأثير على تطبيقك. الميزات Features ابحث عن المكتبة في Google متبوعة بـ vs، مثل: picasso library vs. قارن المكتبات التي تؤدي نفس الغرض ثم اختر المكتبة الأنسب لمشروعك والتي لا تحتوي على ميزات إضافية لا تحتاجها، لأن ميزاتٍ أكثر تعني حجم تطبيق (apk) أكبر. الرخصة License أغلب الرخص تطالب بإدراج نسخة من حقوق النشر [copyright] إلى تطبيقك. بعضها يزيد فيطالب بمتطلبات أخرى، مثلاً Google Maps يطالب بإضافة: "Map data ©2015 Google" كعلامة مائية كما في الصورة التالية أسفل اليمين: وبعض رخص المكتبات يطالب بجعل المشروع مفتوح المصدر [Open Source]. عدم التزامك بالتراخيص قد يؤدي إلى رفض تطبيقك من متجر Google Play بسبب انتهاك حقوق النشر، أو قد يؤدي إلى مشاكل قانونية. زبدة الموضوع اقرأ رخصة المكتبة التي تنوي استخدامها جيداً. مرجع مساعد لمعرفة أنواع الرخص: https://choosealicense.com الحذر من ازدياد حجم التطبيق متأثراً بكثرة المكتبات المستخدمة، حتى لا تواجه مشكلة: 64K Method Limit ماذا إذا لم تجد المكتبة المناسبة؟ وكنت تحتاج هذه الميزة بشكل متكرر في تطبيقاتك؟ الجواب هو: النسخ واللصق وأفضل من ذلك أن تُنشأ مكتبتك الخاصة وهذا: دليل إنشاء مكتبة أندرويد. نقطة أخيرة في هذا الجزء، إذا كنت في بدايات مشوارك لتعلم أساسيات البرمجة لنظام أندرويد وتطوير تطبيقات بغرض التعلم فلا يُنصح باستخدام مكتبات خارجية حتى تزداد فهماً للنظام وجزئياته ومكتباته الأساسية. الجزء العملي: في هذا الجزء سنعتمد على أحد تطبيقات المهام الموجودة هنا والمسمى: todo‑mvp، ولمتابعة التطبيق يلزمك التالي: برنامج Android Studio برنامج إدارة الإصدارات Git خطوات تنزيل مشروع المهام todo‑mvp: انسخ رابط المشروع: https://github.com/googlesamples/android-architecture.git افتح برنامج Git Bash. غير إلى المجلد الذي تريد استنساخ المشروع إليه، على سبيل المثال: cd D:\AndroidStudioProjects استنسخ المشروع بالأمر git clone وألصق الرابط السابق: git clone https://github.com/googlesamples/android-architecture.git انتقل للمشروع: cd android-architecture ثم انتقل للتفريع todo-mvp كالتالي: git checkout todo-mvp افتح أندرويد استديو ثم: Open an existing Android Studio project >> [path to android-architecture folder] >> todoapp شغل التطبيق. إذا سارت الأمور على ما يرام ستظهر لك هذه الواجهة: أضف بعض المهام بالضغط على الأيقونة الحمراء. ثم اضغط على الأيقونة ☰، ثم Statistics، ستظهر لديك إحصائية بالمهام النشطة والمكتملة بشكل نصي كما يلي: مهمتنا هي إضافة جانب رسومي للإحصائية وتحسين شكل (الميزة ص). خطوات البحث عن مكتبة: هل الميزة ص (تمثيل البيانات الإحصائية بطريقة رسومية) موجودة في تطبيقات أخرى؟ بالنسبة لي نعم، رأيت ذلك في تطبيق مصاريف وبعض تطبيقات البنوك، وأتوقع وجود مكتبة لها لكثرة الحاجة إلى تمثيل البيانات الإحصائية بصورة رسومية: سأتجه إلى موقع stackoverflow ، وأبحث عن android charts library. من ضمن النتائج الظاهرة لدي والمتعلقة بموضوعنا والأعلى تقييماً: Charts for Android Android charting libraries وكلاهما يرشحان المكتبة MPAndroidChart في المقام الأول. أيضاً في مرجعنا Must Have Libraries في قسم Drawing تظهر المكتبة في أعلى القائمة، وكذلك في موقع android-arsenal.com تعتبر الأعلى تقييماً في قسم Graphics. لنطبق معايير التي ذكرنها على هذه المكتبة: الصيانة: مجموع المساهمين 57 منهم مؤسس المكتبة PhilJay و4 مساهمين رئيسيين وجه لهم المؤسس شكر خاص أسفل صفحة المكتبة. ما يهمنا أن مجتمع المكتبة كبير وهو مؤشر على استمرارية الصيانة. آخر إصدار للمكتبة بتاريخ Mar 23, 2017 وآخر تحديث للمكتبة كان في Jul 20, 2017 وهو تاريخ قريب، وأنشئت المكتبة بتاريخ Apr 25, 2014. المكتبة موثقة بشكل جيد في التبويب Wiki وتحتوي على أمثلة ونماذج وتطبيق لتجربتها. القضايا المعروفة: نحن سنستخدم [Pie Chart] أو المخطط الدائري ولا توجد أخطاء [bugs] قد تؤثر على عملنا حتى كتابة هذه المقالة. الميزات: ابحث في قوقل عن: MPAndroidChart vs، من ضمن النتائج هذه المقارنة والتي تعطي الأفضلية لهذه المكتبة. الرخصة: Apache License 2.0، ملخصها هنا. خطوات استخدام المكتبة في المشروع: إضافة الاعتماد في ملف build.gradle: على مستوى المشروع [project level]: allprojects { repositories { jcenter() maven { url "https://jitpack.io" } //إضافة هذا السطر } } وعلى مستوى التطبيق [app level]: dependencies { ... compile 'com.github.PhilJay:MPAndroidChart:v3.0.2' } ثم زامن ملفات gradle. انتقل إلى ملف statistics_frag.xml وأضف View من نوع المخطط الدائري: <com.github.mikephil.charting.charts.PieChart android:id="@+id/chart" android:layout_width="match_parent" android:layout_height="match_parent" /> وعدل طول TextView إلى: android:layout_height="wrap_content" سنحصل على النتيجة التالي: في ملف StatisticsFragment.java نضيف متغير من النوع PieChart: private TextView mStatisticsTV; private PieChart mPieChart; ثم في الإجراء [method] المسمى onCreateView نضيف: mStatisticsTV = (TextView) root.findViewById(R.id.statistics); mPieChart = (PieChart) root.findViewById(R.id.chart); // هذا السطر الآن كل ما نحتاجه هو تعديل الإجراء showStatistics في ملف StatisticsFragment.java: ... mStatisticsTV.setText(displayString); // نضيف التالي بعد هذا السطر // لحساب النسبة المئوية للمهام النشطة والمكتملة float total = numberOfIncompleteTasks + numberOfCompletedTasks; float percentOfIncompleteTasks = numberOfIncompleteTasks / total * 100; float percentOfCompletedTasks = numberOfCompletedTasks / total * 100; // مصفوفة المدخلات وكلما أضفنا مُدخل جديد سيزيد عدد الحصص في المخطط الدائري List<PieEntry> entries = new ArrayList<>(); // إذا كان لدينا مهمة أو مهام غير مكتملة if (numberOfIncompleteTasks != 0) { // نضيف حصة إلى المخطط الدائري بعنوان نشط entries.add(new PieEntry(percentOfIncompleteTasks, "نشط")); } else { // وإلا سنضيف كلمة "أحسنت" في منتصف المخطط الدائري لأننا أنجزنا جميع المهام mPieChart.setCenterText("أحسنت"); } // إذا كان لدينا مهمة أو مهام مكتملة if (numberOfCompletedTasks != 0) { // نضيف حصة إلى المخطط الدائري باسم مكتملة entries.add(new PieEntry(percentOfCompletedTasks, "مكتملة")); } // الكائن التالي يحتوي المدخلات السابقة والتي يجب أن تكون من نفس النوع // ويسمح بإضفاء اللمسات التصميمية PieDataSet dataSet = new PieDataSet(entries, ""); dataSet.setColors(ColorTemplate.MATERIAL_COLORS); dataSet.setValueFormatter(new PercentFormatter()); dataSet.setValueTextSize(14); // لإضافة الوصف الذي يظهر أسفل اليمين Description description = new Description(); description.setText("إحصائية المهام"); mPieChart.setDescription(description); // الكائن التالي يحتوي كائن مجموعة البيانات السابق PieData data = new PieData(dataSet); mPieChart.setData(data); // للتحديث mPieChart.invalidate(); سنحصل على النتيجة التالي: هذا والله أعلم وصلى الله وسلم على نبينا محمد. المراجع: أحد دروس دورة Advanced Android App Development (يستلزم التسجيل) Android Architecture Blueprints مكتبة MPAndroidChart
  28. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته شرح الملحقات الأربعة التالية Label, Button, TextView, TextField الخصائص المشتركة بينهم text للحصول على النص أو تعيينه. style لتعيين خصائص CSS مخصصة له وحدة id لتعيين تعريف للملحق textAlignment لمحاذات النص "initial" | "left" | "center" | "right" textDecoration الخط الذي تحت النص "none" | "underline" | "line-through" | "underline line-through" textTransform لتحول النص ( جميع الحروف صغيرة او كبيرة ) "initial" | "none" | "capitalize" | "uppercase" | "lowercase" whiteSpace المسافات البيضاء بين الحروف "initial" | "normal" | "nowrap" | رقم لتحديد المسافة isEnabled حالة تفعيل للملحق ( الكل ما عدا Label ) خصائص الملحق Label textWrap السماح للنص بالألتفاف على السطور ( الصفوف ) خصائص الملحق Button tap تشغيل دالة عند النقر على الزر خصائص الملحقان TextView و TextField المشتركة hint نص للتوضيح editable قابلية الحقل لتعديل النص الذي بداخلة true | false keyboardType نوع لوحة المفاتيح datetime | phone | number | url | email returnKeyType نوع زر الرجوع ( مثل "ارسال" , "رجوع" , "اذهب" ) done | next | go | search | send returnPress تشغيل دالة عند الضغط على زر الرجوع في لوحة المفاتيح secure حماية الحقل ( حقل مشفر ) true | false textChange تشغيل دالة عندما يتغير النص autocorrect التصحيح التلقائي للنص true | false مثال على الملحقات <Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo"> <StackLayout> <Button text="مرحبًا" tap="{{ hello }}"/> <TextView text="مرحبًا بالعالم" editable="false" /> <TextView hint="عالم البرمجة" editable="true" /> <TextView hint="عالم البرمجة" text="دورة nativescript" editable="true" /> <TextField text="السلام عليكم ورحمة الله وبركاته"></TextField> <TextField hint="ادخل رقم الجوال الخاص بك" keyboardType="phone"></TextField> <TextField hint="ادخل كلمة السر" secure="true"></TextField> <Label text="الدرس الحادي عشر 11"></Label> </StackLayout> </Page>
  1. عرض المزيد من النشاطات

عالم البرمجة

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