شرح App Shortcuts


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

قامت شركة قوقل باطلاق انسخة ٧.١ من نظام نوقا ولم يكن التحديث لاصلاح المشاكل وانما جلب معه بعض المميزات الجديدة ولعل من ابرزها هو اختصارات التطبيق  App Shortcuts وسنقوم باذن الله في هذا الدرس بشرح هذه الميزه وطريقة استخدامها.

ماهي App Shortcuts ومتى نستخدمها ؟!

ببساطة تقوم باضافة قائمة على التطبيق تسمح للمستخدم بعرضها دون الدخول الى التطبيق وهي مشابهه لما يوجد في نظام ios  . ويمكن استخدامها لعرض مهمات او خصائص للمستخدم تسهل الوصول لها بسرعه وسلاسة !،

تحتوي على نوعين :

static : تضاف في ملفات الريسورس للتطبيق وتكون ثابته ولايمكن تغييرها الا بنشر التطبيق مره اخرى

dynamic : نقوم باضافة في الوقت الفعلي ويمكن تحديثها دون الحاجة الى نشر التطبيق مره اخرى

اضافة App Shortcuts :

اضافة اختصارات التطبيق تكون بشكل سهل وسنبدأ باضافة القائمة الثابته والتي سنعرفها من ملفات resources .

Nov-28-2016 15-57-19.gif

 

STATIC SHORTCUTS

ساقوم بافتراض ان لديك مشروع في تطبيق اندرويد ستديو واحتاج منك الذهاب الى ملف AndroidManifest.xml وستقوم باضافة meta-data الموجود في الكود في الاسفل :

<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />

تلاحظ في تاق meta-data يوجد android:resource وهذا المفتاح من يتعامل مع resource في التطبيق والذي عرف في res/xml/shourtscuts.xml هنا يمكنك تعريف اختصاراتك الثابته للتطبيق.

الان سنقوم باضافة اختصار يفتح لنا مثلاً StaticShourtcutActivity كما سنلاحظ في الكود التالي :

<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
    <shortcut
        android:enabled="true"
        android:icon="@drawable/ic_home_black_24dp"
        android:shortcutDisabledMessage="@string/static_shortcut_disabled_message"
        android:shortcutId="static"
        android:shortcutLongLabel="@string/static_shortcut_long_label"
        android:shortcutShortLabel="@string/static_shortcut_short_label">
        <intent
            android:action="android.intent.action.VIEW"
            android:targetClass="com.example.ahmed.appshoutcut.StaticActivity"
            android:targetPackage="com.example.ahmed.appshoutcut" />
    </shortcut>
</shortcuts>

 

تلاحظ ان root للكود هو shortcuts والذي سيقوم بالتعامل مع اكثر من shourtcut في التطبيق :

enabled

هنا تقوم بتفعيل او الغاء تفعيل الاختصار

icon

الايقونة الخاصة بالاختصار

shortcutDisabledMessage 

تظهر رسالة في حال قام المستخدم بالضغط على اختصار غير مفعل

shortcutLongLabel

عنوان طويل يظهر في حال كان الانشر يستطيع عرضها “التابلت مثلاً”

shortcutShortLabel

هنا هو النص الذي سيظهر للمستخدم كعنوان للاختصار

intent

هنا المهمه او الامر الذي سيظهر في حال المستخدم اختار الاختصار


DYNAMIC SHORTCUTS

الان سنقوم بالتعامل مع النوع الثاني من الاختصارات وهي “المتغيره” والتي تتحدث مباشرة دون الحاةه الى نشر التطبيق مره اخرى وعلى عكس الاختصارات الثابته لن نحتاج الى اضافتها في xml او resource التطبيق ولكن سنقوم بكتابتها في كود Java .

الان سنقوم باضافة اول اختصار متغير وسنتعامل مع ShortcutManager و ShortcutInfo.Builder وسنقوم ببناء اول اختصار متغير في الشاشة الرئيسية MainActivity.onCreate#

 ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
        
        ShortcutInfo webShortcut = new ShortcutInfo.Builder(this, "shortcut_web")
                .setShortLabel("a7med.name")
                .setLongLabel("Open a7med.name web site")
                .setIcon(Icon.createWithResource(this, R.drawable.ic_web_black_24dp))
                .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://a7med.name")))
                .build();

        shortcutManager.setDynamicShortcuts(Collections.singletonList(webShortcut));

