مساحة اعلانية

آخر المواضيع

إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس

إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس
إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس

في مارس من هذا العام، قامت شركتى Dark Sky  و Forecast .io بتقديم التوقعات. والتوقع هو عبارة عن نظام مناخي بسيط يسمى API وهو برنامج يمكنه الكشف عن بيانات الطقس على كل من المدى البعيد والمدى القصير، وفي هذه السلسلة، سوف تظهر لك كيفية إنشاء تطبيق الطقس الرائع لدائرة الرقابة الداخلية مدعوم بالتوقعات. 


مـقـــدمــــــــة
في وقت سابق من هذا العام، قدمت  شركة Dark Sky  التوقعات، وهو API بسيط يمكنه التنبؤ بالطقس على المدى البعيد والقصير. في هذه السلسلة سوف نقوم بإنشاء تطبيق دائرة الرقابة الداخلية مدعوم من قبل API Forecast ونقدم التوقعات مجاناً لعدد يصل إلى الآلاف ممن يقومون باستدعاء API يومياً، لذا لا تتردد في التسجيل للحصول على حساب مطور، تابع معي.

على الرغم من أن عدد الأغلفة مفتوحة المصدر لتوقعات API تتوفر في هذه السلسلة سوف نستخدم مكتبة AFNetworking Library للإستعلام عن توقعات API في الجزء الأول من هذه السلسلة، فإننا سنضع الأساس للمشروع وتنفيذ واجهة المستخدم الأساسية. على الرغم من أن طلبنا فى إطار بسيط، إلا أنه سيتم دعم مواقع متعددة، وهذا ما سنركز عليه في هذا المقال.

1. إعداد المشروع 

الخطوة 1: إنشاء المشروع
قم بفتح برنامج X-Code وقم بإنشاء مشروع جديد "New Project" يعتمد على قالب "Empty Application" كما هو واضح فى (الشكل 1) ثم قم بتسمية المشروع بأسم Rain وقم بتمكين Automatic Reference Counting كما في (الشكل 2).

إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس
إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس
إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس
إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس

الخطوة 2: إضافة المكتبات "Libraries"
فى هذا المشروع، سوف نقوم باستخدام ثلاثة مكتبات مفتوحة المصدر (١) SVProgressHUD ويتم إنشاؤها بواسطة Sam Vermette 
(2) AFNetworking ويتم إنشاؤها بواسطة Mattt Thompson 
(3) ViewDeck ويتم إنشاؤها بواسطة Tom Adriaenssen

وبما أننى أحب بشراهة التعامل مع CocoaPods سوف أقوم باستخدامه من أجل تثبيت وإدارة المكتبات التى أخترناها أعلاه فى مشروعنا.

إذا لم تكن معتاداً على استخدام CocoaPods، فأنا أوصيك بزيارة الموقع الإلكتروني للـ: CocoaPods أو قراءة مقدمتي لـ: CocoaPods يمكنك أيضاً إضافة كل مكتبة لمشروعك يدوياً إذا كنت تفضل عدم استخدام CocoaPods.

أغلق مشروعك "Close your project" ثم انتقل إلى أصل المشروع، وأنشىء ملف اسمه Podfile افتح Podfile في محرر النصوص المفضل لديك واستبدل محتوياته بما هو ظاهر عندك أدناه.

في pod file الخاص بالمشروع، سوف نقوم بتحديد النظام الأساسي، وهدف الإنتشار، والجرابات "Pods" التى نريد إدراجها في المشروع.

platform :ios, '6.0' pod 'ViewDeck', '~> 2.2.11'pod 'AFNetworking', '~> 1.2.1'pod 'SVProgressHUD', '~> 0.9.0'

  قم بفتح التطبيق Terminal، ثم انتقل إلى أصل المشروع، وقم بتثبيت المكتبات من خلال تنفيذ pod install بالإضافة إلى تثبيت الأغلفة الثلاثة التي حددناها فى ملف الغلاف الخاص بالمشروع. ولقد خلقت لنا CocoaPods بالفعل مساحة للعمل فى X-Code. قم بفتح مساحة العمل الجديدة قبل تنفيذ الأمرOpen Rain.xcworkspace  من سطر الأوامر.

الخطوة 3: إضافة التبعيات
إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس
إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس

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

البنايات. وتعتمد مكتبة AFNetworking على Mobile Core Services  و       

 System Configuration والمكتبة ViewDeck مما يحقق الإستفاة من الإطار 

مساحة العمل Quartz Core حدد الم. حدد المشروع من Project Navigator ثم 

اختر Rain target من قائمة الأهداف، افتح صفحة Build Phases فى الجزء 

العلوي، ثم قم بتوسيع Link Binary With Libraries. اضغط فوق زر (+) كي 

تستطيع ربط مشروعك مع إطارات العمل المذكورة آنفاً سنقوم باستخدام إطار Core 

Location. فى وقت لاحق من هذا المقال، والآن هو الوقت المناسب لربط مشروعك 

