استخدام الـ Rules في الـ JUnit لإضافة سلوكيات لدوال الاختبارات


مستوى المقال: مبتدئ

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

في هذه المقالة سنتعلم كيفية استخدام القواعد (Rules) في الـ JUnit والتي تسمح لنا بأضافة سلوك (behavior) جديد او التغيير في بعض السلوكيات على دوال الاختبارات.
 

المواضيع السابقة (مهمة لفهم هذا المقال)

هذه هي المقالات السابقة والتي تعتبر ضروريه لفهم هذا المقال فاذا لم تكن لديك خلفيه مع الـ JUnit انصحك بقرائتها قبل هذا الموضوع والذي يعتبر المقال الرابع من اصل ستة مقالات.

 

رابط المشروع

https://github.com/mzdhr/KidCalculator-JUnit

وتوجد كلاسات هذه المقالة باسم: RuleTest.java و RuleWatchEye.java في المشروع. واذا اردت الاطلاع على كلاس الـموديل فهذه هي Calculator.java.

 

ماذا سوف تقرئ في هذه المقالة
ماهي القواعد Rule و كيفية استخدامها؟ ما الفرق بين النوتيشن ClassRule و Rule وكيفية استخدامهم ومتى؟ امثله لإستخدام بعض من القواعد كقاعدة الـ Timeout و الـ TestName و الـ ErrorCollector. واخيراً كيفية انشاء قاعدة خاصة بنا؟

 

ماهي القواعد (Rule) للـ JUnit

هي عبارة عن عناصر منئشه من كلاس قاعدي (اي كلاس يعمل implements للإنترفيس TestRule يعتبر كلاس قاعدي). فأنت تقوم بأنشائها كـ Field في كلاس الاختبارات.

 

كيفية استخدام هذه القواعد Rules في الـ JUnit

بعد انشاء عنصر من كلاس قاعدي كـ Field في كلاس الاختبارات, تضع فوقه النوتيشن Rule او ClassRule وبهذا تصبح هذه القاعدة مطبقه على جميع دوال الاختبارات التي بداخل الكلاس او على الكلاس نفسها.

 

قائمة بجميع القواعد الموجوده في الـ JUnit

هذه كلاسات قواعد معده مسبقاً, وجاهزه لإنشاء عناصر منها واستخدامها مع النوتيشن Rule و ClassRule في كلاس اختباراتك.

  • TemporaryFolder لإنشاء ملفات ومجلدات وهميه, لإستخدامهم عند الحاجة اثناء كتابة الاختبارات.
  • ExternalResource مثل سابقتها, ولكن هنا تنشئ عناصر وهميه مثل التعامل مع: socket و server و database connection الخ...
  • ErrorCollector تسمح لدوال الاختبار بالاستمرار للنهاية, حتى مع حدوث خطئ لاتتوقف, وتقوم بجمع كل الاخطاء ومن ثم عرضهم.
  • Verifier اذا اردت ان تتحقق من شئ قبل بداية الاختبارات, مثلاً هل شبكة الانترنت متصلة ام لا.
  • TestWatchman/TestWatcher تسمح لك هذه القاعدة بمراقبة دوال الاختبارات و طباعة Log في الكونسول للمراقبة, مثلاً هل بدء الاختبار؟ هل انتهى؟ هل نجح ام فشل؟. 
  • TestName اذا اردت الحصول على اسم دالة الاختبار فهذه القاعدة هي المسؤله عن ذلك, مثلاً تستطيع من خلالها طباعة اسم دالة الاختبار من داخلها.
  • Timeout لوضع حد زمني لدوال الاختبارات, على ان تكتمل كل دالة قبل الوصول لذلك الزمن وإلا تفشل.
  • ExpectedException تسمح لك بالحصول على تحكم اكثر في رسالة ونوع الـ exception المرمي في دالة الاختبار.
  • RuleChain لربط القواعد مع بعضها البعض.

 

قائمة بـ نوتيشنات الخاصه للقواعد (annotation) الموجوده في الـ JUnit

  • Rule تستخدم لتطبيق القاعدة على جميع دوال الاختبارات.
  • ClassRule تستخدم لتطبيق القاعدة على كلاس الاختبارات فقط وليس على دوالها.

 

استخدام النوتيشن ClassRule مع القاعدة Timeout

أستخدام النوتيشن ClassRule والذي من خلاله نستطيع تطبيق  اي قاعده يأتي فوقها على الكلاس نفسها وليس على دوالها.

اما القاعدة Timeout فهي تسمح لتحديد وقت (بأجزاء الثانية)  حتى ينتهي الاختبار في مدة هذا الوقت والا يعتبر الاختبار فاشل.

المثال

    // Rule for Class
    @ClassRule
    public static TestRule timeout = Timeout.millis(5000);

تعقيب

بانشاء هذا الـ Field من كلاس القاعدة Timeout وتحديد النوتيشن له بـ ClassRule (لاحظ انه static لانه يعود للكلاس نفسها) فهذا يعني: أن كلاس الاختبارات يجب ان تنهي جميع اختباراتها في اقل من ٥ ثواني, والا تعتبر الاختبارات بها فاشله.

 

اذن متى نستخدم النوتيشن الـ ClassRule؟

اذا اردنا ان نطبق قاعدة على الكلاس نفسها, و ايضاً اذا اردنا من القاعدة ان تعمل لمره واحده فقط. مثلاً عندما تريد الاتصال بالانترنت قبل عمل كلاس الاختبارات هذه ثم اطفاء الانترنت عند نهايتها, او اذا كنت تريد تشغيل السيرفر لتجهيزه قبل عمل كلاس الاختبارات هذه ثم اطفائه عند نهايتها, فـ ClassRule مناسبه جداً.

اقتباس

اذن من هذا التصرف نرى ان النوتيشن ClassRule تشابه عملها للنوتيشن BeforeClass و AfterClass كما بالمقال الاول, ولكنها هنا تختص بالقواعد.

 

استخدام النوتيشن Rule مع القاعدة Timeout

هنا سنستخدم نفس القاعدة Timeout لتحديد فتره زمنيه, ولكن ليس على الكلاس! بل على جميع دوال الاختبارات باستخدام النوتيشن Rule كما بالمثال التالي.

المثال

    // Rules for @Test Methods
    @Rule
    public final TestRule timeoutForMethods = Timeout.millis(1005);

تعقيب

بانشاء هذا الـ Field من كلاس القاعدة Timeout وتحديد النوتيشن له بـ Rule فهذا يعني: أن اي دالة اختبار تأتي بالنوتيشن Test عليها ان تنهي عملها قبل تخطي هذا الوقت وهو تقريباً ثانيه, والا سوف تفشل.

