AMR0T

Members
  • عدد منشوراتي

    19
  • تاريخ الإنضمام

  • تاريخ اخر زياره

السمعه بالموقع

8 Neutral

عن العضو AMR0T

  • الرتبه
    مبدع مثابر
  • تاريخ الميلاد 01/29/97

معلومات عامة

  • الجنس
    ذكر
  1. تأكد ان الpicker موصل بشكل صحيح في الـ Storyboard (احذفه وارجع ضيفه)
  2. بسم الله الرحمن الرحيم مقدمة: في هذا الموضوع، سنتطرق لأحد أهم التحديثات التي قدمتها Apple مع الـ Xcode 11 والـ Swift4 بروتوكول Codable 😍 الذي سيستبدل الNSCoding القديم سيء الذكر، يقوم بجعل الـ Data model لديك Encodable, Decodable لتتكامل بشكل كامل مع أي بيانات خارجية كالـ JSON الآن أصبح بإمكانك تحويل البيانات من والى الـ JSON بسطر واحد 😇 التطبيق: لنأخذ مثال على Structure بإسم Movie، هنا سأقوم بتعريف الـ Structure ك Codable لنستطيع تحويله من والى الـ JSON بسهولة struct Movie: Codable { enum MovieGenere: String, Codable { case horror, skifi, comedy, adventure, animation } var name : String var moviesGenere : [MovieGenere] var rating : Int } 😅 لنقوم بانشاء أوبجكت من هذا الـ Structure للتوضيح مثلاً: (let upMovie = Movie(name: "Up", moviesGenere: [.comedy , .adventure, .animation], rating : 8 في هذا المثال لدينا فيلم باسم "Up" من نوع comedy, adventure, animation وتقييمه 8/10 الآن لنرى كيف نحول هذا الأوبجكت إلى بيانات JSON Encode let jsonData = try? JSONEncoder().encode(upMovie) let jsonString = String(data: jsonData, encoding: .utf8) print("JSON String : " + jsonString!) Voala الـ ()JSONEncoder سيقوم بتحويل الأوبجكت الى JSON Data وسيكون الناتج كالتالي { "name": "Up", "moviesGenere": [ "comedy", "adventure", "animation" ], "rating": 4 } Decode بنفس الطريقة، سنستعمل الـ ()JSONDecoder لتحويل البيانات من JSON الى اوبجكت من الـ Movie let upMovie = try? JSONDecoder().decode(Movie.self, from: jsonData) print("Name : \(upMovie.name)") print("Rating : \(upMovie.rating)") بفك ملف الـ JSON سنحصل على الأوبجكت upMovie وبامكاننا الوصول لخصائصه واستعمالها كما نشاء النتيجة ستكون كالتالي Name : Up Rating : 4 إلى هنا نصل الى ختام موضوعنا أستودعكم الله الذي لا تضيع ودائعه
  3. بسم الله الرحمن الرحيم "المكتبات مفتوحة المصدر"، من منا لم يستخدمها في أحد مراحل تطويره لأحد التطبيقات أو المواقع لم قد أعيد كتابة ما قام به آخرون؟ لم اختراع العجلة من جديد؟ في هذا الموضوع، سنستعرض أهم المكتبات مفتوحة المصدر التي لم ترها من قبل، لتساعدك على تطوير تطبيقك للiOS وتسهل عليك عملك في آخر كل مقالة، سأعرض قائمة مختصرة للمكتبات المذكورة مع روابط للمشاريع الخاصة بها على github 1. DZNEmptyDataSet هذه المكتبة يجب أن تكون مضمنة داخل الiOS SDK الأساسية للتعامل مع الجداول والمجموعات الفارغة بشكل افتراضي، جدول فارغ يعني صفحة بيضاء، مما يؤدي الى تجربة مستخدم سيئة جدا باستخدام هذه المكتبة، ستستطيع تجاوز هذه المشكلة بمجرد تبني بروتوكولات بسيطة في الكلاس الخاص بك، وسوف تعرض لك رسالة مناسبة في حالة كان جدولك فارغا 'pod 'DZNEmptyDataSet 2. PDTSimpleCalendar هذه المكتبة عبارة عن تقويم سهل وسريع الاستخدام، وبالامكان تخصيصها بشكل كبير سواء بالمظهر او العمل 'pod 'PDTSimpleCalendar 3. Chameleon إذا كنت قد قضيت معظم وقتك في البرمجة الى أن أصبحت مصمما سيئا (مثلي)، فعليك استخدام هذه المكتبة هذه المكتبة مبنية على كلاس UIColor، تقدم لك ألوان جميلة لتستخدمها في تطبيقك للiOS هذه المكتبة تساعدك أيضا على انشاء Color Palette من أحد الألوان التي تختارها انت. 'pod 'ChameleonFramework 4. Alamofire مكتبة متخصصة بجميع أمور الشبكات. إذا كنت تريد التحميل، الرفع، جلب JSONs .. عليك باستخدام هذه المكتبة 'pod 'Alamofire 5. TextFieldEffects ألا تعتقد أن الUITextField الخاص بالiOS ممل بعض الشيء؟ حقيقة لا حاجة لي بشرح هذه المكتبة، فقط سأعرض لك هذه الصورة لترى ما تستطيع عمله بمجرد استخدامها بمشروعك 'pod 'TextFieldEffects 6. PKRevealController 2 سننهي الموضوع بالأفضل (في رأيي عالأقل) مكتبة لدعم الSide Menu سواء من اليمين، اليسار أو الجهتين معا بعد تجربة عدة مكتبات تقدم نفس الميزة، أستطيع القول بأن هذه المكتبة هي الأسهل في التجهيز والدمج مع المشروع الحالي 'pod 'PKRevealController ملخص المكتبات المذكورة (قائمة TL;DR): DZNEmptyDataSet [تحسين مظهر الجداول الفارغة] PDTSimpleCalendar [اضافة تقويم قابلة للتخصيص] Chameleon [مكتبة الألوان] Alamofire [للتعامل مع التحميلات] TextFieldEffects [مظاهر مختلفة للUITextField] PKRevealController [مكتبة لإضافة Side menu] وصلنا الى ختام الموضوع، أستودعكم الله الذي لا تضيع ودائعه
  4. بسم الله الرحمن الرحيم "المكتبات مفتوحة المصدر"، من منا لم يستخدمها في أحد مراحل تطويره لأحد التطبيقات أو المواقع لم قد أعيد كتابة ما قام به آخرون؟ لم اختراع العجلة من جديد؟ في هذا الموضوع، سنستعرض أهم المكتبات مفتوحة المصدر التي لم ترها من قبل، لتساعدك على تطوير تطبيقك للiOS وتسهل عليك عملك في آخر كل مقالة، سأعرض قائمة مختصرة للمكتبات المذكورة مع روابط للمشاريع الخاصة بها على github 1. DZNEmptyDataSet هذه المكتبة يجب أن تكون مضمنة داخل الiOS SDK الأساسية للتعامل مع الجداول والمجموعات الفارغة بشكل افتراضي، جدول فارغ يعني صفحة بيضاء، مما يؤدي الى تجربة مستخدم سيئة جدا باستخدام هذه المكتبة، ستستطيع تجاوز هذه المشكلة بمجرد تبني بروتوكولات بسيطة في الكلاس الخاص بك، وسوف تعرض لك رسالة مناسبة في حالة كان جدولك فارغا 'pod 'DZNEmptyDataSet 2. PDTSimpleCalendar هذه المكتبة عبارة عن تقويم سهل وسريع الاستخدام، وبالامكان تخصيصها بشكل كبير سواء بالمظهر او العمل 'pod 'PDTSimpleCalendar 3. Chameleon إذا كنت قد قضيت معظم وقتك في البرمجة الى أن أصبحت مصمما سيئا (مثلي)، فعليك استخدام هذه المكتبة هذه المكتبة مبنية على كلاس UIColor، تقدم لك ألوان جميلة لتستخدمها في تطبيقك للiOS هذه المكتبة تساعدك أيضا على انشاء Color Palette من أحد الألوان التي تختارها انت. 'pod 'ChameleonFramework 4. Alamofire مكتبة متخصصة بجميع أمور الشبكات. إذا كنت تريد التحميل، الرفع، جلب JSONs .. عليك باستخدام هذه المكتبة 'pod 'Alamofire 5. TextFieldEffects ألا تعتقد أن الUITextField الخاص بالiOS ممل بعض الشيء؟ حقيقة لا حاجة لي بشرح هذه المكتبة، فقط سأعرض لك هذه الصورة لترى ما تستطيع عمله بمجرد استخدامها بمشروعك 'pod 'TextFieldEffects 6. PKRevealController 2 سننهي الموضوع بالأفضل (في رأيي عالأقل) مكتبة لدعم الSide Menu سواء من اليمين، اليسار أو الجهتين معا بعد تجربة عدة مكتبات تقدم نفس الميزة، أستطيع القول بأن هذه المكتبة هي الأسهل في التجهيز والدمج مع المشروع الحالي 'pod 'PKRevealController ملخص المكتبات المذكورة (قائمة TL;DR): DZNEmptyDataSet [تحسين مظهر الجداول الفارغة] PDTSimpleCalendar [اضافة تقويم قابلة للتخصيص] Chameleon [مكتبة الألوان] Alamofire [للتعامل مع التحميلات] TextFieldEffects [مظاهر مختلفة للUITextField] PKRevealController [مكتبة لإضافة Side menu] وصلنا الى ختام الموضوع، أستودعكم الله الذي لا تضيع ودائعه
  5. السلام عليكم ورحمة الله وبركاته في هذه المقالة سنتعلم طريقة تسجيل وتشغيل المقاطع الصوتية باستخدام Swift 4 تسجيل وتشغيل المقاطع سهل ولكن يحتاج الى الوقت لكتابة الاكواد، لذا لنبدأ أولا، قم باستدعاء الفريموورك AVFoundation المختصة بمعالجة كل المقاطع المرئية والصوتية على منصات أبل import AVFoundation ثم قم بتبني البروتوكولات AVAudioRecorderDelegate و AVAudioPlayerDelegate لاستخدامهما لاحقا class ViewController: UIViewController, AVAudioRecorderDelegate, AVAudioPlayerDelegate { ثم قم باضافة: زرين لبدء/ايقاف التسجيل/التشغيل recordingSession ليعالج جلسة تسجيل الصوت audioRecorder ليتعامل مع التسجيل نفسه ويحفظه audioPlayer لتشغيل المقاطع الصوتية بعد تسجيلها وpath لحفظ مسار ملف الصوت المسجل وتشغيله لاحقا @IBOutlet weak var recordButton: UIButton! @IBOutlet weak var playButton: UIButton! var recordingSession: AVAudioSession! var audioRecorder: AVAudioRecorder! var audioPlayer: AVAudioPlayer? var path: URL! (يتم استخدام الrecordingSession من قبل نظام iOS لتنظيم تسجيل وتشغيل المقاطع الصوتية منعا لتداخل الأصوات من أكثر من تطبيق -مثلا لكي لا يستطيع تطبيق تسجيل او تشغيل صوت عند اجراء مكالمة-) تسجيل الصوت يتطلب اذن المستخدم ليتم، ان تم اعطاء الاذن في هذا الكود سيتم تغيير نص زر التسجيل، اضف هذا الكود في ()viewDidLoad playButton.isEnabled = false recordingSession = AVAudioSession.sharedInstance() do { try recordingSession.setCategory(AVAudioSessionCategoryPlayAndRecord) try recordingSession.setActive(true) recordingSession.requestRecordPermission() { [unowned self] allowed in DispatchQueue.main.async { if allowed { self.recordButton.setTitle("تسجيل", for: .normal) } else { // failed to record! } } } } catch { // failed to record! } قبل اضافة الIBAction الخاص بزر التسجيل، سنضيف ميثود startRecording التي (1) ستحدد مكان حفظ ملف الصوت، (2) اعدادات مسجل الصوت ثم (3) بدء التسجيل func startRecording() { let audioFilename = getDocumentsDirectory().appendingPathComponent("recording.m4a") //1 path = audioFilename playButton.isEnabled = false let settings = [ //2 AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 12000, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue ] do { audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings) //3 audioRecorder.delegate = self audioRecorder.record() //3 recordButton.setTitle("ايقاف التسجيل", for: .normal) } catch { finishRecording(success: false) } } سنحتاج لاضافة الميثود getDocumentsDirectory لتعمل الميثود السابقة في هذه الميثود سنعيد المسار المتاح من النظام للتطبيق لحفظ الملفات فيه، سنقوم بحفظ الملف الصوتي في المسار الرئيسي للتطبيق func getDocumentsDirectory() -> URL { let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) let documentsDirectory = paths[0] return documentsDirectory } انتهينا من الفنكشن الخاصة ببدء التسجيل، والآن نحتاج لفنكشن تقوم بانهاء التسجيل عند طلب المستخدم func finishRecording(success: Bool) { audioRecorder.stop() audioRecorder = nil playButton.isEnabled = true if success { recordButton.setTitle("اعادة التسجيل", for: .normal) } else { recordButton.setTitle("تسجيل", for: .normal) // recording failed :( } } بانهاء الفنكشنين السابقة، نستطيع اخيرا كتابة الIBAction الخاص بزر التسجيل @IBAction func recordTapped(_ sender: Any) { if audioRecorder == nil { startRecording() } else { finishRecording(success: true) } } وهكذا نكون قد انتهينا من تسجيل الصوت لتشغيل الصوت سنضيف الIBAction التالي الخاص بزر التشغيل @IBAction func playTapped(_ sender: Any) { do { audioPlayer = try AVAudioPlayer(contentsOf: path) audioPlayer?.delegate = self audioPlayer?.play() recordButton.isEnabled = false } catch { // couldn't load file :( } } نهاية سنضيف الفنكشين التالية من البروتوكولات التي قمنا بتبنيها في بداية المقالة الأولى audioRecorderDidFinishRecording يتم استدعاؤها عن توقف التسجيل في بعض الاحيان، قد يقوم النظام بايقاف التسجيل عنوة (عندما ترد مكالمة على سبيل المثال)، في هذه الفنكشن سنتأكد عند عدم نجاح التسجيل لنضبط نص زر التسجيل func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { if !flag { finishRecording(success: false) } } والفنكشن الثانية audioPlayerDidFinishPlaying يتم استدعاؤها عند انتهاء تشغيل المقطع الصوتي func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { recordButton.isEnabled = true } (بالامكان استخدام ()audioPlayer.stop لايقاف التشغيل) (تأكد من ربط الOutlets والActions قبل التشغيل) النتيجة النهائية
  6. السلام عليكم ورحمة الله وبركاته هذه المقالة ستتحدث عن الCoredata، كدليل لمن لم يستخدم ال Coredata ابدا من قبل. سننشئ تطبيق to-do بسيط لشرح كيفية استخدام الCoredata لماذا الCoredata؟ يتم استخدام ال Coredata لحفظ بيانات الObjects على الجهاز لاستعادتها لاحقا بعد اغلاق التطبيق/الجهاز وهو ما يعرف بـ Persistence Data هناك مزايا للCoredata كامكانية التعديل على الrecords والفلترة والPartial Loading عكس الUserDefaults وصحيح أن هناك frameworks أخرى تقدم ميزة حفظ البيانات locally (مثل مكتبة Realm على سبيل المثال) لكننا لن نتطرق للفروقات بينهما و سنتحدث فقط عن الCoredata ماذا ستتعلم هنا؟ في دليل المبتدئين لن نغوص عميقا في الCoredata، لكنك ستتعلم كيفية حفظ، استعادة، وحذف البيانات. سأفترض بأنك على معرفة بالdelegates, optionals وكيفية انشاء UITableView بسيط ان لم تكن على علم بها أو ان اردت مراجعة هذه المفاهيم ستجدها في المواضيع التالية: الoptional في swift3 مقدمة في الtableview في برنامج الxcode الtable view في الxcode ( الجزء الثاني ) واجهة المستخدم في هذا الدليل سنقوم بانشاء to-do-app بسيط يتكون من واجهتين، الأولى تتكون من UITableView فقط لعرض المهام المضافة والثانية ستسخدم لاضافة المهام تحتوي على UITextField لادخال نص المهمة وUIButton لحفظ المهمة والعودة للواجهة السابقة بداية عندما تقوم بانشاء المشروع لأول مرة، تأكد من اختيار Use Core Data عند انشاء المشروع ستلاحظ وجود ملف جديد xcdatamodeld. هذا الملف كال Spreadsheet سيستعمل لانشاء مودل للبيانات التي نرغب بتخزينها اضغط على الملف الجديد واضف Task كEntity وname كAttribute من نوع String سنبدأ بحفظ البيانات في الواجهة الثانية AddTask للقيام بذلك يجب أن نحصل على access للCoredata Stack هناك كلمتين مخيفتين عليك تذكرهما جيدا، NSPersistentContainer و NSManagedObjectContext يمكنك التفكير بال Container كالصندوق الذي سنحتفظ فيه بالبيانات، والContext كفتحة الصندوق التي سنستطيع من خلالها اضافة، استرجاع وحذف البيانات بالمناسبة، في الAppDelegate.swift ستجد العديد من الميثودز والخصائص المتعلقة بالCoredata، كل ماعلينا عمله هو استخدامها في الواجهة الثانية سنضيف الكود التالي 1/ في هذا السطر قمنا بانشاء reference للContext (الصندوق) لنقوم بالتخزين فيه 2/ قمنا بتحديد نوع البيانات التي نريد تخزينها (من نوع Task Entity) وقمنا بتحديد الname المراد تخزينه 3/ طلبنا من الContext حفظ البيانات التي تم ادخالها. (بما أن الTask Entity تم ربطها بالContext فسيقوم الContext بحفظها) 4/ سنعود للواجهة الأولى لعرض المهمات الآن لعرض المهمات سنضيف الكود التالي للواجهة الأولى class TasksTable: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext //1 var tasks: [Task] = [] //2 override func viewDidLoad() { //3 super.viewDidLoad() tableView.delegate = self tableView.dataSource = self } override func viewWillAppear(_ animated: Bool) { //4 getData() tableView.reloadData() } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { //5 return tasks.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //5 let cell = UITableViewCell() let task = tasks[indexPath.row] if let myName = task.name { cell.textLabel?.text = myName } return cell } func getData() { //6 do { tasks = try context.fetch(Task.fetchRequest()) } catch { print("Fetching Failed") } } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { //7 if editingStyle == .delete { let task = tasks[indexPath.row] context.delete(task) (UIApplication.shared.delegate as! AppDelegate).saveContext() do { tasks = try context.fetch(Task.fetchRequest()) } catch { print("Fetching Failed") } } tableView.reloadData() } } سنقوم بشرح الكود السابق بالتفصيل 1/ قمنا بانشاء reference لل Context في أعلى الكلاس لنستخدمه عند الحاجة في الميثودز المختلفة 2/ قمنا بانشاء Array مؤقتة لتخزين البيانات المجلوبة من الContext وعرضها في الجدول 3/ في الViewDidLoad قمنا بتحديد الواجهة الحالية ك Delegate و DataSource للجدول 4/ في الViewWillAppear قمنا باستدعاء الميثود ()getData لجلب البيانات باستخدام الContext وتحديث الجدول لعرضها 5/ في الميثودين numberOfRowsInSection و cellForRowAt indexPath قمنا بانشاء الجدول وعرض الخلايا بالبيانات المطلوبة 6/ في الميثود getData قمنا بطلب fetch من الContext وقمنا بتحديد الTask Entity للحصول على آخر نسخة من بياناتها هذا الاستدعاء يجب أن يكون متضمن داخل do-catch block 7/ في الميثود editingStyle الخاصة بالجدول قمنا باضافة ميزة السحب للحذف (كالتالي) واستخدمنا الميثود ()context.delete لحذف الtask التي تم اختيارها ثم قمنا باعادة استدعاء البيانات من جديد وتحديث الجدول لعرضها النتيجة النهائية للتطبيق ونصل هنا الى ختام مقالتنا أستودعكم الله الذي لا تضيع ودائعه
  7. السلام عليكم ورحمة الله وبركاته في هذه المقالة سنتعلم طريقة تسجيل وتشغيل المقاطع الصوتية باستخدام Swift 4 تسجيل وتشغيل المقاطع سهل ولكن يحتاج الى الوقت لكتابة الاكواد، لذا لنبدأ أولا، قم باستدعاء الفريموورك AVFoundation المختصة بمعالجة كل المقاطع المرئية والصوتية على منصات أبل import AVFoundation ثم قم بتبني البروتوكولات AVAudioRecorderDelegate و AVAudioPlayerDelegate لاستخدامهما لاحقا class ViewController: UIViewController, AVAudioRecorderDelegate, AVAudioPlayerDelegate { ثم قم باضافة: زرين لبدء/ايقاف التسجيل/التشغيل recordingSession ليعالج جلسة تسجيل الصوت audioRecorder ليتعامل مع التسجيل نفسه ويحفظه audioPlayer لتشغيل المقاطع الصوتية بعد تسجيلها وpath لحفظ مسار ملف الصوت المسجل وتشغيله لاحقا @IBOutlet weak var recordButton: UIButton! @IBOutlet weak var playButton: UIButton! var recordingSession: AVAudioSession! var audioRecorder: AVAudioRecorder! var audioPlayer: AVAudioPlayer? var path: URL! (يتم استخدام الrecordingSession من قبل نظام iOS لتنظيم تسجيل وتشغيل المقاطع الصوتية منعا لتداخل الأصوات من أكثر من تطبيق -مثلا لكي لا يستطيع تطبيق تسجيل او تشغيل صوت عند اجراء مكالمة-) تسجيل الصوت يتطلب اذن المستخدم ليتم، ان تم اعطاء الاذن في هذا الكود سيتم تغيير نص زر التسجيل، اضف هذا الكود في ()viewDidLoad playButton.isEnabled = false recordingSession = AVAudioSession.sharedInstance() do { try recordingSession.setCategory(AVAudioSessionCategoryPlayAndRecord) try recordingSession.setActive(true) recordingSession.requestRecordPermission() { [unowned self] allowed in DispatchQueue.main.async { if allowed { self.recordButton.setTitle("تسجيل", for: .normal) } else { // failed to record! } } } } catch { // failed to record! } قبل اضافة الIBAction الخاص بزر التسجيل، سنضيف ميثود startRecording التي (1) ستحدد مكان حفظ ملف الصوت، (2) اعدادات مسجل الصوت ثم (3) بدء التسجيل func startRecording() { let audioFilename = getDocumentsDirectory().appendingPathComponent("recording.m4a") //1 path = audioFilename playButton.isEnabled = false let settings = [ //2 AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 12000, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue ] do { audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings) //3 audioRecorder.delegate = self audioRecorder.record() //3 recordButton.setTitle("ايقاف التسجيل", for: .normal) } catch { finishRecording(success: false) } } سنحتاج لاضافة الميثود getDocumentsDirectory لتعمل الميثود السابقة في هذه الميثود سنعيد المسار المتاح من النظام للتطبيق لحفظ الملفات فيه، سنقوم بحفظ الملف الصوتي في المسار الرئيسي للتطبيق func getDocumentsDirectory() -> URL { let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) let documentsDirectory = paths[0] return documentsDirectory } انتهينا من الفنكشن الخاصة ببدء التسجيل، والآن نحتاج لفنكشن تقوم بانهاء التسجيل عند طلب المستخدم func finishRecording(success: Bool) { audioRecorder.stop() audioRecorder = nil playButton.isEnabled = true if success { recordButton.setTitle("اعادة التسجيل", for: .normal) } else { recordButton.setTitle("تسجيل", for: .normal) // recording failed :( } } بانهاء الفنكشنين السابقة، نستطيع اخيرا كتابة الIBAction الخاص بزر التسجيل @IBAction func recordTapped(_ sender: Any) { if audioRecorder == nil { startRecording() } else { finishRecording(success: true) } } وهكذا نكون قد انتهينا من تسجيل الصوت لتشغيل الصوت سنضيف الIBAction التالي الخاص بزر التشغيل @IBAction func playTapped(_ sender: Any) { do { audioPlayer = try AVAudioPlayer(contentsOf: path) audioPlayer?.delegate = self audioPlayer?.play() recordButton.isEnabled = false } catch { // couldn't load file :( } } نهاية سنضيف الفنكشين التالية من البروتوكولات التي قمنا بتبنيها في بداية المقالة الأولى audioRecorderDidFinishRecording يتم استدعاؤها عن توقف التسجيل في بعض الاحيان، قد يقوم النظام بايقاف التسجيل عنوة (عندما ترد مكالمة على سبيل المثال)، في هذه الفنكشن سنتأكد عند عدم نجاح التسجيل لنضبط نص زر التسجيل func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { if !flag { finishRecording(success: false) } } والفنكشن الثانية audioPlayerDidFinishPlaying يتم استدعاؤها عند انتهاء تشغيل المقطع الصوتي func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { recordButton.isEnabled = true } (بالامكان استخدام ()audioPlayer.stop لايقاف التشغيل) (تأكد من ربط الOutlets والActions قبل التشغيل) النتيجة النهائية
  8. السلام عليكم ورحمة الله وبركاته هذه المقالة ستتحدث عن الCoredata، كدليل لمن لم يستخدم ال Coredata ابدا من قبل. سننشئ تطبيق to-do بسيط لشرح كيفية استخدام الCoredata لماذا الCoredata؟ يتم استخدام ال Coredata لحفظ بيانات الObjects على الجهاز لاستعادتها لاحقا بعد اغلاق التطبيق/الجهاز وهو ما يعرف بـ Persistence Data هناك مزايا للCoredata كامكانية التعديل على الrecords والفلترة والPartial Loading عكس الUserDefaults وصحيح أن هناك frameworks أخرى تقدم ميزة حفظ البيانات locally (مثل مكتبة Realm على سبيل المثال) لكننا لن نتطرق للفروقات بينهما و سنتحدث فقط عن الCoredata ماذا ستتعلم هنا؟ في دليل المبتدئين لن نغوص عميقا في الCoredata، لكنك ستتعلم كيفية حفظ، استعادة، وحذف البيانات. سأفترض بأنك على معرفة بالdelegates, optionals وكيفية انشاء UITableView بسيط ان لم تكن على علم بها أو ان اردت مراجعة هذه المفاهيم ستجدها في المواضيع التالية: الoptional في swift3 مقدمة في الtableview في برنامج الxcode الtable view في الxcode ( الجزء الثاني ) واجهة المستخدم في هذا الدليل سنقوم بانشاء to-do-app بسيط يتكون من واجهتين، الأولى تتكون من UITableView فقط لعرض المهام المضافة والثانية ستسخدم لاضافة المهام تحتوي على UITextField لادخال نص المهمة وUIButton لحفظ المهمة والعودة للواجهة السابقة بداية عندما تقوم بانشاء المشروع لأول مرة، تأكد من اختيار Use Core Data عند انشاء المشروع ستلاحظ وجود ملف جديد xcdatamodeld. هذا الملف كال Spreadsheet سيستعمل لانشاء مودل للبيانات التي نرغب بتخزينها اضغط على الملف الجديد واضف Task كEntity وname كAttribute من نوع String سنبدأ بحفظ البيانات في الواجهة الثانية AddTask للقيام بذلك يجب أن نحصل على access للCoredata Stack هناك كلمتين مخيفتين عليك تذكرهما جيدا، NSPersistentContainer و NSManagedObjectContext يمكنك التفكير بال Container كالصندوق الذي سنحتفظ فيه بالبيانات، والContext كفتحة الصندوق التي سنستطيع من خلالها اضافة، استرجاع وحذف البيانات بالمناسبة، في الAppDelegate.swift ستجد العديد من الميثودز والخصائص المتعلقة بالCoredata، كل ماعلينا عمله هو استخدامها في الواجهة الثانية سنضيف الكود التالي 1/ في هذا السطر قمنا بانشاء reference للContext (الصندوق) لنقوم بالتخزين فيه 2/ قمنا بتحديد نوع البيانات التي نريد تخزينها (من نوع Task Entity) وقمنا بتحديد الname المراد تخزينه 3/ طلبنا من الContext حفظ البيانات التي تم ادخالها. (بما أن الTask Entity تم ربطها بالContext فسيقوم الContext بحفظها) 4/ سنعود للواجهة الأولى لعرض المهمات الآن لعرض المهمات سنضيف الكود التالي للواجهة الأولى class TasksTable: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext //1 var tasks: [Task] = [] //2 override func viewDidLoad() { //3 super.viewDidLoad() tableView.delegate = self tableView.dataSource = self } override func viewWillAppear(_ animated: Bool) { //4 getData() tableView.reloadData() } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { //5 return tasks.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //5 let cell = UITableViewCell() let task = tasks[indexPath.row] if let myName = task.name { cell.textLabel?.text = myName } return cell } func getData() { //6 do { tasks = try context.fetch(Task.fetchRequest()) } catch { print("Fetching Failed") } } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { //7 if editingStyle == .delete { let task = tasks[indexPath.row] context.delete(task) (UIApplication.shared.delegate as! AppDelegate).saveContext() do { tasks = try context.fetch(Task.fetchRequest()) } catch { print("Fetching Failed") } } tableView.reloadData() } } سنقوم بشرح الكود السابق بالتفصيل 1/ قمنا بانشاء reference لل Context في أعلى الكلاس لنستخدمه عند الحاجة في الميثودز المختلفة 2/ قمنا بانشاء Array مؤقتة لتخزين البيانات المجلوبة من الContext وعرضها في الجدول 3/ في الViewDidLoad قمنا بتحديد الواجهة الحالية ك Delegate و DataSource للجدول 4/ في الViewWillAppear قمنا باستدعاء الميثود ()getData لجلب البيانات باستخدام الContext وتحديث الجدول لعرضها 5/ في الميثودين numberOfRowsInSection و cellForRowAt indexPath قمنا بانشاء الجدول وعرض الخلايا بالبيانات المطلوبة 6/ في الميثود getData قمنا بطلب fetch من الContext وقمنا بتحديد الTask Entity للحصول على آخر نسخة من بياناتها هذا الاستدعاء يجب أن يكون متضمن داخل do-catch block 7/ في الميثود editingStyle الخاصة بالجدول قمنا باضافة ميزة السحب للحذف (كالتالي) واستخدمنا الميثود ()context.delete لحذف الtask التي تم اختيارها ثم قمنا باعادة استدعاء البيانات من جديد وتحديث الجدول لعرضها النتيجة النهائية للتطبيق ونصل هنا الى ختام مقالتنا أستودعكم الله الذي لا تضيع ودائعه
  9. بسم الله الرحمن الرحيم مقدمة استعرضنا في الجزء الأول طريقة ارسال البيانات بين ال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، يمكنك أنت أن تقوم بالتعامل مع البيانات كما تشاء. وصلنا الى نهاية موضوعنا اليوم، أترككم في حفظ الله ورعايته.
  10. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته في البداية ماهو 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 مرتين متتاليتين) النتيجة النهائية للمشروع: الى هنا نصل الى ختام موضوعنا استودعكم الله الذي لا تضيع ودائعه
  11. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته في البداية ماهو 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 مرتين متتاليتين) النتيجة النهائية للمشروع: الى هنا نصل الى ختام موضوعنا استودعكم الله الذي لا تضيع ودائعه
  12. بسم الله الرحمن الرحيم مقدمة استعرضنا في الجزء الأول طريقة ارسال البيانات بين ال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، يمكنك أنت أن تقوم بالتعامل مع البيانات كما تشاء. وصلنا الى نهاية موضوعنا اليوم، أترككم في حفظ الله ورعايته.
  13. بسم الله الرحمن الرحيم سنتعرف في هذا الدرس على طريقة انشاء تطبيق يدعم تعدد اللغات بحسب لغة الجهاز، سنعتمد اللغتين العربية والانجليزية في هذا التطبيق. سنبدأ أولا باضافة جميع اللغات التي نريد دعمها في تطبيقنا نذهب الى 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): وصلنا الى ختام الدرس، أستودعكم الله الذي لا تضيع ودائعه.
  14. بسم الله الرحمن الرحيم سنتعرف في هذا الدرس على طريقة انشاء تطبيق يدعم تعدد اللغات بحسب لغة الجهاز، سنعتمد اللغتين العربية والانجليزية في هذا التطبيق. سنبدأ أولا باضافة جميع اللغات التي نريد دعمها في تطبيقنا نذهب الى Project Navigator ثم نقوم باضافة اللغات المطلوبة من الأسفل نلاحظ تقسيم الملفات الى لغتين كما في الصورة االآن سنقوم بانشاء ملف من نوع String لنخزن فيه جميع الStrings باللغتين العربية والانجليزية اضغط على File > New > File واختر النوع String كما في الصورة تأكد من تسمية الملف باسم "Localizable" تماما كما هو ليتعرف عليه الxCode بشكل صحيح الآن نختار الملف الجديد من اليسار، ومن قائمة الFile Inspector نضغط على Localize ونختار اللغة الانجليزية مبدئيا الآن نختار اللغة العربية أيضا لنقوم بتقسيم ملف الLocalizable.strings الى لغتين قم باضافة الStrings المطلوبة الى الملفين بالطريقة التالية ليقوم الxCode بعمل Build تأكد من: كتابة الkey بشكل موحد بين اللغتين العربية والانجليزية وضع علامة ال= بين الkey والvalue كتابة الvalue بين علامتي تنصيص "" انهاء السطر بSemi-Colon ; لنرى طريقة استدعاء الStrings من ملف Localizable قمت بعمل الواجهة البسيطة التالية: وقمت بربط الOutlets والActions كالتالي: الآن اضف الكود التالي في زر اللغة الانجليزية: let path = Bundle.main.path(forResource: "en", ofType: "lproj") let bundle = Bundle.init(path: path!)! as Bundle nameLbl.text = bundle.localizedString(forKey: "WebsiteName", value: nil, table: nil) والتالي لزر اللغة العربية: let path = Bundle.main.path(forResource: "ar", ofType: "lproj") let bundle = Bundle.init(path: path!)! as Bundle nameLbl.text = bundle.localizedString(forKey: "WebsiteName", value: nil, table: nil) في السطر الأول: قمنا بتعريف الpath الذي سيوصلنا لكل من ملفات اللغة العربية والانجليزية (يمكنك معرفة الResource والType من قائمة الFile Inspector عند الضغط على ملفي العربية والانجليزية) في السطر الثاني قمنا بتعريف كل من ملفات اللغتين كVariable يحمل الاسم bundle في السطر الثالث قمنا باستدعاء الString المطلوب باستخدام الميثود localizedString(_:String,_:String,_:Strint( والآن عند تجربة البرنامج نرى النتيجة التالية GIF: والآن سنقوم بتغيير النص اعتمادا على لغة الجهاز باستخدام ميثود واحدة فقط لنرى ذلك قمت بتغيير الStoryboard ليحتوي على زر واحد فقط، وقمت باضافة الoutlets والactions كالتالي: والآن قم باضافة السطرين التاليين للميثود viewDidLoad() let btnTitle: String = NSLocalizedString("ButtonText", comment: "") languageBtn.setTitle(btnTitle, for: .normal) والسطر التالي للميثود الخاصة بالbutton action nameLbl.text = NSLocalizedString("WebsiteName", comment: "") في هذه الاكواد، قمنا باستعمال الميثود NSLocalizedString لاستدعاء الString المناسب بحسب لغة الجهاز لتجربة البرنامج سنقوم بتشغيله على جهاز بلغة انجليزية، ثم سنحولها للعربية ونعيد التجربة وصلنا الى ختام الدرس، أستودعكم الله الذي لا تضيع ودائعه. تم ترقية هذا الطرح المميز الى صفحة المقالات
  15. بسم الله الرحمن الرحيم مقدمة عادة يتم استخدام ال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. يعزل الزر عن باقي الأزرار ويستعمل عادة لزر الالغاء وهنا نصل الى ختام موضوعنا، نلقاكم على خير باذن الله سبحانك اللهم وبحمدك أشهد أن لا اله إلا أنت، استغفرك وأتوب اليك

عالم البرمجة

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