الخاص بهذا الإطار، كما فى ( الشكل 3 )

الخطوة 4: تحريرالملفات الرئيسية المترجمة سابقاً
قبل أن نبدأ تنفيذ البنية الأساسية لتطبيق الطقس لدينا، أنها فكرة جيدة لتعديل الملف 

الرئيسي المترجم مسبقاً في مشروعناً. أضف عبارة المضمون لكل من الأطر التي 

أضفناها إلى مشروعنا منذ لحظة وتقوم بفعل نفس الوظيفة بالنسبة للمكتبات 

AFNetworking، SVProgressHUD، وViewDeck.

#import <Availability.h>

#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import <QuartzCore/QuartzCore.h>
    #import <CoreLocation/CoreLocation.h>
    #import <MobileCoreServices/MobileCoreServices.h>
    #import <SystemConfiguration/SystemConfiguration.h>

    #import "AFNetworking.h"
    #import "SVProgressHUD.h"
    #import "IIViewDeckController.h"
#endif

2. وضع الأساسيات
مفهوم وبنية التطبيق سهلة وبسيطة. حيث يدير التطبيق ثلاث وجهات:
 (1) مشهد في المنتصف يبين الأحوال الجوية الحالية لموقع معين، ومشهد على اليمين 

يبين الأحوال الجوية خلال الأيام القليلة المقبلة، ومشهد على اليسار مع قائمة من المواقع.
والبنية الأساسية سوف تقدم لنا إحساساً أفضل بالموضوع بمجرد أن نقوم بتنفيذه.

لإنشاء هذا الهيكل، علينا الإستفادة من ViewDeck library الرائع، حيث يتم إنشاؤه 

وإدارته من قبل Tom Adriaenssen ، وتعتبر مكتبة ViewDeck واحدة من أقوى 

أنماط تصميمات عروض الإنزلاق الأصلية والرائعة في الفيس بوك لدائرة الرقابة 

الداخلية.

الخطوة 1: متحكمات العرض
قبل أن نضع مكتبة ViewDeck للإستخدام، نحن بحاجة إلى إنشاء طبقات متحكم 

العروض التى تدير الثلاث مشاهد التى قد تم توضيحها أعلاه.

قم بإنشاء ثلاثة أقسام فرعية UIViewController تسمى 

 MTWeatherViewController ، MTForecastViewController ، 

و MTLocationsViewController ، على الترتيب (الشكل 4). لا تنسى إنشاء 

واجهة المستخدم أو ملف XIB لكل فئة (الشكل 4).

إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس
إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس

الخطوة 2: إنشاء وحدة تحكم مشاهدة العرض
افتح MTAppDelegate.m ثم قم بإدخال الملفات الرئيسية الخاصه بالثلاث أقسام 

الفرعية، أضف إمتداد الملحق ثم قم بإنشاء خاصية من النوع UIViewController. 

وقم بتسميته viewDeckController والسبب في ذلك سوف يتضح في خلال لحظات.

#import "MTAppDelegate.h" #import "MTWeatherViewController.h"#import "MTForecastViewController.h"#import "MTLocationsViewController.h" @interface MTAppDelegate () @property (strong, nonatomic) IIViewDeckController *viewDeckController; @end

في application:didFinishLaunchingWithOptions:
نبدأ بإنشاء نموذج لكل واحدة من الطبقات الفرعية الثلاث لـ: UIViewController ثم 

نقوم بالبدء في إنشاء نموذج من الطبقة IIViewDeckController ثم تمرير متحكم 

العروض  كبرهان لـ: initWithCenterViewController:leftViewController:rightViewContro

ller 

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

أما عن بقية تنفيذ application:didFinishLaunchingWithOptions فمن 

المؤكد أنه أصبح مألوفاً بالنسبة لك. حيث أننا نقوم بتحديد بداية نافذة التطبيقات ثم 

ضبط متحكم العروض كالتالي:


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    // Initialize View Controllers    MTLocationsViewController *leftViewController = [[MTLocationsViewController alloc] initWithNibName:@"MTLocationsViewController" bundle:nil];    MTForecastViewController *rightViewController = [[MTForecastViewController alloc] initWithNibName:@"MTForecastViewController" bundle:nil];    MTWeatherViewController *centerViewController = [[MTWeatherViewController alloc] initWithNibName:@"MTWeatherViewController" bundle:nil];     // Initialize View Deck Controller    self.viewDeckController = [[IIViewDeckController alloc] initWithCenterViewController:centerViewController leftViewController:leftViewController rightViewController:rightViewController];     // Initialize Window    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];     // Configure Window    [self.window setRootViewController:self.viewDeckController];    [self.window makeKeyAndVisible];     return YES;}

قم ببناء ثم تشغيل التطبيق في برنامج المحاكاه الخاص بـ: ios 
أو على برنامج حسي فيزيائي كي ترى المكتبه ViewDeck وبالرغم من أن كل 

المشاهدات والعروض فى متحكم العروض تكون فارغة تماماً فإن بناء التطبيق الأساسي 

يكون جاهزاً.