الان عند تشغيل التطبيق سنلاحظ اضافة الاختصار الثاني الخاص بنا .

هناك الكثير عن App Shortcuts سأتكلم عنه في تدوينات لاحقة باذن الله

شكرا لكم جميعاً وعذراً على الانقطاع

A7med
  • تم تعديل بواسطه A7med


1


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


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



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

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

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

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


سجل حساب جديد

تسجيل الدخول

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


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

Ads Belongs To This website

  • بسم الله الرحمن الرحيم 
    أطلقت google مؤخراً Android IoT وفي هذه المقالة البسيطة سنقوم بثبيت النسخة على ٌRaspberry Pi 3 وعمل مشروع بسيط.
    بداية عليك تحميل النسخة الخاصة بالـ Raspberry Pi 3 من هنا وبعد ذلك تثبيته على SD card حسب النظام الذي تستخدمه, بالنسبة لمستخدمي Ubuntu كالتالي 
    sudo dd if=Downloads/androidthings_rpi3_devpreview_4.img of=/dev/yourFlash  بعد ذلك ادخل SD card في Raspberry Pi وأوصل الكهرباء, وسلك Ethernet يفترض أن ترى مثل هذه الشاشة 
     
     
    لاحظ وجود IP في أسفل الصفحة فهو الذي سنستخدمه في التواصل مع Raspberry Pi وإيصاله بالشبكة.
    الان نفتح  Android studio وعن طريق terminal الموجود فيه نقوم بكتابة الأمر التالي 
    adb connect <ip-address> ip address هو الرقم المكتوب لديك في أسفل الشاشة بعد Ethernet IP, لو تم الاتصال بشكل صحيح سيظهر هذا السطر 
    connected to <ip-address>:5555 , بهذه الطريقة نكون متصلين بالـ Raspberry Pi لكن, نريد أن نجعل الأتصال عن طريق wifi 
     فنقوم بكتابة الأمر التالي 
    adb shell am startservice \ -n com.google.wifisetup/.WifiSetupService \ -a WifiSetupService.Connect \ -e ssid <Network_SSID> \ -e passphrase <Network_Passcode> ssid أسم الشبكة المراد الاتصال بها 
    passcode الرقم السري الخاص بالشبكة 
    انتهينا من فقرة التثبيت إن صح التعبير وقد يتخللها الكثير من الإشكالات وdebug وأرحب بأي استفسار لتجاوز هذه المهمة, الان لننتقل إلى برمجة الـ board.
     أولا علينا أن نقوم بتوصيل كما في الصورة 
     
     

     
    وهنا شرح لـ pins الخاصة بـ Rpi
    الكود كالتالي 
    public class MainActivity extends Activity { private Gpio mLedGpio; private boolean mLedState = false; Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = (Button) findViewById(R.id.button); PeripheralManagerService service = new PeripheralManagerService(); //system service responsible for managing peripheral connections try { String pinName = "BCM4";//pin number mLedGpio = service.openGpio(pinName); mLedGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);//Output pin ,start with 0v } catch (IOException e) { e.printStackTrace(); } mButton.setOnClickListener(v -> { try { if (mLedState) { mLedGpio.setDirection(Gpio.ACTIVE_HIGH);//5v mLedState = false; }else { mLedGpio.setDirection(Gpio.ACTIVE_LOW);//0v mLedState = true; } } catch (IOException e) { e.printStackTrace(); } }); } }  أما الـ UI فيحتوي button واحد id الخاص فيه هو button 

    بداية لدينا 
    PeripheralManagerService service = new PeripheralManagerService();  PeripheralManagerService هي Service من من نظام الأندرويد تسهل لك التعامل مع pin الخاصة بـ Rpi
    بعد ذلك قمنا بختيار pin عن طريق اسمه 
    String pinName = "BCM4"; mLedGpio = service.openGpio(pinName); mLedGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW); وربطه بمتغير من نوع Gpio, ثم قمنا بضبط إعداده بأن يكون output ويبدأ بـ 0 فولت ويوجد العديد من Constant التي تساعد في هذه الاعدادت
    mButton.setOnClickListener(v -> { if (mLedState) { mLedGpio.setDirection(Gpio.ACTIVE_HIGH); mLedState = false; }else { mLedGpio.setDirection(Gpio.ACTIVE_LOW); mLedState = true; } هنا قلنا في حال تم الضغط على button تأكد من حالته, في حال كان  on اجعله off والعكس, ونلاحظ أننا قمنا بأستخدام المزيد من Constant  التي تحدثنا عنها في السطر السابق.
    ونلاحظ أيضا أننا كتبنا الدالة الخاصة بنا بطريقة lambda.
     
    لمعلومات أكثر عن أساسيات hardware
    واللاستزادة :
    https://developer.android.com/things/sdk/index.html
    مستوى المقال: مبتدئ

    بواسطه 3zcs , في

  • كثير من مطوري الاندرويد يقومون باستخدام AsyncTask وخاصة عندما يريدون تنفيذ بعض الاكواد في thread منفصل, ولكن كثير منهم يتعامل مع هذا الكلاس بطريقة خاطئة ودون فهم لدورة حياة الاندرويد وما الذي يحدث بالضبط في الخلفية, في هذه المقالة سنوضح الخطأ الشائع وكيف تقوم بمعالجة المشكلة.
    الاستخدام الخاطئ
    الطريقة الشائعة والتي تجدها في معظم المواقع والتي ستجدها بالتاكيد في معظم مشاريع مطوري الاندرويد هي كالتالي:
    public class MainActivity extends Activity {     @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);   }     // Somewhere the AsyncTask is started     public class MyAsyncTask extends AsyncTask<Void, Void, String> {       @Override protected String doInBackground(Void... params) {       // Do work       return result;     }       @Override protected void onPostExecute(String result) {       Log.d("MyAsyncTask", "Received result: " + result);     }   } } المشكلة بالكود هي ان كلاس  AsyncTask مضمن بداخل الـ Activity وبالتالي اي تغيير في حالة الـ Activity والتي قامت باستدعاء الـ AsyncTask الموجود بداخلها قد يقتل عمل الـ AsyncTask, ولكن لن يتم ازالة الـ Activity من الذاكرة الا في حالة انتهاء الـ AsyncTask.
    الان لتتخيل معي المثال التالي: لدي اكتفتي واحدة وبداخلها AsyncTask وفي كل مرة اقوم باستدعاء الاكتفتي فانا استدعي AsyncTask جديد واي تغيير في حالة الاكتفتي ربما استدعي AsyncTask اخر, وكل واحد من الـ AsyncTask لديه عملية يجب عليه ان ينفذها قبل ان يُقتل وقبل ان يسمح للـ Activity بان تزال من الذاكرة.
    ماسيحدث بالمثال بالاعلى هو مشكلتين, الاولى هي مشكلة في الذاكرة memory issues والثانية هي انك ستفقد العمليات التي قام الـ AsyncTask بتنفيذها وستقوم بتكرار العملية اكثر من مرة.
    معالجة المشكلة
    لمعالجة المشكلة بالاعلى فالافضل ان تقوم باستخدام مبدأ الـ event bus وطريقة تنفيذه كالتالي:
    نقل الـ AsyncTask الى كلاس منفصل عن الـ  Activity. انشاء كلاس جديد يقوم بتخزين ناتج  AsyncTask عند اكتماله. ربط الـ الـAsyncTask بالـ Activity الخاصة بك عن طريق الـ bus. لذلك سنقوم بانشاء كلاس جديد تحت مبدأ الـ Singleton ويكون كالتالي:
     public class MyBus { private static final Bus BUS = new Bus();  public static Bus getInstance() { return BUS;    } } ولنفترض ان الـ AsyncTask سيقوم بارجاع String فقط عن اكتمال عمله, لذلك سنقوم بانشاء  كلاس جديد وليكن اسمه AsyncTaskResultEvent :
    public class AsyncTaskResultEvent { private String result; public AsyncTaskResultEvent(String result) { this.result = result; } public String getResult() { return result; } } نقوم بانشاء كلاس الـ AsyncTask والذي سيقوم بتنفيذ اي كود تريده:
    public class MyAsyncTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... params) { Random random = newRandom(); final long sleep = random.nextInt(10); try{ Thread.sleep(sleep * 1000); } catch(InterruptedException e) { e.printStackTrace(); } return"Slept for "+ sleep + " seconds"; } @Override protected void onPostExecute(String result) { MyBus.getInstance().post(newAsyncTaskResultEvent(result)); } } اخيرا سنقوم بربط الـ AsyncTask  مع الـ Activity ولكن يجب عليك بالغاء الربط بينهم في ميثود onDestroy حتى لانقع في نفس المشكلة من جديد:
    public class MainActivity extends Activity {   @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     findViewById(R.id.button).setOnClickListener(newView.OnClickListener() {       @OverridepublicvoidonClick(View v) {         newMyAsyncTask().execute();       }     });     MyBus.getInstance().register(this);   }   @Override protected void onDestroy() {     MyBus.getInstance().unregister(this);     super.onDestroy();   }   @Subscribe public void onAsyncTaskResult(AsyncTaskResultEvent event) {     Toast.makeText(this, event.getResult(), Toast.LENGTH_LONG).show();   } } اخيراً
    هذه الطريقة ليست محصورة مع الـ AsyncTask بل تستطيع استخدامها مع اي امر اخر, فعلى سبيل المثال الـ services لديه نفس المشكلة اذا استخدمتها بطريقة خاطئة وحلها هو باستخدام الـ event bus.
    مستوى المقال: متوسط

    بواسطه Alhazmy13 , في



  • البعض اخبرني بأنه يرغب بأمثله في الـ Animation وليس فقط شرح الأساسيات
     
    لذلك قررت اشرح مثال لعمل Menu مشابه للمدرج في تطبيق Telegram
     
     
    الفكرة باختصار هي إضافة زر بدلاَ من النص في الـ Navigation bar
    ومن ثم تغير موقع الـ View عند الضغط على الزر


    اذا لنبدأ في الشرح
     
    أولا نقوم بإضافة الـ Navigation Controller


    عن الطريق تطبيق اتباع الصورة التالية :
     
     


     

    ومن ثم نضيف View اسفل الـ Navigation bar


    بحجم عرض 375 وارتفاع 50

    طبعا يعتمد على الابعاد الي انت تبغاها انا استخدمت هذه الابعاد

    نضيف القيود التالية :

     

     

    ومن ثم نضيف 3  ازرار

    احجام الايقونات كانت متفاوتة  ، لكن اعتمدت على حجم 60 في 60
    او اصغر بقليل بالنسبة لأحجام ايقونات الـ x2
    الخاصة بحجم iPhone 7 و iPhone SE

    الان سوف نحتاج نستخدم الـ Stack View
    لأنه سوف يسهل علينا جعل الايقونات بمسافة متساوية !
     
    قم بتحديد جميع الايقونات في أنٍ واحد

    ومن ثم ثم باتباع الصورة التالية :
     
     

     



    سوف تلاحظ تلاصق الايقونات مع بعض

    قوم بالذهاب الى خصائص الـ Stack View

    واجعل المسافة Spacing بـ 60


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

     

     


    واجعل الايقونات في منتصف الـ View

    الان قم بإضافة قيود الى الـ Stack View:
     



    لاحظ باني اضفت القيود بشكل يدوي
    لجعله الـ Stack View
    ياخد حجم الـ View الذي خلفه

    عند التعامل مع الـ Stack View يتوجب عليك استخدام القيود لتغير ابعاده

    لاحظ الفرق

    قبل القيود كان بالشكل التالي :
     
     



    بعد إضافة القيود اصبح بالشكل التالي :
     
     

     


    الان سوف تلاحظ بان الازرار والـ View متناسبة مع جميع احجام الاجهزة

    أيضا لاحظ بأنه عند استخدام الـ Stack View

    تكون لست بحاجة الى وضع قيود لكل زر على حدى !

    كل ما عليك هو وضع القيود على نفس الـ Stack View فقط

    هذه هي قوة الـ Stack View

    سوف تحتاج الى استخدامه بكثره وخاصة عند عمل الـ Animation
     
    الان نعود للدرس
     
    قبل ان نبدأ تأكد بأن الـ Stack View
    بداخل الـ View الذي اضفناها لأننا سوف نضيف الـ View فقط الى ملف الاكواد !
     
    يمكن التأكد بمشاهده الترتيب

    الـ Stack يكون بداخل الـ View الذي اضفناه سابقا

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

     


    الان نقوم بإضافة الـ View الى ملف الاكواد
    ونجعله باسم Menu View

    وأيضا نضيف قيد الـ top  الخاص بـ View

    ونجعله اسم menuTop



    يصبح ملف الاكواد بالشكل التالي

     
    import UIKit class ViewController: UIViewController {     @IBOutlet weak var menuTop: NSLayoutConstraint!          @IBOutlet weak var menuView: UIView!          override func viewDidLoad() {         super.viewDidLoad()                               }


    الان نقوم بإضافة الاكواد التالية في الـ ViewDidLoad


     
     override func viewDidLoad() {         super.viewDidLoad()                  menuTop.constant = -65                  let button =  UIButton(type: .custom)         button.frame = CGRect(x: 0, y: 0, width: 200, height: 40)         button.setTitle("Menu", for: .normal)         button.addTarget(self, action: #selector(self.menuButton), for: .touchUpInside)         self.navigationItem.titleView = button                  menuView.layer.borderWidth = 0.5         menuView.layer.borderColor = UIColor.gray.cgColor                       }
    في البداية جعلنا الـ View مخفي بجعل القيمة -65 وبالتالي يصبح الـ View
    في الأعلى خلف الـ Navigation bar

    ومن ثم انشأنا زر من نوع .custom
     
    اعطينا ابعاد له وهيا
    عرض 200 وارتفاع 40
     
    العرض تستطيع تغيره
    انا جعلته 200 لأنه في حال وجود زر في اليمين واليسار لن يغطي عليه

    اما الارتفاع فهو 40
    ارتفاع الـ Navigation bar
    بدون شريط الساعة
    هو 44 لذلك حجم 40 يعتبر مناسب


    ومن ثم اضفنا عنوان للزر باسم Menu

    وبعدها اضفنا Target
    الي هو جعل الزر يستجيب للضغط
    طبعا اضفنا اسم للـ Function وهو menuButton

    سوف نضيفه لاحقا

    اخيراً قمنا بإضافة الزر بداخل العنوان الـ Navigation bar

    في الأسفل اضفنا الحدود حولينا الـ view
    وغيرنا لون الحدود الى اللون الرصاصي
     
    الان نضيف Function الزر

    باضافة التالي :

     
        func menuButton(button: UIButton) { }

    الان سوف نضيف الـ Aniamtion

    الفكره باختصار

    عند الضغط على الزر لأول مره نغير القيود الى 0
    ليظهر الـ View

    لماذا 0 ؟

    لانه عند اضافة الـ Navigation bar
    تصبح المسافه اسفله بقيمة 0
    بالنسبة الى القيود
    فين حين بدون الـ Navigation bar
    تكون المسافة اسفل شريط الساعه هي الـ 0
     
    وعند الضغط على الزر للمره الثانية نغير القيود الى -65
    وبالتالي يختفي الـ View

    كيف نعلم بأمر الـ Menu هو ظاهر او مختفي ؟
    الطريقة هي باستخدام متغير من نوع bool

    يكون true وتعني بانه مخفي
    ويصبح false
    اذا كان غير مخفي

    لذلك نضيف متغير في خارج الـ Function وأيضا بخارج الـ ViewDidLoad

    باسم
        var check = true

    ومن ثم نضيف التالي في الـ menuButton
     
        func menuButton(button: UIButton) {               if check == true {                  UIView.animate(withDuration: 0.5, animations: {             self.menuTop.constant = 0             self.view.layer.layoutIfNeeded()             self.check = false                      })                  }else {                        UIView.animate(withDuration: 0.5, animations: {                 self.menuTop.constant = -65                 self.view.layer.layoutIfNeeded()                                 self.check = true                                               })                                   }            }

     
    الذي قمنا به هو عمل if
    وقبنا بالتشيك اذا كان القيمة true فهو مخفي لذلك سوف نظهره
    واذا كان غير مخفي سوف نخفيه

    صورة بالنتيجة النهائية :

     

     



    اذا كنت تتسأل كيف غيرت لون الـ Navigation Bar

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




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

    بواسطه X901 , في


  • تحدثنا سابقا عن أنواع مختلفة للـ Animation
     
    وفي هذا الموضوع سوف نتحدث أيضا عن نوع اخر
     
    النوع هذا مختلف عن الانواع الأخرى فهو يتركز على موضوع التسلسل الزمني
     
    ذكرت في موضوع الاول من هذه السلسلة عن كيفية عمل Animation للمربع بحيث يتحرك بشكل حرف L

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

     
    UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:0, y: 300) }) { (true) in UIView.animate(withDuration: 1.0, animations: { self.squareView.transform = CGAffineTransform(translationX:-122, y: 300) }) }  
    الاول : هو كثرة الاقواس وتداخلها ، في حال كان التحرك اكثر تعقيداً سوف يزداد تعقيد الكود وتكثر الاقواس وبالتالي يصعب تعديله


    الثاني : اذا اردت جعل الحركة متسلسلة بنفس التوقيت سوف ينبغي عليك تعديل توقيت كل Animation على حدى



    هذه هي فوائد النوع الجديد 
    ازلت الاقواس المتداخله سهولة تعديل الكود او الوقت الكلي للـ Animation  
    قبل أن نبدأ في الشرح العملي ، يتوجب عليا شرحه بصورة نظرية
     
    كيف يعمل ؟

    عند استخدام UIView.animate
    فنحن نقوم بإضافة وقت زمني واذا استخدمنا Function معين سوف نستطيع اضافة أيضا زمن تأخير
    الـ Animation سوف يبدا وينتهي في الزمن المعين بصورة ثابته

    واذا اردنا ان نحرك في اتجاه مختلف فإننا نقوم بإضافة UIView.animate
    أخر بداخل اقواس الاستكمال التي نضعها أحيانا بقيمة true
    بما يعني في كل مره سوف نضع زمن مختلف ونعيد كتابة UIView.animate
     
    في هذا النوع الذي سوف نقوم به هو التالي :
     
    سوف نعطي زمن كلي محدد ولنقول مدة ثانية واحده
    ومن ثم ننفذ الـ Aniamtion بالنسبة المئوية !


    ما المقصود بالنسبة المئوية؟
     
    افرض بأن مدة الثانية الواحدة كامله منذ بدايتها الى نهاتيها تكون بنسبة 100%

    اذا سوف نضع Animation يبدا مباشرة وتكون بنسبة 0% الى نسبة 50%
    يتم التحريك بصورة عامودية ومن النسبة 50% الى 100%
    يتم التحريك بصورة افقيه
     
    صورة توضيحية :
     

     
    هذا هو فكرة النوع هذا باختصار
     
    وقت واحد ، يتم تجزئيه بالنسبة وعمل تحريك مختلف
    لذا ذكرت في بداية الموضوع بأن هذا النوع يعتمد على التسلسل الزمني
    فهو يعمل كالخط الزمني !
     
    أخيرا يتوجب عليك ان تعلم بأن :

    النسبة لن تكون 100%
    ولكن سوف تكون 1 ، بمعنى نقوم بالقسمة على 100
    اذا اردنا نسبة 25% سوف تكون 0.25
    اذا اردنا نسبة 50 سوف تكون 0.5
    وهكذا
     
    اذا استوعبت وفهمت الكلام السابق ، تكون فهمت فكرة هذا النوع !
     
    اسم النوع الجديد هو UIView.animateKeyframes
     
    الان لجعلك تلاحظ الفرق الجوهري بين استخدام UIView.animate

    وبين استخدام UIView.animateKeyframes
     
    سوف اعطيك نفس المثال مرتين
     
    الاولى بالطريقة التقليدية باستخدام UIView.animate
     
    الثانية باستخدام UIView.animateKeyframes
     
    في هذا الدرس سوف نعود الى استخدام المربع الأزرق التي استخدمناه في الجزء الاول والثاني من هذه السلسلة
     
    ما الذي سوف نقوم به ؟

    سوف نقوم بتحريك المربع الأزرق بشكل مستطيل
    يبدا من الزاوية السفلية اليسرى
    ويتحرك الى الزاوية السفلية اليمنى ومن ثم يتحك الى الزاوية العلوية اليمنى
    ومن ثم يتحرك الى الزاوية العلوية اليسرى
    واخيراً يعود الى الزاوية السفلية اليسرى
     
    قبل اذهب الى الـ Storyboard واجعله بالشكل التالي :
     



    ومن ثم اضيفهم الى ملف الاكواد
     
    الان سوف نبدأ الدرس
     
    باستخدام الطريقة القديمة

    الكود سوف يصبح بالشكل التالي :

     
     @IBAction func Animation(_ sender: Any) { UIView.animate(withDuration: 0.75, animations: { self.square.transform = CGAffineTransform(translationX: 225, y: 0)         }) { (true) in             UIView.animate(withDuration: 0.75, animations: {                 self.square.transform = CGAffineTransform(translationX: 225, y: -500)                         }, completion: { (true) in                          UIView.animate(withDuration: 0.75, animations: {                 self.square.transform = CGAffineTransform(translationX: -0, y: -500)                }, completion: { (true) in                       UIView.animate(withDuration: 0.75, animations: {                 self.square.transform = .identity                }, completion: { (true) in                })                })             })         }     }  
    صورة توضيحية بالنتيجة :
     

     
    هل لاحظت المشكلة في الكود ؟

    كثرة الاقواس وتداخلها يصعب من عملية تعديل الكود
    ماذا اذا اردت حذف احدى الـ Animation ؟

    ستواجه صعوبة في إيجاد كود الـ Animation
    المراد حذفه
     
    ماذا اذا اردت تعديل الوقت الكلي ؟
    سوف يتوجب عليك تعديل كود كل Animation على حده
    في المثال السابق استخدمنا وقت 0.75
     
    لماذا  ؟
    لنصل الى توقيت كلي 3.0
    فكل Animation سوف يستغرق 0.75 ثانية فقط
     
    الان للتنقل الى النوع الجديد

     
        @IBAction func Animation(_ sender: Any) {                  UIView.animateKeyframes(withDuration: 3.0, delay: 0.0, options: [], animations: {                          UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25, animations: {                 self.square.transform = CGAffineTransform(translationX: 225, y: 0)             })                          UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.5, animations: {                 self.square.transform = CGAffineTransform(translationX: 225, y: -500)             })                          UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.75, animations: {                 self.square.transform = CGAffineTransform(translationX: -0, y: -500)             })                          UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 1.0, animations: {                self.square.transform = .identity                          })                                   }, completion: nil)                                }  
     
    النتيجة الذي وصلنا اليها :
     

     
     
    ما الجديد ؟

    أولا اضفنا animateKeyframes
    ومن ثم اضفنا التوقيت الكلي وهو 3 ثواني
    ولم نضيف تأخير لأننا نريد ان يبدا فوراُ فجعلنا القيمة 0.0
    في الـ options لم نضيف شيء لذا وضعنا اقواس اريه فاضية
    مع العلم أنواع الـ options مختلفة تماما عن الانواع السابقة الموجودة في UIView.animate !
    (لن نتطرق لها في هذا الموضوع)


    والان لاحظ كل Animation ينضاف لحاله بداخل اقواس animateKeyframes
     
    ويطلق عليه UIView.addKeyframe
     
    يتكون من امرين وهي withRelativeStartTime
    والتي تعبر بداية الـ Animation
    كما ذكرنا في بداية الموضوع تعتبر نسبة
    في اول Animation وضعنا قيمة 0.0
    ليبدا في البداية
     
    بالنسبة الى relativeDuration
    يعتبر وقت انتهاء الـ Animation
    في اول Animation وضعنا نسبة 0.25
    والتي تساوي نسبة 25%

    لجعل الامر اكثر وضوحاً

    اذا نظرنا في animateKeyframes
    وضعنا وقت كلي هو 3 ثواني
     
    فاذا قمنا بعملية حسابية بسيطة
     
    3 ضرب 25 قسمة 100 النتيجة سوف تكون 0.75 ثانية
     
    اذاً اول Animation سوف يستغرق 0.75 ثانية فقط
     
    والامر ينطبق مع البقية
     
    في الـ Animation الثاني وضعنا قيمة withRelativeStartTime بـ 0.25
    وقيمة الـ relativeDuration بـ 0.50
     
    اذا نقصنا القيمتين من بعض سوف تكون النسبة 0.25
    وذا اتبعنا نفس الحسبة السابقة سوف تكون النتيجة 0.75 ثانية
     
    من الشرح السابق اتضح لنا فائدة الـ UIView.animateKeyframes
     
    بما يعني :
    اذا اردنا تغير الوقت الكلي فقط نقوم بتغير القيمة الموجودة في UIView.animateKeyframes اذا اردنا تغير مدة Animation معين ، نقوم بتغير نسبة الـ Animation فقط  
    والاهم من ذا كله الكود اصبح نضيف وسهل القراءة والتعديل !
     
     

    مستوى المقال: محترف

    بواسطه X901 , في

  • كيف تحصل على HTTPS: إعداد  (SSL Certificate) في خطوات
     
    شهادة( SSL ( Secure Sockets Layer هي ملف نصي يحتوي على بيانات مشفرة تقوم بتثبيتها على الخادم بحيث يمكنك تأمين / تشفير الاتصالات الحساسة بين موقعك وعملائك.
    هنا الخطوات اللازمة لإعداد SSL Certificate لموقعك الالكتروني دون الحاجة لشراءها من الخادم 
     
    الخطوة الأوليّة هي الاتصال ب SSH الخاصة بك عن طريق السيرفر
    بسم الله نبدأ:
    الخطوة الأولى:
    --------------------------------------------------------
    لإعطاء الأوامر كمشرف Super User
    Sudo su الخطوة الثانية:
    --------------------------------------------------------
    في الخطوة الثانية نقوم بتحديث  مجموعة ال APT قبل اعطاء امر اضافة الSSL Certificate كالتالي:
    Apt-get update Apt-get upgrade الخطوة الثالثة:
    --------------------------------------------------------
    نقوم بعمل ريستارت ل Apatche 
    sudo service apache2 restart  
    الخطوة الرابعة:
    --------------------------------------------------------
    الموقع التالي مصدر رائع يمدك بالأكواد اللازمه والتي قمت بتتبعها في الخطوات القادمة
    https://certbot.eff.org/#debianjessie-apache
    ٤- أ) أولا سيكون عليك إتباع التعليمات هنا لتمكين the Jessie backports repo (هذا الملف يسمح بتحميل ssl) ، إذا لم تكن قد فعلت ذلك. قم بما يلي:
    sudo apt-get install python-certbot-apache -t jessie-backports ٤- ب) The modal window does not pop up. Everything happens in the SSH window. See the attached picture
    certbot --apache


     
    --------------------------------------------------------
    الخطوة الخامسة:
    --------------------------------------------------------
    فتح الملف defult-ssl.conf لنقوم بإضافة نص كما في الخطوة السادسة
    sudo nano /etc/apache2/sites-enabled/default-ssl.conf الخطوة السادسة:
    --------------------------------------------------------
    اضف النص التالي في المنتصف بين (ServerAdmin…) و (DocumentRoot…) ثم قم بحفظ الملف
    Add the text below in between top line (ServerAdmin…) and bottom line (DocumentRoot…) in default-ssl-.conf
    Then exit and save file
     
    ServerAdmin webmaster@localhost
    <Directory /var/www/html/> Options Indexes FollowSymLinks MultiViews AllowOverride All Order allow,deny allow from all </Directory>  DocumentRoot /var/www/html
     
    ثم لحفظ الملف ( ctrl+x)
     
    الخطوة التاسعه:
    --------------------------------------------------------
     نعمل اعادة تشغيل ل Apatche 
     
    sudo service apache2 restart  
     
    --------------------------------------------------------
    قمت برفع الخطوات على ملف PDF لمن يرغب بحفظها
    SSLInstallationReview.pdf
    تحياتي
    لمياء الشمري
     
    مستوى المقال: مبتدئ

    بواسطه لمياء الشمري , في

  • Ads Belongs To This website

    عالم البرمجة

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