اقتباس

اليس هذا مشابهه للخاصة timeout التي تأتي مع النوتيشن Test كما بالمقال الاول, ولكن الفرق هنا ان هذه نكتبها فقط كـ Field وتطبق على جميع الدوال, اي لاحاجة لتكرار كتابة timeout لكل نوتيشن Test.

 

اذن متى نستخدم النوتيشن الـ Rule؟

اذا اردنا ان نطبق قاعدة على كل الدوال, بمعنى اخر ان هذه القاعده سوف تعمل بعدد كل دوال الاختبارات المعلمه بالنوتيشن Test في كلاس الاختبارات. فاذا قمت باستخدام هذا النوتيشن لقاعدة تقوم بالاتصال بالانترنت وقطعه, فانك سوف تقوم بتكرار هذه العمليه لكل دالة اختبار.

اقتباس

اذن من هذا التصرف نرى ان النوتيشن Rule تشابه عملها للنوتيشن Before و After كما بالمقال الاول, ولكنها هنا تختص بالقواعد.

 

استخدام النوتيشن Rule مع القاعدة TestName

مثال اخر لإستخدام النوتيشن Rule مع قاعدة اخرى وهي TestName, وكما جاء سابقاً للـ "TestName: اذا اردت الحصول على اسم دالة الاختبار فهذه القاعدة هي المسؤله عن ذلك, مثلاً تستطيع من خلالها طباعة اسم دالة الاختبار من داخلها."

المثال لأنشائها

    @Rule
    public final TestName testName = new TestName();

تعقيب

عباره عن Field بكلاس الاختبارات.

 

المثال لإستخدامها

    @Test
    public void method_name_test(){
        System.out.println("This message printed from method -> " + testName.getMethodName());
    }

الناتج

This message printed from method -> method_name_test

تعقيب

قمنا باستخدام الفيلد testName بداخل دالة الاختبار "method_name_test", لطباعة اسمها.

 

استخدام النوتيشن Rule مع القاعدة ErrorCollector

مثال اخر لإستخدام النوتيشن Rule مع قاعدة اخرى وهي ErrorCollector , وكما جاء سابقاً للـ "ErrorCollector: تسمح لدوال الاختبار بالاستمرار للنهاية, حتى مع حدوث خطئ لاتتوقف, وتقوم بجمع كل الاخطاء ومن ثم عرضهم."

المثال لإنشائها

    @Rule
    public final ErrorCollector errorCollector = new ErrorCollector();

تعقيب

عباره عن Field بكلاس الاختبارات.

 

المثال لإستخدامها

    @Test
    public void error_collector_test(){
        // Action
        int result01 = calculator.addition(2,2);
        int result02 = calculator.addition(3,3);
        int result03 = calculator.addition(4,4);
        // Assertion
        errorCollector.checkThat(result01, is(3));  // wrong
        errorCollector.checkThat(result02, is(3));  // wrong
        errorCollector.checkThat(result03, is(3));  // wrong
    }

النتيجه

errorcollectorjunitt.thumb.png.a233ae4e3a7a6f6a5306615ebc77c3dc.png

تعقيب

قمنا باستخدام الفيلد errorCollector مع الدالة checkThat واعطيناه ثلاثة احتمالات خاطئه عمداً, لنرى هل يقوم فقط بالتحقق من الاحتمال الاول ثم يفشل ويوقف الاختبار! كما سنرى كيف يحصل مع الداله assertThat لاحقاً! ام يكمل ويأتينا بالاخطاء كلها (نعم اتى بالاخطاء كلها).

 

الاستخدام بدون القاعدة الـ Error Collector

    @Test
    public void no_error_collector_test(){
        // Action
        int result01 = calculator.addition(2,2);
        int result02 = calculator.addition(3,3);
        int result03 = calculator.addition(4,4);
        // Assertion
        assertThat(result01, is(3));  // wrong
        assertThat(result02, is(3));  // wrong
        assertThat(result03, is(3));  // wrong
    }

النتيجه

noerorrcollector.png.9e85530f89900e21fc1ab3fdfe66fe1d.png

تعقيب

بدون استخدام القاعدة ErrorCollector فنحن سنحصل فقط على اول خطئ ثم يتوقف عمل دالة الاختبار.

 

انشاء قاعدة خاصة بنا واستخدام النوتيشن Rule معها

ماذا لو ان القواعد المدمجه مع الـ JUnit لم تلبي رغبة كتابة اختباراتك لمشروعك! وتحتاج المزيد او غيرها من القواعد؟ اذن في هذه الحاله تستطيع ان تنشئ قاعدتك الخاصة كما سيأتي.

 

ماهي وظيفة القاعدة التي سوف ننشئها؟

سننشئ قاعدة تقوم بإضافة سلوك اضافي لكل دالة اختبار لدينا. وهذا السلوك هو: ان كل دالة اختبار تقوم بطباعة ثلاثة اشياء: اسمها و اسم كلاسها و كم استغرقت من وقت.

 

خطوات انشاء قاعدة خاصة في الـ JUnit

  1. انشاء كلاس اختبارات جديدة في مسار مجلد الاختبارات.
  2. عمل implements للـ interface التاليه TestRule.
  3. عمل Override للدالة apply.
  4. انشاء عنصر جديد من كلاس الـ Statement وجعل الدالة apply تقوم بإرجاعه.
  5. وهذا العنصر يحتوي على الدالة evaluate والتي بداخلها سوف نقوم بكتابة الكود الذي سيضيف هذا السلوك الجديد لدوال الاختبارات.

 

الكود (كلاس القاعدة الخاصة بنا)

package com.company;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;


public class RuleWatchEye implements TestRule {
    
    @Override
    public Statement apply(Statement base, Description description) {
        
        return new Statement() {
            
            @Override
            public void evaluate() throws Throwable {
                // ---------- Write Behavior ----------
                long start = System.currentTimeMillis();
                try {
                    // This code will run when each of @Test Methods runs.
                    base.evaluate();    // Trigger for our @Test Methods code's.
                    System.out.println();
                    System.out.println("Method   ---> " + description.getMethodName());
                    System.out.println("In Class ---> " + description.getTestClass().getName());
                } finally {
                    // This code will run when each of @Test Methods is finish running.
                    System.out.println("Takes    ---> " + (System.currentTimeMillis() - start) + " Millisecond");
                }
                // ---------- End of Behavior ----------
            }
            
        };
        
    }
    

}

تعقيب