3- إضافة الأماكن والمواضع
كما ذكرت فى المقدمة في هذه التذكرة التعليمية سوف نضيف كيفية إضافة المواضع إلى 

قائمة المواضع التى يتم إدارتها عن طريق التطبيق ذاته.
حيث أن الطبقة MTLocationsViewController تكون بمثابة الشاحن الرئيسي 

لإدارة قائمة المواضع وتقديمها فى جدول العروض.

يستطيع المستخدم إضافة الموضع الحالي إلى قائمة المواضع عن طريق الانتقال إلى 

الصف الأول من جدول العروض، الذى سوف يكون مسمى باسم "Add Current 

Location".

إضافة موضع جديد إلى إطار عمل المواضع الأصلي هو مسؤولية الطبقة 

 MTWeatherViewController كما سوف نرى فى خلال بضع دقائق.

ولكن هذا يتركنا مع مشكلة أخرى وهي: كيف سيتم العلم أو الإنذار عن المتحكم في 

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

المواضع؟ مركز الملاحظات والإعلام؟

والتفويض هو الإختيار الأمثل فى هذا السياق.
كلما واجهت الحاجة إلى الاتصال في اتجاه واحد بين كائنين، فإنني أميل إلى إختيار 

الوفد لصالح الإخطارات. حيث أن مندوب البروتوكولات أسهل بكثير كي تدير رأسك 

عنه وإمتداده يكون بصورة بسيطة كإعلان عن طريقة أخرى.

وثمة خيار آخر يتمثل في تمرير إشارة إلى وحدة تحكم عرض الطقس إلى متحكم 

عرض المواقع، ولكن أنا لا أحب هذا النوع من ضيق الاقتران. فضيق الاقتران يجعل 

الكود أقل قابلية لإعادة الاستخدام وأنه يؤدي إلى التسلسل الهرمي المعقد أكثر من اللازم 

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

الخطوة 1: إعلان بروتوكول التفويض
افتح MTLocationsViewController.h وقم بتحديث ملف الهيدر كما هو مبين 

أدناه. نحن بصدد إنشاء خاصية لمفوض متحكم العرض. ونقوم بإعلان بروتوكول 

MTLocationsViewControllerDelegate ويحدد البروتوكول طريقتين، (1) 

controllerShouldAddCurrentLocation ، الذي يتم استدعاؤه حينما يتم شغل 

الصف الأول في جدول العروض الخاص بمتحكم العروض، (2) 

controller:didSelectLocation ، الذي يتم استدعاؤه عند قيام المستخدم بتحديد 

موقع من قائمة المواقع.

#import <UIKit/UIKit.h> @protocol MTLocationsViewControllerDelegate; @interface MTLocationsViewController : UIViewController @property (weak, nonatomic) id<MTLocationsViewControllerDelegate> delegate; @end @protocol MTLocationsViewControllerDelegate <NSObject>- (void)controllerShouldAddCurrentLocation:(MTLocationsViewController *)controller;- (void)controller:(MTLocationsViewController *)controller didSelectLocation:(NSDictionary *)location;@end 

الخطوة 2: إضافة جدول العرض
أعد زيارة MTLocationsViewController.h مرة أخرى. قم بإنشاء منفذ لعرض 

جدول وحدة التحكم وتأكد من مطابقة الطبقة MTLocationsViewController إلى 

كل من البروتوكولات UITableViewDataSource و UITableViewDelegate.

#import <UIKit/UIKit.h> @protocol MTLocationsViewControllerDelegate; @interface MTLocationsViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> @property (weak, nonatomic) id<MTLocationsViewControllerDelegate> delegate; @property (weak, nonatomic) IBOutlet UITableView *tableView; @end @protocol MTLocationsViewControllerDelegate <NSObject>- (void)controllerShouldAddCurrentLocation:(MTLocationsViewController *)controller;- (void)controller:(MTLocationsViewController *)controller didSelectLocation:(NSDictionary *)location;@end 

قم بفتح MTLocationsViewController.xib ، قم بإضافة جدول عرض إلى 

صفحة العروض الخاصة بالمتحكم، وتحديد dataSource الخاص بجدول العرض 

والمنفذ delegate إلى File's Owner object حدد File's Owner object ثم 

قم بتوصيل منفذ tableView الخاص به إلى جدول العرض الذى أضفناه سابقاً إلى 

صفحة متحكم العروض (الشكل 5).

إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس
إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس

الخطوة 3: ملء عرض الجدول
قبل أن تقوم بتنفيذ البروتوكولات UITableViewDataSource 

و UITableViewDelegate ، نحن بحاجة إلى إنشاء خاصية من شأنها أن تكون 

بمثابة مصدر بيانات جدول العرض.

قم بإنشاء ملحق فئوي أو إضافة فئوية في الجزء العلوي من 

MTLocationsViewController.m ثم إنشاء خاصية تسمى لستينص من النوع 

NSMutableArray فإنه سيتم تخزين المواقع التي تم إدارتها بواسطة التطبيق 

