1. بسم الله الرحمن الرحيم 
    في هذا الدرس سأقوم بشرح كيفية عمل الخرائط بإستخدام MapKit.
     
    أولا : إفتح xcode و قم بإختيار Single View Application .

    وأدخل المعلومات كما في الصوره التالية :

    ثانيا : إذهب الى storyboard . من صندوق الأدوات إختر MapKit View و قم بسحبها الى main View .

    الأن نضع القياسات لـ MapKit View بحيث تكون بنفس حجم الـmain View كالتالي:
    إضغط على زر Pin في أسفل يمين الـ storyboard ثم إضغط الخطوط الحمراء المتقطعة لتصبح متصلة كما في الصورة التالية :

     
    ثالثا: أنشئ IBOutlet من MapKit View كالتالي : 
    أختر Assistant Editor وتأكد أن ملف ViewController.swift ظاهر أمامك . حدد MapKit View و قم بالضغط على زر الفأره الأيمن + زر Ctrl ثم قم بالسحب إلى الكود البرمجي داخل ViewController class .. قم بتسمية ال IBOutlet بالإسم التالي  mapview كما في الصورة التالية : 
     

     
    رابعا : إذهب إلى ملف ViewController.swift وقم بتضمين مكتبة MapKit في بداية الكود البرمجي .
    import MapKit  
    خامسا : في دالة ViewDidLoad أضف الكود البرمجي التالي : 
    override func viewDidLoad() {         super.viewDidLoad()          // 1         mapview.mapType = MKMapType.standard                 // 2                let location = CLLocationCoordinate2D(latitude: 51.50007773, longitude: -0.1246402)          // 3                let span = MKCoordinateSpanMake(0.05, 0.05)         let region = MKCoordinateRegion(center: location, span: span)         mapview.setRegion(region, animated: true)         4//         let annotation = MKPointAnnotation()         annotation.coordinate = location         annotation.title = "Big Ben"         annotation.subtitle = "London"         mapview.addAnnotation(annotation)     }  
     
    ١- نختار نوع الخريطة بإستخدام خاصية MKMapType , وإخترنا نوعها Standard. 
    ٢- أنشأنا ثابت إسمه location وأسندنا إليه دالة تسمى CLLocationCoordinate2d و وضعنا فيها خطوط الطول والعرض لمدينة لندن .
    ٣- قيمة span تمثل خطوط الطول والعرض لجزء الخريطة المراد عرضه (أي اتساع الخريطة من الشمال إلى الجنوب و من الشرق إلى الغرب ), كلما كانت قيمته صغيره كان الجزء المعروض من الخريطة كبير ,أيضا يحدد مستوى التكبير الحالي في الخريطة .
    - دالة الـ MKCoordinateRegion تحدد المنطقة التي حددنا إحداثياتها في الخطوة الثانية .
    ٤- أنشأنا كائن ثابت من كلاس MKPointAnnotation و أسميناه annotation ( annotation هي العلامة الحمراء التي تظهر عادة في خرائط أوبر) , 
    -أسندنا إلى الإحداثيات coordinate قيم الثابت location الذي أنشأناه في الخطوة الثانية و وضغنا في الـ Title إسم المكان و الـ SubTitle إسم العاصمة. 
     
     
     الأن قم بتشغيل المشروع : 

    مستوى المقال: مبتدئ
  2. بسم الله الرحمن الرحيم
     
    تكلمنا في المقالة السابقة (القواميس Dictionaries في السويفت - الجزء الأول) عن عدّة نقاط وهي :
    ماهي القواميس Dictionaries ؟ كيفية تعريف وإنشاء القواميس Dictionaries :     1    إنشاء قواميس فارغة .
        2    إنشاء قواميس بقيم إبتدائية .
       إضافة عناصر جديدة للقواميس .   إزالة قيم القواميس .   وفي هذه المقالة سنكمل ماتعلمناه عن القواميس وسنتحدث عن :
     
    التعديل على قيم القواميس . خاصية count في القواميس. استخدام التكرار مع القواميس .  
    التعديل علي قيم القواميس : ذكرنا سابقاً أننا عندما نريد التعامل مع قيم القواميس والوصول لها فإن ذلك سيكون عن طريق المفتاح الخاص بالقيمة، وبناءاً على ذلك نستطيع الوصول والتعديل على قيمة معينة من خلال المفتاح الخاص بها .
     
    * ملاحظة : حتى تتمكن من التعديل على قيم القواميس من المهم أن تكون من نوع var (متغير) وليس let (ثابت) .
     
    وللتعديل علي أي قيمة سيكون ذلك كالتالي  :

     
    مثال (١) :
    var  Jobs : [Int : String] = [1:"Programmer" , 2:"Designer" , 3:"Writer"] Jobs [3] = "Secretary"  
    في هذا المثال تم تغيير قيمة المفتاح (3) في المتغير Jobs من (Writer) إلى (Secretary) .
     
    أيضاً للتعديل على القيم نستطيع استخدام الدالة updateValue (ForKey:) وسنستخدمها كالتالي :

     
    سنقوم بتغيير قيمة المفتاح (3) في المتغير Jobs في المثال السابق من (Writer) إلى (Secretary) ولكن باستخدام الدالة :
    Jobs.updateValue("Secretary", forKey: 3)  
    خاصية Count في القواميس :  
    في القواميس نستطيع استخدام الخاصية count لمعرفة عدد العناصر الموجودة .
     
    مثال (١) :
    var  Jobs : [Int : String] = [1:"Programmer" , 2:"Designer" , 3:"Writer"] var  NewJobs : [Int : String] = [1:"Doctor" , 2:"Engineer"] print(Jobs.count) print(NewJobs.count) 
    في هذا المثال لدينا متغيرين من نوع Dictionary وهم Jobs و NewJobs الأول يحتوي على (٣عناصر)  ،بينما المتغير الثاني يحتوي على (عنصرين فقط) .
    بعد ذلك قمنا باستخدام الخاصية count مع كلا المتغيرين لطباعة عدد العناصر الموجودة في كل متغير وعلى ذلك سيتم طباعة رقم 3 ومن ثم رقم 2 والتي تدل كما قلنا على عدد عناصر كل متغير .
     
    تستطيع الآن تجربة ذلك في ال xCode لترى النتيجة  
     
     
    استخدام التكرار مع القواميس :  
    لابد وأنك لاحظت في الأمثلة السابقة جميع المخرجات تكون للقيم بدون المفتاح ولقيمة واحدة فقط ، فلنفترض الآن بأنك أردت طباعة كل المفاتيح الموجودة مع قيمهم ، ولعمل ذلك فإننا سنستخدم for-In loop (يُفترض بأنك قد تعرفت على for-In loop كيفية كتابتها وفكرتها قبل التعرف على القواميس ) .

    مثال (١) :
     
    var  Jobs : [Int : String] = [1:"Programmer" , 2:"Designer " , 3:"Writer"] for (x , y)  in Jobs {          print ( " Key =   \(Key)  and the value is =  \(Value) ") }  
    بالمثال السابق أردنا طباعة جميع المفاتيح مع قيمها للقاموس Jobs ،واستخدمنا الـ for - In loop لطباعتها .
    في for- in loop سيصبح للمفاتيح ثابت وأسميناه x وللقيم أيضاً ثابت واسميناه y 
    والمخرجات ستكون كالتالي :

     
    بإمكانك أيضاً باستخدام الـ for-In loop مع القواميس أن تقوم بطباعة جميع المفاتيح فقط أو جميع القيم فقط كالتالي :
     
    for x in Jobs.keys {     print ( " Key =   \(x)  ") } استخدمنا الخاصية keys بعد اسم القاموس لنتمكن من طباعة جميع المفاتيح في القاموس Jobs.
    والمخرجات ستكون كالتالي :

     
    * لاحظ بأن المخرجات ليست بالترتيب لأن كما ذكرنا سابقاً (في مقالة القواميس Dictionaries في السويفت - الجزء الأول) بأن القواميس تعتبر غير مرتبة unorderd .

    ولطباعة القيم فقط سنستخدم الخاصية values :
    for x in Jobs.values {          print ( " Key =   \(x)  ") }  والمخرجات كالتالي :

     
     
    وبذلك نكون قد انتهينا بحمد الله من موضوع القواميس Dictionaries بجزئيه الأول والثاني ،أتمنى أكون قد وفقت في الشرح ولا تتردد في سؤالي عن أي نقطة قد تصعب عليك
     
    وصلى الله وسلم على نبينا محمد وعلى آله وصحبه أجمعين ..
    مستوى المقال: مبتدئ
  3. بسم الله الرحمن الرحيم
     
    مقدمة
    استعرضنا في الجزء الأول طريقة ارسال البيانات بين ال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، يمكنك أنت أن تقوم بالتعامل مع البيانات كما تشاء.
     
    وصلنا الى نهاية موضوعنا اليوم، أترككم في حفظ الله ورعايته.
    مستوى المقال: متوسط
  4.  
    مرة فترة طويلة على تدوينات المتعلقة بالـ Animation !
    اليوم اقدم لكم مفهوم جديد في تجربة المستخدم وواجهة المستخدم وكيفية عمله =)

    شرح التدوينة في سطر واحد سوف يكون The Power of StackView !
    كيف الاستفادة من الـ StackView في عمل Animation
    في اقل عدد من الاسطر وبنتيجة رائعة =)
     
    لأول مره سوف اعمل الـ Animation مع الـ TablwView
    في هذا الـ Animation سوف نستفيد من ميزة السحب للأعلى وللأسفل
    الموجوده في الـ TableView  ، (أيضا يمكنك عملها مع الـ ScrollingView)
     
    النتيجة النهائية سوف تكون بالشكل التالي :
     

     
    ماذا سوف تحتاج ؟
    أولا : سوف نحتاج الى إضافة UIView
    في الجزء العلوي
     
    (سبب إضافة هذا الـ UIView
    لان الـ StackView لا تستطيع إضافة لون خلفية له
    واسهل حل يكون إضافة UIView خلفه واضافة اللون الى UIView)
     
    ثانيا : نقوم بإضافة الاوبجكت بنفس ترتيب الموجود في هذه الصورة
    ثالثا : نقوم بتحديد كل من الصورة + النص واضافاتهم معا كـ StackView
    سوف نطلق عليه (StackView)
    رابعا : نحدد الـ StackView السابق + الزر وندمجهم كـ StackView اخر
    سوف نطلق عليه (BigStackView)
     
    هيكل الواجهة سوف يكون بهذا الشكل :

     
    لاحظ بأني جعلت الـ View الذي باسم Color
    غير مرتبط بأي View اخر
     
    السبب الفعلي هو لاني اضفته بعد ما انتهيت من تنفيذ المشروع !
     
    وأيضا سابقا واجهة مشكله عند جعل الـ StackView بداخل الـ UIView
    الازار لم استطع الضغط عليها ، لذا يفضل جعله بالشكل السابق
     
    شكل الـ Storyboard سوف يكونب الشكل التالي :
     

     
    الان سوف نضيف القيود بالشكل التالي :
     
    BigStackView:

     
    profileImage: (الصورة)

    NameLable: (النص)

    FollowImage: (الزر)

     (الفيو سوف نضيف قيود وأيضا لا تنسى إعطائه لوناً)UIView:

    TableView: (الجدول)

     
    لا نحتاج الى إضافة قيود الى الـ StackView الصغير (الذي يحتوي على الصورة والنص)
     
    بداية سوف تقوم بإضافة بيانات الجدول كما تريدها
     
    ومن ثم سوف تقوم بإضافة كل من StackView
    و bigStackView الى ملف الاكواد
     
    وأيضا سوف نقوم بإضافة قيود الـ profileImage التي وضعناها سابقا
    Height و Width
     
    اذا لا تعلم كيف تضيفها فشاهد هذه الصورة لكيفية إضافة قيد الـ Height :

    وطبق نفس الامر لإضافة قد الـ Width
     
    أيضا سوف نضيف قيد الـ Height الخاص بـ bigStackView الى ملف الاكواد لكي نستطيع تصغير حجمه عند السحب الى اعلى وأيضا ارجاعه الى حجمه الساسي عند السخب الى أسفل
     
    ومن ثم سوف نضيف الاكواد التالية :
     
    نضيف المتغير في بداية ملف الاكواد

     
        var lastContentOffset: CGFloat = 0 وضيفة هذا المتغير تحديد ما اذا كان المستخدم يسحب الجدول للاعلى او الأسفل (عندما يكون بقيمة 0 هذا يعني بانه لم تتم أي عملية سحب سواء للاعلى او الأسفل )
     
    ومن ثم نضيف الـ Function التالي :

     
      func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {         self.lastContentOffset = scrollView.contentOffset.y     }  
    هذا الـ Function
    وظيفته ما اذا تمت عملية سحب سوا للأعلى او للأسفل ويتم تخزين القينة في متغير lastContentOffset
     
    ومن ثم نستخدم هذا الـ Function
    لتحديد اذا كان القيمة اعلى من القيمة المخزنة في متغير lastContentOffset  معناه تم السحب الى الأعلى
    ولكن اذا كان القيمة اصغر من القيمة المخزنة في متغير lastContentOffset معناه السحب الى الأسفل

     
     func scrollViewDidScroll(_ scrollView: UIScrollView) {   if (self.lastContentOffset < scrollView.contentOffset.y) { // السحب الى اعلى ، افعل امراً ما      } else if (self.lastContentOffset > scrollView.contentOffset.y) { //  السحب الى اسفل ، افعل امراً ما} }  
    الان نبدا في المهم
     
    نضيف التالي :
    اسفل اقواس الـ // السحب الى اعلى ، افعل امراً ما

     
    UIView.animate(withDuration: 0.5,animations: { // صغرنا الصورة بتغير قيودها self.progileHight.constant = 45 self.profileWidth.constant = 45 // حولنا الى وضع افقي  bigStackView self.bigStackView.axis = .horizontal   // حولنا الى وضع افقي StackView self.StackView.axis = .horizontal     // غيرنا المسافة بين الصورة والنص الى ١٥   self.StackView.spacing = 15             // صغرنا ارتفاع الـ bigstackview                                 self.StackViewHight.constant = 60                           // طلبنا بتحديث القيود   self.view.layoutIfNeeded()   })  
    ومن ثم سوف نضيف الاكواد التالية اسفل سطر
    //  السحب الى اسفل ، افعل امراً ما
    UIView.animate(withDuration: 0.5,animations: { // ارجاع حجم الصورة الى حجمها الاصلي   self.progileHight.constant = 100     self.profileWidth.constant = 100    // ارجاع وضعهم الى الوضع العامودي  self.bigStackView.axis = .vertical   self.StackView.axis = .vertical      // ارجاع المسافة المسافة الاصلية                           self.StackView.spacing = 5                           // ارجاع ارتفاع الـ bigStackView    self.StackViewHight.constant = 160 // طلب تحديث القيود        self.view.layoutIfNeeded()      })  
    ملف الاكواد النهائي سوف يكون بالشكل التالي التالي :
     
     
    import UIKit class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource {          @IBOutlet weak var bigStackView: UIStackView!          @IBOutlet weak var StackView: UIStackView!          @IBOutlet weak var StackViewHight: NSLayoutConstraint!         @IBOutlet weak var progileHight: NSLayoutConstraint!               @IBOutlet weak var profileWidth: NSLayoutConstraint!                    var rowsNames = ["Row 0", "Row 1", "Row 2", "Row 3", "Row 4", "Row 5",                   "Row 6", "Row 7", "Row 8", "Row 9", "Row 10", "Row 11",                   "Row 12", "Row 13", "Row 14", "Row 15", "Row 16", "Row 17",                   "Row 18", "Row 19", "Row 20", "Row 21", "Row 22", "Row 23",                   "Row 24", "Row 25", "Row 26", "Row 27", "Row 28", "Row 29", "Row 20"]               var lastContentOffset: CGFloat = 0               override func viewDidLoad() {         super.viewDidLoad()          }          // MARK: - UITableViewDataSource               func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {         return rowsNames.count     }      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {         let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)                  cell.textLabel?.text = rowsNames[indexPath.row]                  return cell     }                func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {         self.lastContentOffset = scrollView.contentOffset.y     }         func scrollViewDidScroll(_ scrollView: UIScrollView) {         if (self.lastContentOffset < scrollView.contentOffset.y) {                                             UIView.animate(withDuration: 0.5,                                    animations: {                                                                                                                                                   self.progileHight.constant = 45                                     self.profileWidth.constant = 45                 self.bigStackView.axis = .horizontal                                                                          self.StackView.axis = .horizontal                                                                    self.StackView.spacing = 15                                                                         self.StackViewHight.constant = 60                                                                          self.view.layoutIfNeeded()                                  })                                                    } else if (self.lastContentOffset > scrollView.contentOffset.y) {                                      UIView.animate(withDuration: 0.5,                            animations: {                                                     self.progileHight.constant = 100                             self.profileWidth.constant = 100                                                          self.bigStackView.axis = .vertical                             self.StackView.axis = .vertical                                                         self.StackView.spacing = 5                             self.StackViewHight.constant = 160            self.view.layoutIfNeeded()                                                           })         }     } }  
    النتيجة النهائية :

     
    مستوى المقال: متوسط
  5. بسم الله الرحمن الرحيم
    السلام عليكم ورحمة الله وبركاته
     
    في البداية ماهو Timer؟
    "هو كلاس مؤقت يقوم باطلاق تنبيه بعد أن فترة زمنية يحددها المستخدم، ليرسل رسالة محددة الى Object معين"
     
    في هذا الموضوع سنتعلم كيفية انشاء تطبيق مؤقت بسيط باستخدام كلاس Timer 
    - قم بانشاء مشروع جديد على الxCode من نوع Single View Application

     
    - سم البروجكت كما تحب وتأكد من اختيار Swift كلغة المشروع

     
    - اضف UILabel و UIButton ثلاث مرات بالتصميم المناسب لك، قمت انا بتصميم الواجهة كالتالي

     
    - الآن اضف الOutlets والActions والFunctions والVariables كالتالي

    (قمنا بتعريف variable من كلاس Timer في الاعلى لنستطيع الوصول اليه من كل الميثودز)
     
    - اضف الكود التالي لميثودي updateTimer() و startBtnTapped() 
    @IBAction func startBtnTapped(_ sender: Any) { timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true) } func updateTimer() { intCounter += 1 //Set counter in UILabel timeLbl.text! = String(format: "%02d:%02d:%02d", intCounter / 3600, (intCounter % 3600) / 60, (intCounter % 3600) % 60) } في الكود السابق قمنا باستدعاء الميثود scheduleTimer التي ستقوم بانشاء لوب يتكرر بحسب الوقت الذي نحدده ليقوم بالأكشن الذي نحدده أيضا 
    timeInterval: هو الوقت الذي نريد انتظاره قبل تكرار الأكشن مرة أخرى، قمنا بتحديده 1 لتتكرر الميثود ()updateCounter كل ثانية target: هو self حيث قمنا باختيار الView controller الحالية ViewController.swift selector: هو الأكشن الذي سنقوم به بعد كل timeInterval userinfo: اذا اردت ارسال أي data الى الميثود المستدعاة يمكنك ارسالها من هنا، لكنا قمنا بوضعها nil حاليا repeats: هل تريد تكرار الأكشن كل timeInterval أم اطلاق الأكشن مرة واحدة فقط؟ (في حالة اختيار repeats: true سيقوم الtimer بتكرار تطبيق الأكشن كل timeInterval الى مالا نهاية، أو إلى أن نوقفه يدويا) في الميثود ()updateTimer قمنا بزيادة الكاونتر قيمة 1، ثم قمنا بحساب عدد الساعات والدقائق والثواني التي لعرضها في الtimeLbl
     
    - قم باضافة الكود التالي لبقية ميثودز الأكشن:
    @IBAction func stopBtnTapped(_ sender: Any) { //Invalidate timer timer.invalidate() } @IBAction func resetBtnTapped(_ sender: Any) { intCounter = 0 stopBtnTapped(sender) timeLbl.text = "00:00:00" } في الميثود الأولى:
    ()invalidate: تستعمل لايقاف الtimer (في حالة قمت باختيار repeats: true)
    في الميثود الثانية:
    قمنا باعادة ضبط الكاونتر وايقاف المؤقت تماما، واعادة ضبط الtimeLbl
     
    - أخيرا أضفت بعد الأكواد لايقاف الأزرار عن العمل عند الضغط عليها (لكي لا يضغط المستخدم على زر Start مرتين متتاليتين)

     
    النتيجة النهائية للمشروع:

     
    الى هنا نصل الى ختام موضوعنا
    استودعكم الله الذي لا تضيع ودائعه
    مستوى المقال: مبتدئ
  6. رغم انه المفترض في هذا الموضوع اتحدث عن طرق الاستخدام الخاطئ للـ 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 استخداماً
    لكن هناك طرق أخرى أيضا !
     
    مستوى المقال: متوسط
  7. بسم الله الرحمن الرحيم
     
    سنتعرف في هذا الدرس على طريقة انشاء تطبيق يدعم تعدد اللغات بحسب لغة الجهاز، سنعتمد اللغتين العربية والانجليزية في هذا التطبيق.
     
    سنبدأ أولا باضافة جميع اللغات التي نريد دعمها في تطبيقنا
    نذهب الى 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):


     
     
    وصلنا الى ختام الدرس، أستودعكم الله الذي لا تضيع ودائعه.
    مستوى المقال: متوسط
  8. ماهو ال 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 .

     
    الان نقوم بتشغيل التطبيق لرؤيه النتيجه
     

     
    اتمنى أنني وفقت في شرح هذا المفهوم المهم بطريقه سهله وبسيطه ـ شكرا لكم ولعالم البرمجه
     
     
    مستوى المقال: متوسط
  9. بسم الله الرحمن الرحيم
     
    كما نعرف فإن لغة السويفت 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")  
     
    وصلنا لنهاية المقالة أتمنى أن أكون قد وفقت في إيصال المعلومات بشكل واضح وسهل للجميع ،وبإذن الله سيكون هناك جزء ثاني لهذه المقالة نتعمق فيها أكثر في القواميس .
    مستوى المقال: متوسط
  10. من أكثر الأسباب التي تؤدي الى زيادة معدل تقييم تطبيقك في متجر التطبيقات هو عن طريق سؤال المستخدم من داخل التطبيق !
     
    هناك طرق كثيره ومكتبات متنوعه لفعل ذلك ووظيفتها تكمن في سؤال المستخدم ما اذا اعجبهم التطبيق ويريد كتابة مراجعة له ، فيتم تحويلهم الى متجر التطبيقات
     
    ولكن اليوم سوف نتحدث عن 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 في نظامها
     
     
    مستوى المقال: محترف
  11. بسم الله الرحمن الرحيم
     

    مقدمة
    عادة يتم استخدام الUIAlertController لعرض نافذة تحذيرية للمستخدم، تحتوي على رسالة وازرار لاتخاذ اجراءات بناء عليها.
    يمكن استخدامها أيضا لتمكين المستخدم من ادخال بيانات تسجيل دخول او حساب جديد، عرض بيانات للمستخدم، أو عرض المزيد من الاعدادات في نافذة خارجية.
    منذ قدوم iOS9، أبل قامت بالغاء الكلاس القديم UIAlertView واستبدلته بUIAlertController، حيث لم تعد تحتاج لضبط الdelegatesـ وتستطيع الآن تخصيص النافذة كما تشاء.
    هناك نوعين من الUIAlertController:
    1- Alert UIAlertController

    2- ActionSheet UIAlertController

     
    والآن سنرى كيفية انشائها وتخصيصها
    (لانشاء النوافذ المطلوبة، يمكنك تضمين جميع الأكواد القادمة في داخل IBAction او TableViewCell أو حتى في viewDidLoad لتقوم بتنفيذ الكود مباشرة) 
     
    - نافذة Alert تحتوي على TextField:
    أول نافذة تحذيرية سنقوم بانشائها، تحتوي على textfield واحد، بالاضافة الى زرين submit و cancel
    سنبدأ أولا باضافة الأسطر التالية:
    let alert = UIAlertController(title: "AlertController Tutorial", message: "Submit some text", preferredStyle: .alert) // Submit button let submitAction = UIAlertAction(title: "Submit", style: .default, handler: { (action) -> Void in // Get 1st TextField's text let textField = alert.textFields![0] print(textField.text!) }) // Cancel button let cancel = UIAlertAction(title: "Cancel", style: .destructive, handler: { (action) -> Void in }) في الثلاث أسطر الأولى، قمنا بانشاء UIAlertController، وحددنا عنوانه والرسالة المضمنة فيه، وحددنا نوعه ليكون alert.
    تاليا قمنا بانشاء أول زر submitAction، وخصصنا عنوانه وستايله، وفي بلوك الhandler الأخير أضفنا الأوامر التي نرغب بتنفيذها عند الضغط على هذا الزر
    أخيرا قمنا بانشاء زر cancel بستايل destructive. ليكون أحمر اللون
     
    الآن بقي لنا أن نضيف هذين الزرين الى النافذة التي أنشأناها أولا، ومن ثم نعرض النافذة للمستخدم.
    ولكن قبل هذا، لا ننسى أن ننشئ و نضيف الTextField الى النافذة، فنكمل باضافة الكود التالي:
    // Add 1 textField and customize it alert.addTextField { (textField: UITextField) in textField.keyboardAppearance = .dark textField.keyboardType = .default textField.autocorrectionType = .default textField.placeholder = "Type something here" textField.clearButtonMode = .whileEditing } // Add action buttons and present the Alert alert.addAction(submitAction) alert.addAction(cancel) present(alert, animated: true, completion: nil) في الأسطر الأولى قمنا بانشاء وتخصيص واضافة الTextField في خطوة واحدة.
    ثم أخيرا قمنا باضافة الزرين السابق انشاؤهما وقمنا بعرض الUIAlertController عن طريق استدعاء الميثود present
     
    النتيجة:

     
     
    - نافذة ActionSheet تحتوي على عدة خيارات:
    بطريقة مشابهة جدا لنوافذ الAlert، سنقوم بانشاء نافذة ActionSheet. أولا سنقوم باضافة الكود التالي:
    let alertController = UIAlertController(title: "Action Sheet", message: "What would you like to do?", preferredStyle: .actionSheet) let sendButton = UIAlertAction(title: "Send", style: .default, handler: { (action) -> Void in print("Send button tapped") }) let deleteButton = UIAlertAction(title: "Delete", style: .destructive, handler: { (action) -> Void in print("Delete button tapped") }) let cancelButton = UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) -> Void in print("Cancel button tapped") }) قمنا أولا بانشاء UIAlertController من نوع ActionSheet، ثم أنشأنا عدة أزرار بستايلات مختلفة وكل زر يحتوي على بلوك الactions الخاص به.
    الآن سنضيف الأزرار الى الUIAlertController ومن ثم سنعرضها:
    alertController.addAction(sendButton) alertController.addAction(deleteButton) alertController.addAction(cancelButton) present(alertController, animated: true, completion: nil)  
    النتيجة:

     
    لاحظ أن ستايلات الأزرار تختلف بالشكل: 
    defaults. يعطيك شكل زر Send now الطبيعي
    destructive. يضيف اللون الأحمر ويستعمل عادة لأوامر الحذف لتنبيه المستخدم
    cancel. يعزل الزر عن باقي الأزرار ويستعمل عادة لزر الالغاء
     
     
    وهنا نصل الى ختام موضوعنا، نلقاكم على خير باذن الله
    سبحانك اللهم وبحمدك أشهد أن لا اله إلا أنت، استغفرك وأتوب اليك
    مستوى المقال: متوسط
  12. في  البرمجة بشكل عام المتغيرات Variabals والثوابت Constant موجودة بهدف ان تتيح لك تخزين البيانات لاعادة استخدامها مره اخرى في البرنامج. وعند تعريفك لمتغير جديد في البرنامج فانت ستقوم بربط اسم محدد مثل name او age بقيمة محددة من نوع محدد. 
     
    تسمية المتغيرات
    لديك كامل الحرية في تسمية المتغيرات ماعدا بعض القيود مثل التالية:
    لا تبدآ برقم لا تتضمن مسافات لا تتضمن رموز عمليات رياضية لا تستخدم الكلمات التي تستخدمها لغه سويفت كجزء منها يجب آن تلتزم بافضل المممارسات في تسمية المتغيرات بحيث تضمن كتابة كود سهل القراءة والفهم، خاصة اذا كنت تعمل في فريق برمجي. اذا اردت اضافة متغير لتخزين اسم فستكون تسميته name مناسبه ومنطقيه اكثر من تسميته n فقط! ولو اردت كتابة اسم متغير بكلمات متعددة فاستخدم اسلوب camel case بحيث اول كلمه من الاسم المتعدد تكون بحروف صغيره ثم اول حرف يكون كبير لاي كلمه اخرى مثل userOneName.
     
    إضافة المتغيرات والثوابت
    عملية اضافة وتعريف المتغيرات والثواتب هي واحدة. اولاً يتم تحديد اما ثابت let او متغير var حسب الحالة، ثم اسم المتغير حتى يمكنك استدعائه به في البرنامج لاحقاً، ثم لديك الخيار اما ان تحدد قيمة للمتغير عند تعريفه اول مره كما في المثال التالي
    var name = "robot" او تترك المتغير فارغ بدون قيمة لتحددها في وقت اخر ولكن عليك حينها استخدام شيئ يدعى في سويفت type annotation وهو كتابة نوع البيانات التي تريد منحها لاحقاً للمتغير. كما في المثال التالي:
    var name: String name = "robot" بالطبع لا يفضل استخدام هذا الاسلوب الثاني اذا كنت ستقوم بكتابة البيانات مباشرة في السطر الثاني لتعريف اسم المتغير. دائما عند تعريف المتغير مع قيمته اكتب المتغير وقيمته في سطر واحد كما في المثال الاول، ولو اردت الحاق نوع المتغير قبل القيمة فلن تمنعك سويفت من ذلك ولكنه امر لا داعي له في معظم الاحوال.
    var name: String = "robot"  
    الفرق بين المتغيرات والثوابت
    الثوابت Constant في سويفت تكون باختصار let وتعني ان القيمة ستصبح ثابته للآبد ولا يمكنك تغييرها مرة اخرى. 
    let name = “robot” اما المتغيرات وتبداء باختصار var تسمح لك بتغيير قيمتها بقدر لا محدود.
    var name = “robot” حينما تحاول ان تغير قيمة الثابت فسوف تحصل على خطآ:
    let age = 22 age = 25 اما المتغيرات فسيكون متاح لها تغييرها بالقدر الذي تريد:
    let age = 22 age = 25 age += 2 print(age) الكود السابق سيطبع 27 لان اخر سطر باضافة + قبل = يعني اضافة 2 على قيمة المتغير الذي بالسطر السابق ثم حفظ المجموع كقيمة للمتغير .
    يمكنك ايضاً نسخ او منح قيمة اي متغير او ثابت معرف مسبقاً الى متغير جديد او اخر:
    let minAge = 18 var userOneAge = minAge  
    لماذا ومتى تستخدم متغير او ثابت؟
    بالتآكيد يمكنك دوماً ادخال كل البيانات كمتغيرات var وان لا تتقيد باستخدام الثوابت let في برنامج٫ سويفت لن تجبرك بل ستسمح لك بذلك، ربما ستقترح عليك احياناً ان تغير بعض البيانات من متغير الى ثابت لانه سيكون من الافضل ذلك حسب طريقة سويفت في انجاز الامور. لكن كمبرمج فعال وخبير عليك ان تهتم باستخدام الثوابت والمتغيرات بنفسك حسب الحالة التي امامك وذلك لعدة اسباب اهمها:
    امان اللغة، لنفرض لو كان في تخطيطك للحل البرمجي حددت ان تلك القيمة يجب ان تكون ثابته دائماً وادخلتها كذلك في الكود ولكن لاحقاً نسيت وحاولت تغييرها فسوف ينبهك Xcode بالخطآ ولن يسمح لك ببناء البرنامج قبل اصلاحه. المترجم الذي يفسر الكود الخاص بس من سويفت الى اللغه التي يفهمها الكمبيوتر سيعرف ان هذه القيمة يجب ان تكون ثابته في الذاكره دائما ويغلق احتمال العوده لتغييرها لاحقاً بالتالي هو يتعامل مع الثوابت بشكل مختلف عن المتغيرات وهذا يجعل لها معالجه مختلفه تنعكس على سرعه وفعالية اداء البرنامج. ولنفرض انك تريد عمل برنامج يستخدم GPS ويحسب المسافه من مدينة لاخرى فسوف تقوم بتمثيل البيانات الرئيسية كالتالي:
    مكان الانطلاق: سيزودك به GPS مره احدة عن انطلاق الرحلة وسيظل كما هو طوال مسيره الرحلة ولن يتغير بالتالي ستقوم بتعريفه كثابت let وجهة الرحلة: ايضاً سيتم تحديده في بداية الرحلة بواسطة GPS ولن يتم تغييره طوال مسيرة الرحلة بالتالي ستقوم بتعريفه كثابت let. الموقع الحالي + المسافة المقطوعة + المسافة المتبقية: كل هذه البيانات ستتغير دائماً وبشكل فوري كلما تحركت بالتالي ستقوم بتعريف كل منها كمتغير var  
    انواع البيانات Data Type
    تملك سويفت كيغرها من لغات البرمجة العديد من انواع البيانات التي يمكن ان تحددها لكل متغير او ثابت تدخله في برنامجك. وعند تعريف انواع البيانات دائما يتم استخدام حرف كبير لاول الكلمة.
    رآينا في الامثله السابقة نوعين من تلك البيانات المستعملة وهي البيانات النصية وتسمى في سويفت String تمثل الحروف او النصوص بشكل عام:
    var name: String name = "robot" والبيانات الرقمية وتسمى Int كاختصار ل integer وتمثل الارقام العادية 1234 الخ:
    var age: Int age = 25 يوجد ايضاً Double وتمثل الارقام التي تملك فاصلة عشرية 1.0 2.0 3.0 4.0 الخ:
    var longitude: Double longitude = -87.244556 والنوع الاخر هو للبيانات المنطقية التي تحتمل اجابة صح true او خطآ false فقط واختصارها Bool بمعنى Boolean:
    var nothingNew: Bool nothingNew = true يوجد أيضاً نوع اخر هو Character ويستخدم اذا اردت اضافة بيانات عبارة عن حرف واحد فقط:
    var blood: Character blood = "A"  
    استنباط الانواع Type inference
    سويفت تملك ميزة قوية مثل type inference التي تقوم باستنتاج نوع البيانات من المتغير حينما تدخل قيمته٬ بالتالي لا داعي لكتابة نوع البيانات الا اذا كنت ستعرف المتغير ولا تنوي ادخال قيمة في نفس السطر كما شرحنا سابقاً. 
    مع type inference سيكون شكل الاكواد السابقة التي شرحنا بها انواع البيانات بالشكل الابسط والافضل كما يلي:
    var name = "robot" var age = 25 var longitude = -87.244556 var nothingNew = true  
    كان هذا كل شيء هام كمقدمة عن المتيغرات والثوابت والبيانات في سويفت. اتمنى ان لا تتردد في مشاركتي بآي استفسار او تعليق على حسابي في تويتر كما اتطلع لانضمام المزيد من المتعلمين على قناة سلاك حتى نستمتع بالتعلم معاً.
     
    اسئلة وتمارين
    أحاول في مجموعة سلاك اعداد تمارين تفاعلية من اعدادي واضيف عليها تمارين كتاب Apple التعليمي حتى ترسخ الفهم للمبتدئين تماماً وسأضع هنا التمارين الخاصة بهذا المقال. بإمكانك ارسال الحل لنا في مجموعة سلاك اطلب دعوة من حسابي في تويتر.
    قم بتعريف جميع المعلومات التالية في متغيرات او ثوابت var/let حسب ما تتطلبه كل معلومه مع ضروره تعريف نوع كل متغير او ثابت تقوم بعمله حسب نوع البيانات اذا كانت نصية او رقمية او منطقية. Name: Christopher Walken Age: 74 Birthplace: Astoria, New York, USA Residence: Wilton, Connecticut, USA Profession: Actor Years active: 1953–present Won Oscar: Yes Blood Type: A Weight: 75.62 Height: 181.3 ----------------------- اسئلة اضافية // اي القيم التالية سيكون من الافضل تمثيلها كثابت؟ let A. اسم اللاعب B. مستوى اللاعب C. درجة اللاعب D. موقع اللاعب // أي القيم التالية سيكون من الافضل تمثيلها كمتغير؟ var A. الاسم B. تاريخ الميلاد C. العمر D. عنوان المنزل // اختار الجمل التي لا يمكن ان تكون سبباً صحيحاً لاستخدام الثابت let? A. استخدام الثابت من الممارسات الجيدة التي يجب على المبرمج الالتزام بها للقيم الثابته التي لا تتغير B. المفسر يقدم معالجة مخصصة للبيانات الثابته تختلف عن المتغيرة C. لدعم أمان اللغة فالمبرمج لا يستطيع بالخطأ تغيير قيمة لا يجب ان تتغير في ذهنه حينما صمم الحل البرمجي D. معظم البيانات التي نتعامل معها لا تتغير // باستخدام استنباط نوع البيانات التلقائي الذي تدعمه سويفت, اي من البيانات التالية ستكون من نوع Double? A. var state = "California" B. let country = "USA" C. let population = 39250017 D. let speedLimit = 75.0 // هل الكود التالي سيعمل بشكل صحيح؟ ولماذا؟ let number: Double = 3 A. نعم لان المفسر سيمنح number القيمة 3.0 B. نعم لان 3 من نوع Double C. لا لأن 3 ليست من نوع Double  
    مستوى المقال: مبتدئ

عالم البرمجة

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