العمل كله يحدث داخل الدالة "evaluate" حيث اننا قمنا بأنشاء العنصر start وجعلناه يخزن وقت البدء. ثم بداخل الـ Try قمنا بكتابة ماذا نريد ان يتم عند تشغيل اي دالة اختبارات. وفي الـ finally قمنا بكتابة ماذا نريد ان يتم عند الانتهاء من تشغيل اي دالة اختبارات. ولاتنسى قرائة التعليقات الموجودة بالكود للإيضاح اكثر, وايضاً قرائة خطوات الانشاء السابقه حتى يسهل الفهم.

 

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

    // Custom Rule
    @Rule
    public final RuleWatchEye ruleWatchEye = new RuleWatchEye();

تعقيب

فقط نقوم بعمل عنصر كـ Field منها في كلاس اختباراتنا. ونضع فوقه النوتيشن Rule, وعند تشغيل اي دالة اختبار فسوف يتم تفعيل قاعدتنا عليها.

 

الناتج

Method   ---> time_out_annotation_test
In Class ---> com.company.RuleTest
Takes    ---> 803 Millisecond

تعقيب

تم تشغيل الدالة time_out_annotation_test في كلاس الاختبارات RuleTest.java بشكل عادي, وتمت طباعة هذه الاسطر الثالثه السابقه, اذن قاعدتنا تعمل بشكل ممتاز. واذا اردت ان تقوم بطباعة جميع المعلومات للدوال الاخرى فقط قم بتشغيل الكلاس بأكملها. ونستطيع ايضاً استخدام هذه القاعد (وذلك فقط بانشاء عنصر منها كـ Field) في كلاسات اختباراتنا الاخرى.

 

الكود الكامل لكلاس الاختبارات للقواعد في هذه المقالة

package com.company;

import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import org.junit.rules.TestName;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

public class RuleTest {
    // Fields
    private Calculator calculator;

    // Rule for Class
    @ClassRule
    public static TestRule timeout = Timeout.millis(5000);

    // Rules for @Test Methods
    @Rule
    public final TestRule timeoutForMethods = Timeout.millis(1005);
    @Rule
    public final TestName testName = new TestName();
    @Rule
    public final ErrorCollector errorCollector = new ErrorCollector();

    // Custom Rule
    @Rule
    public final RuleWatchEye ruleWatchEye = new RuleWatchEye();

    @Before
    public void setUp() {
        // Arrange
        calculator = new Calculator("Yellow");
    }

    @Test(timeout = 1000)
    public void time_out_annotation_test() throws InterruptedException {
        Thread.sleep(800);
    }

    @Test
    public void time_out_global_annotation_test() throws InterruptedException {
        Thread.sleep(800);
    }

    @Test
    public void error_collector_test(){
        // Action
        int result01 = calculator.addition(2,2);
        int result02 = calculator.addition(3,3);
        int result03 = calculator.addition(4,4);
        // Assertion
        errorCollector.checkThat(result01, is(3));  // wrong
        errorCollector.checkThat(result02, is(3));  // wrong
        errorCollector.checkThat(result03, is(3));  // wrong
    }

    @Test
    public void no_error_collector_test(){
        // Action
        int result01 = calculator.addition(2,2);
        int result02 = calculator.addition(3,3);
        int result03 = calculator.addition(4,4);
        // Assertion
        assertThat(result01, is(3));  // wrong
        assertThat(result02, is(3));  // wrong
        assertThat(result03, is(3));  // wrong
    }


    @Test
    public void method_name_test(){
        System.out.println("This message printed from method -> " + testName.getMethodName());
    }

}

 

 

في النهاية تعلمنا كيفية استخدام النوتيشن Rule و ClassRule والفرق بينهم. وتطرقنا الى بعض من الامثله للقواعد المدمجة البسيطه (لتسهيل الفهم) مع الـ JUnit كـ ErrorCollector و TestName و Timeout  (للإطلاع على المزيد من الامثله لهذه القواعد), وكيفية استخدامهم مع هذه النوتيشنات. وايضاً تعلمنا كيفية انشاء قاعدة خاصة بنا وكيفية استخدامها وتطبيقها في كلاسات اختباراتنا.

mzdhr

0


اراء المستخدمين


لاتوجد تعليقات لعرضها .



انشئ حساب جديد او قم بتسجيل دخولك لتتمكن من اضافه تعليق جديد

يجب ان تكون عضوا لدينا لتتمكن من التعليق

انشئ حساب جديد

سجل حسابك الجديد لدينا في الموقع بمنتهي السهولة .


سجل حساب جديد

تسجيل الدخول

هل تمتلك حساب بالفعل ؟ سجل دخولك من هنا.


سجل دخولك الان