الخاص بنا.

#import "MTLocationsViewController.h" @interface MTLocationsViewController () @property (strong, nonatomic) NSMutableArray *locations; @end 

تنفيذ البروتوكولات UITableViewDataSource و UITableViewDelegate 

غير واضحة إلى حد ما. فنحن نبدأ بإعلان سلسلة من الثوابت لمعرف إعادة استخدام 

الخلية.

في طريقة viewDidLoad الخاصة بوحدة تحكم العرض نحن نستدعي 

setupView ، وهي طريقة مساعدة تساعدنا أثناء تكوين واجهة المستخدم الخاصة 

بالمتحكم فى طريقة العرض.

في setupView ، نحن نخبر جدول العروض أن يقوم باستخدام فئة 

UITableViewCell كي يقوم بإنشاء مثيل لجدول العرض الجديد الذى قمنا بتعريفه 

في وقت سابق.

static NSString *LocationCell = @"LocationCell";- (void)viewDidLoad {    [super viewDidLoad];    // Setup View    [self setupView];} - (void)setupView {    // Register Class for Cell Reuse    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:LocationCell];}

تنفيذ بروتوكولات UITableViewDataSource و UITableViewDelegate هو 

شيء تافه وبسيط كما ترون أدناه.
اثنين من تفاصيل تنفيذ تتطلب الشرح قليلاً.
نقوم بتخزين كل موضع على هيئة قاموس له أربعة مفاتيح أو أربعة مداخل، (1) 

المدينة (2) القطر (3) خط العرض (4) خط الطول. 

مع الوضع في الإعتبار أن تنفيذ configureCell:atIndexPath ينبغي أن يصبح 

أكثر وضوحاً. لاحظ أن configureCell: atIndexPath ليس أكثر من طريقة 

أخرى للمساعدة.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {    return 1;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {    return ([self.locations count] + 1);}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:LocationCell forIndexPath:indexPath];    // Configure Cell    [self configureCell:cell atIndexPath:indexPath];    return cell;}- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {    if (indexPath.row == 0) {        [cell.textLabel setText:@"Add Current Location"];    } else {        // Fetch Location        NSDictionary *location = [self.locations objectAtIndex:(indexPath.row - 1)];        // Configure Cell        [cell.textLabel setText:[NSString stringWithFormat:@"%@, %@", location[@"city"], location[@"country"]]];    }}- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {    return NO;}- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {    return NO; }
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {    [tableView deselectRowAtIndexPath:indexPath animated:YES];     if (indexPath.row == 0) {        // Notify Delegate        [self.delegate controllerShouldAddCurrentLocation:self];     } else {        // Fetch Location        NSDictionary *location = [self.locations objectAtIndex:(indexPath.row - 1)];         // Notify Delegate        [self.delegate controller:self didSelectLocation:location];    }     // Show Center View Controller    [self.viewDeckController closeLeftViewAnimated:YES];

و tableView: didSelectRowAtIndexPath يتطلب أيضاً شرحاً موجزاً.
 إذا كان المستخدم قد استغل الصف الأول، والمكتوب عليه Add Current Location  
، يتم إعلام المندوب إلى أن الموقع الحالي ينبغي أن يضاف إلى قائمة المواقع. إذا تم 

استغلال أي صف آخر في جدول العرض، يتم تمرير الموقع المناظر كما الوسيطة 

الثانية من controller:didSelectLocation

طريقة تفويض أخرى من بروتوكول المفوض MTLocationsViewController 

حيث يقوم ذلك بتوجيه نظر المندوب أو المفوض إلى أن التطبيق يجب أن يقوم بتحديد 

موقع جديد كموقع application't الافتراضي وبيانات الطقس لهذا الموقع يجب أن 

تكون سهلة المنال.

السطر الأخير من tableView: didSelectRowAtIndexPath هو أيضاً جدير 

بالذكر.
على سبيل المثال IIViewDeckController يقوم بتحديد نفسه إلى متحكمات العرض 

التي يديرها. توفر خاصية viewDeckController الوصول إلى وحدة تحكم العرض 

، وهو مناسب إذا كنت بحاجة إلى الوصول إلى صفحة متحكم العروض.

فهو يعمل بشكل يشبه كثيراً خاصية نافيجاتيونسونتروللير من نموذج متحكمات العرض 

. في السطر الأخير من tableView: didSelectRowAtIndexPath قد أخبرنا 

متحكم العرض المزخرف بإغلاق العرض الأيسر، وهو ما يعني أن المشهد أو العرض 

الأوسط يصبح مرئياً مرة أخرى.

الخطوة 4: مفاتيح وثوابت
قبل أن نواصل ملء جدول عرض المواقع، نحن بحاجة إلى إيلاء الإهتمام لبعض أفضل 

الممارسات. نحن نستخدم حالياً سلسلة حرفية للوصول إلى قيم قاموس الموقع. على 

الرغم من أن هذا يعمل بشكل جيد تماماً، فمن الأفضل وأكثر أماناً استخدام ثوابت خيطية 

لهذا الغرض. لجعل هذا كله بسيطاً وسهل الصيانة والتعديل، فإننا نعرف ثوابت خيطية 

في الموقع الأوسط. اسمحوا لي أن أبين لكم كيف يعمل هذا.

قم بإنشاء فئة فرعية من NSObject وقم بتسميتها MTConstants قم باستبدال 

محتويات MTConstants.h و MTConstants.m كما هو مبين أدناه. يجب أن 

يكون واضحاً لك أن MTConstants ليس فئة Objective-C أنها ليست أكثر من

مكاناً مركزياً لتخزين مجموعة من الثوابت المحددة لمشروعنا.


#pragma mark -#pragma mark User Defaultsextern NSString * const MTRainUserDefaultsLocation;extern NSString * const MTRainUserDefaultsLocations; #pragma mark -#pragma mark Notificationsextern NSString * const MTRainDidAddLocationNotification;extern NSString * const MTRainLocationDidChangeNotification; #pragma mark -#pragma mark Location Keysextern NSString * const MTLocationKeyCity;extern NSString * const MTLocationKeyCountry;extern NSString * const MTLocationKeyLatitude;extern NSString * const MTLocationKeyLongitude;

#import "MTConstants.h" #pragma mark -#pragma mark User DefaultsNSString * const MTRainUserDefaultsLocation = @"location";NSString * const MTRainUserDefaultsLocations = @"locations"; #pragma mark -#pragma mark NotificationsNSString * const MTRainDidAddLocationNotification = @"com.mobileTuts.MTRainDidAddLocationNotification";NSString * const MTRainLocationDidChangeNotification = @"com.mobileTuts.MTRainLocationDidChangeNotification"; #pragma mark -#pragma mark Location KeysNSString * const MTLocationKeyCity = @"city";NSString * const MTLocationKeyCountry = @"country";NSString * const MTLocationKeyLatitude = @"latitude";NSString * const MTLocationKeyLongitude = @"longitude";

لجعل MTConstants مفيدة حقاً، وإضافة عبارة الاستيراد لـ: MTConstants.h إلى
الملف الرئيسى المشغل سابقاً الخاص بمشروعك لذلك فإن الثوابت المعلنة في 

MTConstants تتوفر في جميع مراحل المشروع.

#import <Availability.h> #ifndef __IPHONE_3_0#warning "This project uses features only available in iOS SDK 3.0 and later."#endif #ifdef __OBJC__    #import <UIKit/UIKit.h>    #import <Foundation/Foundation.h>    #import <QuartzCore/QuartzCore.h>    #import <CoreLocation/CoreLocation.h>    #import <MobileCoreServices/MobileCoreServices.h>    #import <SystemConfiguration/SystemConfiguration.h>     #import "AFNetworking.h"    #import "SVProgressHUD.h"    #import "IIViewDeckController.h"     #import "MTConstants.h"#endif

يمكننا الآن تحديث configureCell: atIndexPath

(MTLocationsViewController.m) كما هو مبين أدناه. فهذا ليس فقط سوف 

يعطينا كوداً مكتملاً، وأداءاً سريعاً وبسيطاً، بل أن الإستفادة الحقيقية من هذه الممارسات 

 هي أن هذا البرنامج سوف يحذرنا في حالة الأخطاء المطبعية. أنا متأكد أنه لا يجب أن 

أخبركم بأن الأخطاء المطبعية هي واحدة من الأسباب الأكثر شيوعاً من الأخطاء في 

تطوير البرمجيات.

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {    if (indexPath.row == 0) {        [cell.textLabel setText:@"Add Current Location"];     } else {        // Fetch Location        NSDictionary *location = [self.locations objectAtIndex:(indexPath.row - 1)];         // Configure Cell        [cell.textLabel setText:[NSString stringWithFormat:@"%@, %@", location[MTLocationKeyCity], location[MTLocationKeyCountry]]];    }}

في الوقت الراهن، الخاصية locations فارغة وهكذا سوف يكون جدول العرض. في   

initWithNibName:bundle نحن نستدعى loadLocations كطريقة مساعدة 

 تقوم بتحميل مجموعة من المواقع والمواضع. 

في loadLocations ، قم بتحميل مجموعة من المواقع التي يتم تخزينها في قاعدة 

البيانات الخاصه بمستخدم التطبيق. لاحظ أننا نستخدم ثابت خيطي أخر قد أعلنا عنه في 

MTConstants.h

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];     if (self) {        // Load Locations        [self loadLocations];    }     return self;}
- (void)loadLocations {    self.locations = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:MTRainUserDefaultsLocations]];}

خطوة 5: تعيين مندوب (مفوض)
كما ذكرت سابقاً، فإن المثال MTWeatherViewController بمثابة مفوض مواضع 

متحكم العروض.

أعد زيارة MTAppDelegate.m وقم بتحديث التطبيق: 

didFinishLaunchingWithOptions كما هو مبين أدناه.


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    // Initialize View Controllers    MTLocationsViewController *leftViewController = [[MTLocationsViewController alloc] initWithNibName:@"MTLocationsViewController" bundle:nil];    MTForecastViewController *rightViewController = [[MTForecastViewController alloc] initWithNibName:@"MTForecastViewController" bundle:nil];    MTWeatherViewController *centerViewController = [[MTWeatherViewController alloc] initWithNibName:@"MTWeatherViewController" bundle:nil];     // Configure Locations View Controller    [leftViewController setDelegate:centerViewController];     // Initialize View Deck Controller    self.viewDeckController = [[IIViewDeckController alloc] initWithCenterViewController:centerViewController leftViewController:leftViewController rightViewController:rightViewController];     // Initialize Window    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];     // Configure Window    [self.window setRootViewController:self.viewDeckController];    [self.window makeKeyAndVisible];     return YES;}

قبل إجراء هذا التغيير، يجب أن يظهر تحذير مفاجىء وفوري يخبرك بأن 

 MTWeatherViewController لا يتوافق مع بروتوكول 

MTLocationsViewControllerDelegate

البرنامج المترجم على حق لذلك دعونا نقوم بإصلاح هذا.
افتح MTWeatherViewController.h ثم احصل على الملف الرئيسى لـ: 

 MTLocationsViewController ، وطابق MTWeatherViewController 

لبروتوكول MTLocationsViewControllerDelegate

#import <UIKit/UIKit.h> #import "MTLocationsViewController.h" @interface MTWeatherViewController : UIViewController <MTLocationsViewControllerDelegate> @end

انتظر.. هل من تحذير آخر؟ نحن لم ننفذ الطريقتين المطلوبتين لـ: بروتوكول التفويض 

حتى الآن. افتح MTWeatherViewController.m وقم بإضافة تنفيذ لكل من طرق 

التفويض.

- (void)controllerShouldAddCurrentLocation:(MTLocationsViewController *)controller {    NSLog(@"%s", __PRETTY_FUNCTION__);} - (void)controller:(MTLocationsViewController *)controller didSelectLocation:(NSDictionary *)location {    NSLog(@"%s", __PRETTY_FUNCTION__);}

خطوة 6: جلب الموقع الحالي
حان الوقت أخيراً لجلب الموقع الحالي للجهاز. أضف إمتداد الفئة أو الملحق الفئوي في 

الجزء العلوي من MTWeatherViewController.m ثم أعلن عن خاصيتين، (1)   

    location (NSDictionary) لتخزين الموقع الافتراضي للتطبيق و(2) 

locationManager (CLLocationManager)، سوف نستخدمه لجلب موقع 

الجهاز.

 قم بمطابقة الفئة MTWeatherViewController للبروتوكول 

CLLocationManagerDelegate وعرف متغيرأخر اسمه _locationFound 

من نوع BOOL والغرض من _locationFound سوف يصبح واضحاً في خلال 

بضع دقائق.

#import "MTWeatherViewController.h" @interface MTWeatherViewController () <CLLocationManagerDelegate> {    BOOL _locationFound;} @property (strong, nonatomic) NSDictionary *location; @property (strong, nonatomic) CLLocationManager *locationManager; @end

فى المهيىء المحدد للفئة، initWithNibName:bundle سوف نقوم بتهيئة وتكوين 

مدير الموضع. نحن نحدد متحكم العروض كما لو كان مفوض مدير الموقع وتحديد 

خاصية accuracy الخاصه بمدير الموقع إلى 

kCLLocationAccuracyKilometer


ليست هناك حاجة إلى دقة أفضل فنحن فقط بحاجة إلى موقع لبيانات الطقس.

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];     if (self) {        // Initialize Location Manager        self.locationManager = [[CLLocationManager alloc] init];         // Configure Location Manager        [self.locationManager setDelegate:self];        [self.locationManager setDesiredAccuracy:kCLLocationAccuracyKilometer];    }     return self;}

الجزء التالي من اللغز قيد التنفيذ locationManager: didUpdateLocations ، 

إحدى الطرق للبروتوكول CLLocationManagerDelegate ، الذي يتم استدعاؤه 

في كل مرة مقوم فيها مدير الموقع بعمل تحديث لـ: موقع الأجهزة.

المرحلة الثانية من locationManager: didUpdateLocations هي نماذج من 

CLLocation

وتنفيذ locationManager: didUpdateLocations يكشف أيضاً عن الغرض 

من _locationFound
بالرغم من حقيقة أننا نخبر مدير المواقع أن يقوم بوقف تحديث الموقع بمجرد أن يتم 

استدعاء locationManager: didUpdateLocations ، فإنه ليس من غير 

المألوف أن يقوم تحديث أخر للموقع باستدعاء locationManager 

didUpdateLocations: مرة أخرى حتى بعد إرسال مدير الموقع رسالة من 

stopUpdatingLocation

 إذا حدث هذا، سوف نضيف نفس الموقع مرتين إلى قائمة المواقع.

الحل البسيط هو استخدام متغير مساعد، _locationFound حيث أنه سيحافظ على 

المسارالذي نحن فيه.

 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {    if (![locations count] || _locationFound) return;     // Stop Updating Location    _locationFound = YES;    [manager stopUpdatingLocation];     // Current Location    CLLocation *currentLocation = [locations objectAtIndex:0];     // Reverse Geocode    CLGeocoder *geocoder = [[CLGeocoder alloc] init];    [geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {        if ([placemarks count]) {            _locationFound = NO;            [self processPlacemark:[placemarks objectAtIndex:0]];        }    }];}

نحن نستخرج الموقع الأول من مجموعة المواقع ونستخدم فئة CLGeocoder لعكس 

الترميز الجغرافي لهذا الموقع.

الترميز الجغرافي العكسي يعني ببساطة معرفة اسم (أقرب) مدينة والبلد التى يقع فيها 

الموقع.

معالج الإتمام من reverseGeocodeLocation يقوم بإرجاع مجموعة من 

العلامات الموضعية. والهدف من العلامات الموضعية ببساطة ليس أكثر من وعاء 

لتخزين بيانات الموقع للتنسيق.

- (void)processPlacemark:(CLPlacemark *)placemark {    // Extract Data    NSString *city = [placemark locality];    NSString *country = [placemark country];    CLLocationDegrees lat = placemark.location.coordinate.latitude;    CLLocationDegrees lon = placemark.location.coordinate.longitude;     // Create Location Dictionary    NSDictionary *currentLocation = @{ MTLocationKeyCity : city,                                       MTLocationKeyCountry : country,                                       MTLocationKeyLatitude : @(lat),                                       MTLocationKeyLongitude : @(lon) };     // Add to Locations    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];    NSMutableArray *locations = [NSMutableArray arrayWithArray:[ud objectForKey:MTRainUserDefaultsLocations]];    [locations addObject:currentLocation];    [locations sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:MTLocationKeyCity ascending:YES]]];    [ud setObject:locations forKey:MTRainUserDefaultsLocations];     // Synchronize    [ud synchronize];     // Update Current Location    self.location = currentLocation;     // Post Notifications    NSNotification *notification2 = [NSNotification notificationWithName:MTRainDidAddLocationNotification object:self userInfo:currentLocation];    [[NSNotificationCenter defaultCenter] postNotification:notification2];}

في processPlacemark ، نحن نقوم باستخراج البيانات التي نبحث عنها، وهي 

المدينة، البلد، خط العرض، وخط الطول، وحفظها في القاموس، وتحديث مجموعة من 

المواقع في قاعدة البيانات الافتراضية للمستخدم للتطبيق. 

لاحظ أننا قمنا بفرز مجموعة من المواقع قبل تحديث قاعدة البيانات الافتراضية 

للمستخدم. حيث يتم تحديث خاصية location الخاصة بمتحكم العروض مع الموقع 

الجديد ويتم إرسال إشعار يخطر أي كائن مهتم بهذا الحدث.

هذا ليس كل شيء، فقد تجاوزنا أيضاً خاصية location الخاصة بمتحكم العروض.
 لأن فئة MTWeatherViewController هي المسؤولة عن إضافة مواقع جديدة، 

يمكننا تفويض بضع مسؤولين إضافيين لهذه الفئة، مثل تحديث الموقع الافتراضي في 

قاعدة البيانات الافتراضية للمستخدم.
لأن أجزاء أخرى من التطبيق تحتاج أيضاً إلى معرفة التغير في الموقع، ويتم نشر 

إشعار باسم MTRainLocationDidChangeNotification

نحن أيضاً نقوم باستدعاء updateView أسلوب مساعد آخر سننفذه قريباً.

- (void)setLocation:(NSDictionary *)location {    if (_location != location) {        _location = location;         // Update User Defaults        NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];        [ud setObject:location forKey:MTRainUserDefaultsLocation];        [ud synchronize];         // Post Notification        NSNotification *notification1 = [NSNotification notificationWithName:MTRainLocationDidChangeNotification object:self userInfo:location];        [[NSNotificationCenter defaultCenter] postNotification:notification1];         // Update View        [self updateView];    }}

خطوة 7: إضافة تسمية
نحن لن نقضي الكثير من الوقت على واجهة المستخدم في هذا البرنامج التعليمي، ولكن 

للتأكد من أن كل شيء يعمل كما نتوقع، فمن الجيد أن يكون بعض التغذيات العكسية 

البصرية بإضافة تسمية معينة إلى صفحة متحكم العروض الخاصة بالطقس لعرض 

الموقع المحدد.
قم بفتح MTWeatherViewController.h وأنشئ منفذاً من نوع UILabel وقم 

بتسميته labelLocation


#import <UIKit/UIKit.h> #import "MTLocationsViewController.h" @interface MTWeatherViewController : UIViewController <MTLocationsViewControllerDelegate> @property (weak, nonatomic) IBOutlet UILabel *labelLocation; @end

افتح MTWeatherViewController.xib ، قم بإضافة تسمية لصفحة متحكم 

العروض، وقم بتوصيل المنفذ مع التسمية المضافة حديثاً (الشكل 6).

في updateView (MTWeatherViewController.m) ، نقوم بتحديث التسمية 

مع الموقع الجديد كما هو موضح أدناه.

إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس
إعداد مشروع إنشاء تطبيق مناخي مع توقع الطقس

خطوة 8: تنفيذ بروتوكول التفويض
وبفضل العمل الذي قمنا به حتى الآن، فإن تنفيذ اثنين من طرق بروتوكول 

MTLocationsViewControllerDelegate أصبح أمراً سهلاً.
في controllerShouldAddCurrentLocation ، نخبر مدير الموقع أن يبدأ 

بتحديث الموقع.
في controller:didSelectLocation ، نحن نقوم بضبط خاصية location 

الخاصة بمتحكم العروض إلى الموقع الذى قام المستخدم باختياره فى متحكم عرض 

المواقع، والذي بدوره يقوم باستدعاء الطريقة الابتدائية التى تجاهلناها قليلاً مؤخراً.

4. اللمسات النهائية
قبل اختتام الدفعة الأولى من هذه السلسلة، نحن بحاجة إلى إضافة بعض اللمسات النهائية.

 عندما يطلق المستخدم هذا التطبيق، على سبيل المثال، فإن خاصية location للفئة 

MTWeatherViewController تحتاج لتعيينها فى الموقع الذى تم تخزينه في 

التطبيق الافتراضي للمستخدم. بالإضافة إلى ذلك، عندما يقوم المستخدم بإطلاق تطبيقنا 

للمرة الأولى، لم يتم تعيين موقع افتراضي بعد خلفية تطبيق المستخدم. هذه ليست 

مشكلة كبيرة، ولكن لتقديم خبرة جيدة للمستخدم فمن الأفضل أن يتم جلب الموقع الحالي 

تلقائياً للمستخدم عندما يطلق التطبيق للمرة الأولى.

يمكننا القيام بالعديد من التغيرات من خلال تعديل viewDidLoad الخاص بوحدة 

تحكم عرض الطقس كما هو موضح أدناه.

إن خاصية موقع متحكم العروض يتم تعيينها وفق قاعدة البيانات الافتراضية الخاصة 

بالمستخدم.

إذا لم يوجد أي موقع، فهذا هو self.location ويكون nil ، ونحن نخبر مدير الموقع 

أن يقوم ببدء تحديث الموقع. أو بعبارة أخرى، عندما يتم تشغيل التطبيق لأول مرة، يتم 

استرداد الموقع الحالي وتخزينه تلقائياً.

هناك نهاية ملحة بأننا نحتاج إلى عملية ربط. 
فعند إضافة موقع جديد لمجموعة من المواقع، فإن متحكم عرض الطقس ينشر لنا 

إشعاراً بأننا في حاجة إلى تحديث الفئة MTLocationsViewController بحيث 

يضيف نفسه كمراقب وملاحظ لهذه الإخطارات.

من خلال القيام بذلك، فإن متحكم عرض المواقع يمكنه تحديث طريقة جدول العرض 

الخاص به كلما تمت إضافة موقع جديد.

أعد زيارة MTLocationsViewController.m وقم بتحديث 

 initWithNibName:bundle كما هو مبين أدناه.
أضفنا متحكم العروض كمراقب للإخطارات بإسم 

MTRainDidAddLocationNotification
حيث أن تنفيذ didAddLocation يكون واضحاً ومباشراً، ومن هنا يمكن أن نضيف 

 الموقع الجديد لمجموعة المواقع، أفرز تلك المجموعة بالمدينة، وأعد تحميل جدول 

العرض.

لا تنسى إزالة وحدة تحكم العرض كمراقب في طريقة dealloc. 
بل هو أيضاً ممارسة جيدة لتعيين الخاصية delegate الخاصة بوحدة التحكم في 

العرض إلى nil في dealloc.

قم ببناء وتشغيل التطبيق لترى كيف يعمل كل هؤلاء معاً.

 قد ترغب في تشغيل التطبيق في محاكي دائرة الرقابة الداخلية  بدلاً من تشغيله على 

جهاز فعلي، لأن محاكي دائرة الرقابة الداخلية يدعم محاكاة المواقع (الشكل 7)، الأمر 

الذي يجعل من السهل جداً اختبار التطبيقات القائمة على الموقع مثل هذا الذي قد أنشأناه.

الخلاصة:
على الرغم من أننا لم نقم بتشغيل Forecast API حتى الآن، فقد قمنا بقدر كبير من 

العمل في هذه المقالة.

 آمل أن تكون قد حاولت عمل CocoaPods وأن تكون مقتنعاً بمدى قوته ومرونته.

في الدفعة القادمة من هذه السلسلة، سوف نركز على Forecast API ومكتبة 

AFNetworking


إلى اللقاء فى الدرس القادم إن شاء الله.

الكــاتــب

    • مشاركة

ليست هناك تعليقات:

جميع الحقوق محفوظة لــ الشبح للمعلوميات 2019 ©