Ads Belongs To mzdhr

  • بسم الله الرحمن الرحيم
     
    في إستمرارنا في الحديث عن أهم وأحدث مكتبات منصة الأندرويد سنتحدث اليوم عن واحدة من أشهر المكاتب المستخدمة مؤخراً في بعض التطبيقات المشهورة ومن ضمنها تطبيق Jodel الشهير. هذه المكتبة هي مكتبة Crouton. لنتعرف معاً على هذه المكتبة وما أهميتها وكيف تعمل.
    مكتبة Crouton هي مكتبة تتيح لك تنبيه المستخدم وإظهار بعض الإشعارات. تشبه في عملها مكتبة Toast الشهيرة ولكنها تختلف عنها بأنها تحل بعض المشكلات المتعلقة بـ Toast. واحدة من أهم مشاكل الـ Toast هي مشكلة out of context وهي بأن Toast تعمل وتظهر بغض النظر عن الـ context أو المضمون. قد تظهر في سياق مختلف تماماً عن المتوقع أي أنه عند الإنتقال لـ Activity أخرى سيستمر إشعار الـ Toast بالظهور كما أنها غير قابلة للتعديل وموحدة الشكل. كل هذه المشاكل من الممكن حلها بمكاتب مختلفة ولكن من أسهل هذه المكاتب هي Crouton. ولكن السؤال .. كيف تعمل ؟
     
    مكتبة Crouton تتيح لك التحكم الكامل بشكل الإشعارات ولونها وخصائصها بما تتناسب مع تطبيقك. تعطيك كبداية 3 أشكال ثابتة إذا أردت الإبقاء على أشكال Crouton دون أي تغيير. هذه الأشكال هي :
    1-      Alert Notification : باللون الأحمر والخط الأبيض ولمدة 3 ثواني تقريباً
    2-      Info Notification : باللون الأزرق والخط الأبيض ولمدة 3 ثواني تقريباً
    3-      Confirm Notification : باللون الأخضر والخط الأبيض ولمدة 3 ثواني تقريباً
    لنبدأ التطبيق ونرى كيفية إظهار هذه الأشكال.
     
    قبل بداية التطبيق وكما جرت العادة سنحتاج إلى إضافة Dependency لملف build.gradle ولكن هذه المرة سنحتاج لتعديل ملفي الـ gradle.
    بالنسبة لملف build.gradle "project" سنضيف 
    mavenCentral() داخل Block الـ repositories الموجود في buildscript
     
    بالنسبة لملف build.gradle "module" سنضيف
    compile 'de.keyboardsurfer.android.widget:crouton:[email protected]' نقوم بعمل Sync من الأعلى حتى تضاف المكتبة ونستطيع بعدها البدء في العمل.
     
    نقوم بإنشاء Layout بسيطة توضح عمل هذه المكتبة. تحتوي هذه الـ Layout على 3 أزرار خاصة بكل style وأيضاً على زر Toast وزر آخر لتوضيح الـ Custom Notification.
    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingTop="50dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="INFO" /> <Button android:id="@+id/alert" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Alert" /> <Button android:id="@+id/succ" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Success" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/toast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Toast" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/custom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Custom" /> </LinearLayout> </LinearLayout>  
    صورة توضح الشكل النهائي للـ Layout

     
    نذهب الآن للجزء الأهم وهو ملف الـ Java.
    ملاحظة : سنقوم بتطبيق مفاهيم الـ ButterKnife. لمزيد من المعلومات نرجو زيارة الموضوع التالي:
    دليلك لأفضل مكاتب الأندرويد - الجزء الثاني - ButterKnife
     
    أولاً : سنقوم في البداية بتعريف المتغيرات والـ Buttons
    @BindView(R.id.alert) Button alert; @BindView(R.id.info) Button info; @BindView(R.id.succ) Button succ; @BindView(R.id.toast) Button toast; @BindView(R.id.custom) Button custom;  
    ثانياً : سنقوم بعمل Bind للمتغيرات داخل onCreate method
    ButterKnife.bind(this);  
    ثالثاً : سنقوم بإختيار كل زر للقيام بوظيفة معينة. نلاحظ عندما نريد إظهار Crouton أو إشعار لا نحتاج لتعريف أي متغيرات فهي تعمل بنفس طريقة عمل Toast. لإظهار الإشعارات بالأشكال السابقة التي سبق وتحدثنا عنها سنحتاج لتمرير 3 params فقط وهي الـ Context والنص والشكل سواء كان alert – info – confirm  والطريقة تبدو مشابهة تماماً لطريقة الـ Toast.
    @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO); } @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", Style.ALERT); } @OnClick(R.id.succ) void clicked3() { Crouton.showText(this, "SUCCESS 3alamPro", Style.CONFIRM); } @OnClick(R.id.toast) void clicked4() { Toast.makeText(getApplicationContext(), "3alamPro", Toast.LENGTH_LONG).show(); }  
    رابعاً : نقوم بإختبار التطبيق ونرى الفرق بين Crouton وبين Toast. وكيف أن Toast تستمر بالظهور حتى عند الخروج من التطبيق ولكن Crouton تختفي وهو المطلوب.

     
    ماذا لو أردنا البقاء على هذه الأشكال ولكن نريد تغيير الوقت المحدد مسبقاً. بكل بساطة سنحتاج لعمل Object من كلاس Configuration الموجود مسبقاً داخل مكتبة Crouton.
    Configuration croutonConfiguration;  
    ومن ثم نعرف الـ Object داخل onCreate method ونعطيه الوقت المطلوب ( الوقت المدخل يقاس بالـ milliseconds والثانية الواحدة تساوي 1000 ميلي ثانية ).
    croutonConfiguration = new Configuration.Builder().setDuration(1000).build(); // 1 sec  
    الآن سنقوم بتغيير الوقت لواحدة من الإشعارات المعرفة مسبقاً ولكننا سنحتاج لتمرير باراميتر إضافي وهو getTaskId() سنتكلم عليه فيما بعد ولكن في الوقت الراهن قد لا يهمنا كثيراً. بهذا التعديل سيتغير الوقت ليصبح ثانية واحدة فقط بدلاً من 3 ثواني.
    @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO,getTaskId(),croutonConfiguration); }  
    نقوم بالتجربة والمقارنة بين INFO وبين ALERT ونستطيع رؤية الفرق في التوقيت.

     
    بعد الإنتهاء من أول قسم سنبدأ في تصميم الـ Notification الخاصة بنا. وطبعاً هذا الأمر متاح بكل سهولة مع Crouton. سنقوم في البداية بتغيير لون الخط ولون خلفية الإشعار فقط. نلاحظ بأننا كنا نمرر باراميتر من نوع Style داخل crouton. الباراميتر Style.INFO عبارة عن ستايل جاهز ولتغييره سنحتاج لعمل Style خاص بنا. لذلك سنحتاج إلى عمل Object من كلاس style وإضافة جميع الخصائص بشكل يدوي.
    Style style;  
    ونقوم بتعريف الـ Object داخل onCreate method.
    style = new Style.Builder() .setBackgroundColorValue(Color.parseColor("#000000")) // black .setGravity(Gravity.CENTER_HORIZONTAL) .setConfiguration(croutonConfiguration) .setHeight(100) .setTextColorValue(Color.parseColor("#ffffff")).build(); // white  
    للشرح بشكل أعمق للخصائص : 
     setBackgroundColorValue(Color.parseColor()) نستخدمها لإختيار لون خلفية الإشعار.
    setGravity نستخدمها لإختيار مكان النص سواء كان في المنتصف أو على اليمين أو على اليسار.
    setConfiguration نستخدمها لإختيار المدة الزمنية لإظهار الإشعار. (يجب إنشاء Object منفصل كالذي تم إنشاؤه في الخطوات السابقة )
    setHeight نحدد من خلالها إرتفاع الإشعار.
    setTextColorValue نحدد من خلالها لون خط النص داخل الإشعار.
    ومن ثم ننهي التعريف بـ .build
     
    نقوم الآن بتعديل واحدة من الإشعارات المعرفة سابقاً وإختيار الـ style الخاص بنا بدلاً من المعرف مسبقاً.
    @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", style); }  
    ونرى النتيجة. تعمل بشكل مثالي.

     
     
    سنقوم الان بعمل إشعار مختلف تماماً يتضمن صور ونص وليس نص فقط. لتطبيق هذا الأمر سنحتاج إلى إنشاء Layout جديدة. سنقوم داخل هذه الـ Layout بتصميم شكل الإشعار. نقوم بإنشاء Layout جديدة ونسميها crouton_custom على سبيل المثال.

     
    نقوم بتصميم Layout بسيطة تتضمن شعار الموقع فقط. نلاحظ بأن إرتفاع الـ Layout يمثل إرتفاع الإشعار نفسه لذلك إختيار الإرتفاع مهم جداً.
    <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="#000000"> <ImageView android:layout_width="100dp" android:layout_height="30dp" android:layout_centerInParent="true" android:src="@drawable/pro"/> </RelativeLayout> </RelativeLayout>  
    الشكل النهائي للـ Layout.

     
    للاستمرار بذلك يجب علينا تعريف Object جديد من نوع View وعمل Inflate للـ Layout الخاصة بالإشعار. ومن ثم عمل Crouton جديد.
    @OnClick(R.id.custom) void clicked5() { View customView = getLayoutInflater().inflate(R.layout.crouton_custom, null); Crouton.show(this,customView); }  
    والنتيجة ..

     
    إلى هنا نكون قد وصلنا إلى نهاية الشرح الخاص بالمكتبة. وسنقوم في المواضيع القادمة بإستعراض المزيد من المكتبات الخاصة بمنصة الأندرويد بإذن الله.
    مستوى المقال: مبتدئ

    بواسطه Abdulrahman Hasan Agha , في

  • بسم الله الرحمن الرحيم
    السلام عليكم ورحمة الله وبركاته
     
    Hashing
     
    الكثير من الـ algorithms تستخدم في عملية البحث البسيطة عن Record معين أو Data معينة وسط الآلاف وأحيانا مئات الالاف من الملفات المخزنة في الذاكرة.
    العامل المهم في عملية اختيار الطريقة الممتازة تعتمد على الوقت وفعالية الجهاز المستخدم في عملية البحث ومدى كفائته. وواحدة من أفضل الطرق المستخدمة حالياً هي الـ Hashing .
    سنتعرف معاً في هذا المقال على :
    -          تعريف بسيط عن الـ Hashing.
    -          بعض الطرق المستخدمة في التعامل مع الـ Data في حالة التكرار في طريقة الـ Hashing.
    -          طريقة التطبيق في لغة برمجة Java.
     
    تعريف بسيط عن الـ Hashing:
    هي طريقة تستخدم لحفظ الملفات وتخزينها ومن ثم البحث عنها بأسرع طريقة ممكنة. تتميز هذه الطريقة عن غيرها بأنها قادرة على جلب الـ Data بوقت سريع جدا وسط ملفات ومعلومات كبيرة نوعاً ما مقارنة بغيرها من الطرق.
    هذه الطريقة تتميز بحفظ الـ Data في جدول بحيث يتم إعطاء كل معلومة رقم مميز يتم حفظه داخل جدول من البيانات ومن ثم يتم الوصول إليه مباشرة في حال الحاجة إلى البحث عنه. هذه الطريقة تتميز بقلة الوقت اللازم للقيام بهذه العملية بحيث يمكنك مباشرة معرفة ما إذا كان الملف الجاري البحث عنه موجود أو غير موجود دون الحاجة للبحث عنه داخل الذاكرة بأكملها.
    مثال :
    لنفرض بأن لدينا الأرقام التالية : 1-7-4-8-9-5 .
    إذا قمنا بحفظ هذه الأرقام داخل Array بشكل عشوائي مباشر على سبيل المثال فعند البحث عن رقم 9 ستضطر إلى البحث داخل الـ Array بالكامل لمعرفة ما اذا كان الرقم 9 موجود أو لا. تخيل بأن لديك أكثر من الف رقم او أكثر!! يمكنك تخيل الوقت الطويل الذي ستحتاجه للقيام بعملية البحث هذه.
    لكن باستخدام Hashing يمكننا مثلا تخزين هذه الأرقام داخل Array ولكن ليس بشكل عشوائي كما في السابق وإنما بالنسبة لباقي قسمة العدد على حجم الـ Array على سبيل المثال. لنتفرض بأننا انشأنا Array بحجم 9 خلايا، الصف الأول يوضح الـ index والصف الثاني يوضح العدد الموجود داخل هذا الـ index
    8 7 6 5 4 3 2 1 0 index                   value عند تعبئة هذه الـ Array سنستخدم العلاقة التالية : index = number % arrayLength. بمعنى آخر سيتم وضع الرقم داخل الخانة المعبرة عن باقي قسمة العدد على حجم الـ Array.
    فيكون الجدول بعد التعبئة بهذا الشكل
    8 7 6 5 4 3 2 1 0 index 8 7   5 4     1 9 value لاحظ بأن الرقم 9 وضع بخانة رقم 0 لان باقي قسمة 9 على حجم الـ Array سيعطيك صفر.
    الآن بامكانك مباشرة الذهاب حين البحث عن رقم 9 إلى الرقم المستخرج من الدالة index = number % arrayLength وهو صفر لتعرف ما اذا كان الرقم 9 موجود أو لا وبالتالي توفير الكثير من الوقت.
     
    بعض الطرق المستخدمة في التعامل مع الـ Data في حالة التكرار في طريقة الـ Hashing:
    في المثال السابق لم نصادف أي تكرار في قيمة باقي قسمة الارقام على حجم الـ array ولكن كيف من الممكن حل مشكلة تكرار القيم في حال وقوعها ؟
    هناك الكثير من الحلول التي من الممكن استخدامها لحل هذه المشكلة ولكن سيتم ذكر حل واحد في هذا المقال وسيتم الحديث عن باقي الحلول في مقالات قادمة إن شاء الله.
    1 - Separate Chaining : هذه الطريقة تعتمد على بناء ArrayList بشكل خاص أو أي Structure آخر بشكل عام داخل كل خلية في الـ Hash Table الموضح في الأمثلة السابقة.
    لنأخذ هذه الأعداد على سبيل المثال : 12 – 17 – 29 – 6 – 30 – 31 – 4 – 8 ولنقم بتوزيعها علىHash Table  بحجم 4 خلايا.

     
    في هذه الحالة يتم أولا معرفة الـ index المراد البحث داخله ومن ثم البحث داخل الـ ArrayList لمعرفة ما اذا كان الـ record المراد البحث عنه موجود أو غير موجود.
     
    طريقة التطبيق في لغة برمجة Java:
    هناك الكثير من الطرق لتطبيق فكرة الـ Hashing بواسطة الجافا ولكن سنقوم باتباع الطريقة الأسهل والأقل تعقيداً. سنحتاج إلى إنشاء كلاس List :
    List Class  : عبارة عن كلاس سيتم عمل objects منه  ويحتوي على الـ ArrayList المراد تخزين الـ Data فيها بالإضافة إلى Constructor.
    import java.util.ArrayList; public class List { private ArrayList<Integer> list = new ArrayList<Integer>(); //Constructor public List(){ } public ArrayList<Integer> getList(){ return list ; } }  
    نحتاج أيضاً لإنشاء test class للتأكد من صحة الـ HashTable 
    Test Class : في التيست كلاس سنقوم بإنشاء Array من كلاس List. لاحظ بأنه يجب عليك تعريف كل عناصر هذه الـ Array وبدون هذا التعريف سيظهر لك NullPointerException.
    //hash table List [] hashTable4 = new List [4] ; for (int i = 0 ; i<hashTable4.length ; i++) hashTable4[i] = new List();  
    ميثود print لطباعة الجدول 
    public static void print(List [] array){ for (int i = 0 ; i<array.length ; i++) System.out.println("index "+i+" : "+array[i].getList().toString()); }  
    ميثود getHashCode لمعرفة الـ index المناسب 
    public static int getHashCode(int num){ return num%4 ; }  
    نقوم بتخزين الأرقام في array لتسهيل عملية التصنيف والإدخال ومن ثم نقوم بإدخالها في object hashTable4
    //numbers we want to add int [] listOfNumbers = {12,17,29,30,6,31,4,8}; //add Numbers to the hash table for (int i = 0 ; i<listOfNumbers.length ; i++){ int index = getHashCode(listOfNumbers[i]); hashTable4[index].getList().add(listOfNumbers[i]); }  
    المخرج النهائي 

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

    بواسطه Abdulrahman Hasan Agha , في

  • من أكثر الأسباب التي تؤدي الى زيادة معدل تقييم تطبيقك في متجر التطبيقات هو عن طريق سؤال المستخدم من داخل التطبيق !
     
    هناك طرق كثيره ومكتبات متنوعه لفعل ذلك ووظيفتها تكمن في سؤال المستخدم ما اذا اعجبهم التطبيق ويريد كتابة مراجعة له ، فيتم تحويلهم الى متجر التطبيقات
     
    ولكن اليوم سوف نتحدث عن API آبل اضافته في تحديث iOS 10.3
    يطلق عليه SKStoreReviewController
     
    الفكرة في هذا الـ API هو سؤال المستخدم من داخل التطبيق بدون الحاجة الى تحويلهم الى متجر التطبيقات !
    لكن العيب الوحيد هو عدم المقدرة على جعلهم يكتبوا مراجعة للتطبيق ، فقط يستطيعوا تقييم التطبيق عن طريق النجوم .
     
    وطبيعة الحال هناك عدة شروط :
     
         يجب الا يستخدم الـ API بداخل ViewDidLoad  أو ViewWillApper او غيرهم بدون وجود Logic ، فانت لا تريد أن يقيم المستخدم التطبيق بمجرد فتحه لأول مره وهو لم يستخدمه بعد !     يجب ان لا يستخدم الـ API بداخل Button او يتطلب أي Action من المستخدم ، قد يسبب ذلك في رفض تطبيقك اذا وضعته بداخل Action !    تذكر بأن كل شيء يتم تلقائيا اعتماداً على النظام ، من حيث ظهور رسالة طلب التقييم من عدمها ! لهذا السبب وجد الشرط السابق    أهم شرط هو رسالة التقييم سوف تظهر فقط 3 مرات في السنه لكل مستخدم .    اثناء فترة التطوير الرسالة سوف تظهر بشكل دائم     عند ارسال تطبيقك كنسخه تجربيه باستخدام TestFlight الرسالة لن تظهر ! ، بمعنى أخرى سوف تظهر فقط عند نشر تطبيقك في متجر التطبيقات .  
    ومن النصائح اذا اردت تقييم إيجابي للتطبيقك ، اطلب ظهور رسالة التقييم اذا فعل المستخدم امراً إيجابياً !
    ولا تقاطع المستخدم عند عمل مهمه معينه !! ، ولكن اسأله بعد الانتهاء من مهمة محددة
     
    لذا من الأمثله :
     اذا التطبيق متجر الكتروني اطلب رسالة التقييم بعد إتمام الشراء. اذا كان التطبيق لعبة ، اطلب رسالة التقييم بعد تحقيق سكور عالي في اللعبة أو بعد انتهاء من مرحلة او عدة مراحل في اللعبة . اسأله بعدد عدد مرات معينه من استخدام التطبيق وليس من أول مره !
    بحيث تسأل المستخدم لتقييم تطبيقك في وقت يكون في مزاج جيد !  
    اذا كيف يتم استخدامه ؟
     
    كما ذكرت سابقا الأمر بيد المطور
     
    بصورة مختصرة :
     
    الذي يتطلبه هو فقط استدعاء
    import StoreKit  
    في داخل الـ ViewController الذي يريد ظهور الرسالة عليه
     
    ومن ثم استخدام السطر التالي ، في أي مكان يرده المطور
    SKStoreReviewController.requestReview()  
    بصورة افضل هو حساب عدد مرات فتح التطبيق وإظهار الرسالة بعد عدد مرات محدده
    من دراسة قراءتها بأن افضل وقت هو بعد 5 مرات ويفضل بعد 10 مرات
    ولكن ليس أقل من ذلك ، بهذا الوقت بعد 5 مرات او 10 مرات تكون متأكد بأن المستخدم يستخدم التطبيق باستمرار .
     
    اذا كيف تحسب وقت استخدام المستخدم للتطبيق ؟
     
    عن طريق الاستفادة من UserDefaults
     
    اذا لا تعلم ماهو UserDefaults
    فهو طريقة لحفظ البيانات بشكل دائم ويستخدم مع البيانات البسيطة
    كحفظ رقم Integer مثلا
    او حفظ حالة الـ Switch اذا مفعل او مغلق الخ
     
     
    وبالتالي الفكرة هيا بوضع عدد محدد كمتغير وفي هذا المثال سوف نضع المدة 4
    لأننا نريد أن تظهر رسالة التقييم بعد خامس مره ، ولأنه العد يبدأ من 0
    فالرقم 4 = خامس مره
    0 , 1 , 2 , 3 , 4
     
    ونضع متغير اخر يحسب عدد مرات فتح التطبيق (ViewController)
    ومن ثم نعمل مقارنه اذا وصلت عدد المرات الى 5 مرات تظهر الرسالة
    اذا لم يوصل نزود قيمة المتغير ونحفظه في UserDefaults
     
     
     
    الكود يصبح بالشكل التالي :

     
    import UIKit import StoreKit class ViewController: UIViewController {     let minimumRunCount = 4     let userDefaultsKey = "minimumRunCountUserDefaultsKey"     override func viewDidLoad() {         super.viewDidLoad()         if ShowStoreReview() {             SKStoreReviewController.requestReview()         }     }        func ShowStoreReview() -> Bool {         let count = UserDefaults.standard.integer(forKey: userDefaultsKey)         if count >= minimumRunCount {             return true         } else {                        UserDefaults.standard.set((count + 1), forKey: userDefaultsKey)         }         return false     } }  
    اذا كما ذكرنا سابقا
     
    في البداية عرفنا الحد الادنى قبل ظهور الرسالة في  minimumRunCount  
    عرفنا الـ Key للـ userDefault
    يمكنك كتابة أي شي تريده
     
    بالنسبة الى UserDefaults.standard.integer
    القيمة سوف تكون 0 بشكل تلقائي عند استخدامه
    وهذا ما نريده
    لذا قيمة count سوف تكون 0  في اول مره
     
    ومن ثم عملنا مقارنة اذا كانت قيمة count اكبر او يساوي قيمة minimumRunCount  
    تظهر ترجع قيمة true وبالتالي تظهر الرسالة
     
    اذا لم تكون تساوي او اكبر من قيمة  minimumRunCount  
     
    نزود واحد على قيمة count ونحفظها في userDefault
     
    لذا في المره الثانية يفتح فيها التطبيق تكون قيمة count بـ 1
    عوضاً عن 0
     
    وهكذا
     
    وعندما توصل لقيمة 4
    تظهر الرسالة
     
    صورة الرسالة :

     

     
    هذه من الأمثلة التي تستطيع الاستفادة منها
    تستطيع تحسينها حسب احتياجك
     
    اريد أن أوضح نقطة اخيره وهيا الـ SKStoreReviewController
    فقط يظهر لمستخدم نظام 10.3 فأعلى
     
    لذا تستطيع إضافة السطر التالي في ViewDidLoad
     

     
            if #available(iOS 10.3, *) {             SKStoreReviewController.requestReview()                     } else {  وهنا ضع الطريقة القديمة لتحويله الى متجر التطبيقات//         }  
    فيصبح الكود  ViewDidLoad النهائي بالشكل التالي :

     
        override func viewDidLoad() {         super.viewDidLoad()         if ShowStoreReview() {             if #available(iOS 10.3, *) {                 SKStoreReviewController.requestReview()                          } else {             }         }     }  
     
    واخيراً اريد أن اذكر بأن هناك مطور جرب استخدام SKStoreReviewController
    في تطبيقه ، وادى ذلك الي حصوله على 200 تقييم في خلال 8 أيام !
     
    لذا ينصح بشده باستخدام SKStoreReviewController في تطبيقاتكم
     
    فهناك عدد كبير من التطبيقات في المتجر لا تملك أي تقييم !
     
    لذلك آبل إضافة هذا الـ API في نظامها
     
     
    مستوى المقال: محترف

    بواسطه باسل العمودي , في

  • السلام عليكم ورحمة الله وبركاته
    الدرس الثامن : بناء جهاز لقياس درجة الحرارة والرطوبة .

     
    نبذة عن الدرس :
    من خلال هذا الدرس سوف نقوم بعمل جهاز قادر على قراءة درجة الحرارة وايضا نسبة الرطوبة وذلك من خلال مستشعر الحرارة والرطوبة , ويتم عرضها على الشاشة الكريستالية , المقاومة المتغيرة لتغيير سطوع الشاشة .
     
    متطلبات المشروع :
    أردوينو اونو (Arduino Uno)
    مستشعر الحرارة والرطوبة (DHT11 or DHT22)
    مقاومة متغيرة (variable resistor)
    الشاشة كريستالية(lcd 2*16 )
    أسلاك توصيل (Jumper wire) .
    لوحة تجارب (Breadborad)
     
    الدائرة الكهربائية :
     

     
    آلية عمل حساس الرطوبة والحرارة (DHT11) :
    لدى هذا الحساس القدرة على قراءة درجة الحرارة والرطوبة في البيئة المحيطة به .
     
    الفرق بين DHT11 و DHT22 :

     
    معلومات عن الشاشة الكربستالية :
    يوجد العديد من مقاسات الشاشات الكريستالية مثل 2*16 و 4*20  وفي هذا الدرس استخدمنا مقاس 2*16   وتعني إن الشاشة قادرة على عرض سطرين وكل سطر يحتوي على 16 حرف أو رمز.
     
    الكود البرمجي :
    في البداية نحتاج إلى إضافة مكتبة DHT  والتي تسهل علينا التعامل مع حساس الرطوبة والحرارة.
    أولا : نقوم بتحميل الملف المضغوط  .
    DHT library.zip
    ثانيا :نقوم بتشغيل برنامج Arduino IDE .
    ثالثا :نذهب إلى الشيفرة البرمجية ---< إدراج مكتبة  ---< أضف مكتبة ---< قم بإختيار الملف المضغوط .
    رابعا : قم بإغلاق البرنامج ثم إعد تشغيله .
     
    #include <LiquidCrystal.h> #include "DHT.h" LiquidCrystal lcd(8, 9, 10, 11, 12, 13); int DHTPIN =3; #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); void setup() { dht.begin(); lcd.begin(16, 2); } void loop() { int hum = dht.readHumidity(); int tem = dht.readTemperature(); lcd.setCursor(0, 0); lcd.print("Humidity: "); lcd.print(hum); lcd.setCursor(0, 1); lcd.print("Temperature: "); lcd.print(tem); delay(500); }  
    بعد رفع الكود للاردوينو أضف أحد مصادر الطاقة المذكورة في الدرس السابق لتنقل بها إلى أي مكان .

     
    شرح الكود البرمجي :
    #include <LiquidCrystal.h> تعريف بالمكتبة الخاصة بالشاشة الكريستالية
    #include "DHT.h" المكتبة الخاصة لمستشعر الحرارة والرطوبة
     
    LiquidCrystal lcd(8, 9, 10, 11, 12, 13); دالة تحمل أرقام المنافذ الاردوينو المتصلة بالشاشة الكريستالية .
     
    #define DHTTYPE DHT11 تعريف نوع مستشعر الحرارة والرطوبة المستخدم في درسنا وهو DHT11.
    لو كنت تستخدم نوع DHT22  فقط قم بإستبدال النوع في الكود السابق .
    DHT dht(DHTPIN, DHTTYPE); دالة لتهيئة الحساس وتحمل متغيرين متغير يحمل رقم المنفذ واخر نوع المستشعر .
     
    lcd.begin(16, 2); تعريف بمقاس الشاشة المستخدمة وهو 2*16 .
     
    int h = dht.readHumidity(); متغير لقراءة نسبة الرطوبة من المستشعر .
    int t = dht.readTemperature(); متغير لقراءة درجة الحرارة من المستشعر .
    lcd.setCursor(0, 0); هذه الدالة لجعل المؤشر في بداية السطر الاول.
    lcd.setCursor(0, 1); هذه الدالة لجعل المؤشر في بداية السطر الثاني.
     
    حل تحدي السابق :
    التحدي مذكور في هذا المقال

    يمكنك تحميل حل التحدي من هنا :
    waduino_3.ino
     
    تحدي بسيط :
    قم بجعل الشاشة تعرض فقط درجة الحرارة ثم تعرض درجة الرطوبة فقط لمدة 500 ملي ثانية لكل منهما .
    مستوى المقال: مبتدئ

    بواسطه وضاح العوني , في

  • بسم الله الرحمن الرحيم
    في هذا الموضوع سنقوم بمناقشة المواضيع التالية في التعامل مع صفوف الجداول :
    طريقة إخفاء الصفوف الفارغة في الجدول . طريقة إظهار الصفوف المطلوبة و إخفاء بعض الصفوف ( عن طريق تغيير ارتفاع الصف) .  
    ١- طريقة إخفاء الصفوف الفارغة في الجدول :
    سنقوم بعمل جدول وإضافة بعض القيم فيه ( بافتراض بأن لديك معرفه مسبقة لطريقة عمل الجداول) وهذا الجدول الذي قمت بعمله :

     
    نلاحظ بالجدول السابق وجود صفوف فارغة وقد تكون مزعجة نوعاً ما وقد نرغب في إخفائها :
     
    وحتى تقوم بإخفائها كل ماعليك هو اتباع إحدى الطرق التالية : 
    (لنفترض بأن TableView لدينا اسمه ColorTable)
     
    الطريقة الأولى : إخفاء الـ separator :
    باستخدام السطر التالي بداخل دالة viewDidLoad :
    ColorTable.separatorStyle = .none  
    أو من خلال Interface Builder للجدول ColorTable :

     
    وستكون النتيجة كالتالي :
     

     
    بالنسبة لهذه الطريقة فكما نلاحظ فقد تم إخفاء ال Separetor للجدول كامل .
     
    الطريقة الثانية : بإضافة Footer وجعل الارتفاع له يساوي 0 :
    وذلك بإضافة السطر التالي بداخل داله  viewDidLoad :
    ColorTable.tableFooterView = UIView(frame: .zero)  
    وستكون النتيجة كالتالي :
     

    الطريقة الثالثة : بإضافة UIView كـ Footer وذلك كالتالي :
     
    ١- قم بإضافة UIView أسفل الجدول كما في الصوره ( قمت بتغيير لون UIview للون الأزرق حتى تتضح لكم الفكرة) :


     
    ٢- بعد إضافة ال UIview قم بتغيير الارتفاع له بحيث يكون مساوياً لـ 0 وذلك من خلال Interface Builder:
     

     
    والنتيجة كالتالي :
     

     
    ٢- طريقة إظهار الصفوف المطلوبة و إخفاء بعض الصفوف :
    هذه الطريقة نستطيع الاستفاده منها في حاله أردنا إخفاء بعض الصفوف بشكل مؤقت ( وليست حذف للصفوف) وذلك عند تنفيذ أو حدوث أمر معين ، مثال على ذلك لو أردت إنشاء تقويم Calendar
    وتحت هذا التقويم ترغب بعرض الأحداث Events لذلك الشهر وإخفاءها في الشهور الأخرى :

     
    الفكرة هنا تكون من خلال تغيير الارتفاع للصف المطلوب وجعله مساوياً لل 0 ، ويتم عمل ذلك من خلال استخدام ال function التالية :
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { } سنقوم بتطبيق الفكره على جدولنا ColorTable  ونقوم بـ (إخفاء) اللون الأصفر من الجدول :
    النقاط المهمة لذلك هي :
    ١- انشاء الدالة التالية للتعامل مع ارتفاع صفوف الجدول :
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return } ٢- تعريف متغير من نوع CGFloat و تكون قيمته = 0.0 و هو يدل على قيمة الارتفاع للصف :
    var rowHeight:CGFloat = 0.0  
    ٣- نستخدم ال if - else وذلك للتحقق من الشرط الذي سيتم إخفاء الصفوف المطلوبة عند حدوثه وتحقق الشرط ففي حاله تحقق الشرط ستكون قيمة المتغير الذي تم تعريفه = 0.0
    وإذا لم يتحقق سنجعل ارتفاع الصفوف مثلاً = 60.0 :
     
    let RC = "Yellow" let cell1 = ColorTable.dequeueReusableCell(withIdentifier: "cell") cell1?.textLabel?.text = list[indexPath.row] if (RC == cell1?.textLabel?.text) { rowHeight = 0.0 }else { rowHeight = 60.0 } return rowHeight  
    الكود النهائي بداخل الدالة السابقة سيكون كالتالي :
     
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { var rowHeight:CGFloat = 0.0 let RC = "Yellow" let cell1 = ColorTable.dequeueReusableCell(withIdentifier: "cell") cell1?.textLabel?.text = list[indexPath.row] if (RC == cell1?.textLabel?.text) { rowHeight = 0.0 }else { rowHeight = 60.0 } return rowHeight }  
    وبذلك سيتم (إخفاء) اللون Yellow من القائمة :

     
    هذا مثال بسيط جداً لإخفاء الصفوف بشكل مؤقت دون حذفها فقط حتى تصل الفكرة لكم ..
     
    * خلاصة ماتعلمناه اليوم :
    طريقة إخفاء الصفوف الفارغة في الجدول : من خلال ٣ طرق :
    ١- إخفاء ال Separator :
    ColorTable.separatorStyle = .none ٢-  إضافة Footer وجعل الارتفاع له = 0 :
    ColorTable.tableFooterView = UIView(frame: .zero) ٣- إضافة UIView كـ Footer وجعل ارتفاعه =0 .
     
    طريقة إظهار الصفوف المطلوبة و إخفاء بعض الصفوف ( عن طريق تغيير ارتفاع الصف) . وذلك باستخدام الدالة التالية التي من خلالها نستطيع التحكم بقيمة ارتفاع الصفوف :
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { }  
    وبذلك نكون قد انتهينا اتمنى أن أكون قد وفقت في الشرح وتبسيط المعلومة لكم
    وأرحب بأي مناقشه وأي سؤال يخص الموضوع 🌹
    مستوى المقال: مبتدئ

    بواسطه ساره علي , في

  • Ads Belongs To mzdhr

    عالم البرمجة

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