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

آخر المواضيع

بناء تطبيق الطقس مع التنبؤ بحالة الطقس كواجهة للمستخدم

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

شكل(1) تصميم كريس كاري
وبناء على التخصيص المحدود لل ULKIT، سنكون مضطرين لعمل بعض التعديلات على تصميم كريس. ومع ذلك النتيجة النهائية ستكون مثل التصميم الذي أنشأه كريس. تخصيص UIKit's UISwitch class، على سبيل المثال محدود جدا، وسنقوم باستخدام طريقة أخرى لتنفيذ الجزئية المسؤولة عن تحديد درجة الحرارة. استخدم كريس نوعين من الخطوط في تصميمه Maven Pro و Mission Gothic وعلى الرغم من ذلك IOS تدعم خطوط خاصة والخطوط التي سنستخدمها ستكون مدعومة من قبل ال IOS.
1- الإخطاراتالخطوة الأولى: تعريف الثوابتبمجرد استلام ال controller بيانات الطقس من تطبيق التنبؤ بحالة الطقس، ال view الخاص به يحتاج للتعديل. وهذا يعني أننا بحاجة لإشعار forecast view controller وإرسال بيانات الطقس له.
هناك العديد من الطرق لإشعار forecast view controller بهذا الحدث. إرسال تنبيه من خلال استخدام NSNotificationCenter هو الحل الأبسط ويعطينا المرونة الكافية. ونستطيع استخدام نفس الطريقة لعمل تعديل واجهة المستخدم بعد أن يقوم المستخدم بتعديل إعدادات درجة الحرارة. عند إرسال تنبيه، كل object له علاقة بهذا الحدث يستطيع تسجيل نفسه كمراقب. لقد قمت بالتعديل على MTConstants.h/. لإنشاء ثابت ليحتوي نص كل إشعار
extern NSString * const MTRainWeatherDataDidChangeChangeNotification;extern NSString * const MTRainTemperatureUnitDidChangeNotification;

NSString * const MTRainWeatherDataDidChangeChangeNotification = @"com.mobileTuts.MTRainWeatherDataDidChangeChangeNotification";
NSString * const MTRainTemperatureUnitDidChangeNotification = @"com.mobileTuts.MTRainTemperatureUnitDidChangeNotification";

الخطوة الثانية: متحكم واجهة الطقسبعد استلام رد من تطبيق التنبؤ بحالة الطقس نحتاج لتخزين هذا الرد لاستخدامه لاحقاً في تعديل العرض. قم بإنشاء خاصيتين private  في MTWeatherViewController.m:·Response من نوع NSDictionary والتي سنخزن فيها الرد من تطبيق التنبؤ بحالة الطقس.·Forecast من نوع NSArray والتي ستقوم بحفظ جزء من البيانات وهو بيانات الطقس للساعات القادمة.

#import "MTWeatherViewController.h" #import "MTForecastClient.h" @interface MTWeatherViewController () <CLLocationManagerDelegate> {    BOOL _locationFound;} @property (strong, nonatomic) NSDictionary *location;@property (strong, nonatomic) NSDictionary *response;@property (strong, nonatomic) NSArray *forecast; @property (strong, nonatomic) CLLocationManager *locationManager; @end
لتلقي الإخطارات نحتاج إلى تعديل initWithNibName:bundle:   كما هو موضح في الأسفل (MTWeatherViewController.m). في الملف weatherDataDidChangeChange
نقوم بتخزين  الرد من تطبيق التنبؤ بحالة الطقس في المتغيرات response  و  forecast  ونقوم بالتعديل على العرض. في الملف temperatureUnitDidChangeنحتاج فقط التعديل على ال view أو طريقة العرض ليعكس الإعداد الذي تم تغييره.
- (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]; // Add Observer NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; [nc addObserver:self selector:@selector(reachabilityStatusDidChange:) name:MTRainReachabilityStatusDidChangeNotification object:nil]; [nc addObserver:self selector:@selector(weatherDataDidChangeChange:) name:MTRainWeatherDataDidChangeChangeNotification object:nil]; [nc addObserver:self selector:@selector(temperatureUnitDidChange:) name:MTRainTemperatureUnitDidChangeNotification object:nil]; } return self;}
- (void)weatherDataDidChangeChange:(NSNotification *)notification {
// Update Response & Forecast
[self setResponse:[notification userInfo]];
[self setForecast:self.response[@"hourly"][@"data"]];

// Update View
[self updateView];
}
- (void)temperatureUnitDidChange:(NSNotification *)notification {
// Update View
[self updateView];
}

الخطوة الثالثة: عرض متحكم تطبيق التنبؤ بالطقس 

الخطوات المتبعة تقريبا متماثلة مع ما قمنا به في MTForecastViewController class. قمنا بالتعديل على initWithNibName:bundle: كما هو موضح بالأسفل وإنشاء الخاصيتين response و forecast 
وقمنا ببناء weatherDataDidChangeChangeو temperatureUnitDidChange:

الاختلاف هنا في بيانات الطقس المخزنة في  forecast. سنقوم ببناء updateView لاحقاً في هذا المقال


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Add Observer NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(weatherDataDidChangeChange:) name:MTRainWeatherDataDidChangeChangeNotification object:nil]; [nc addObserver:self selector:@selector(temperatureUnitDidChange:) name:MTRainTemperatureUnitDidChangeNotification object:nil]; } return self;}

#import "MTForecastViewController.h" @interface MTForecastViewController () @property (strong, nonatomic) NSDictionary *response;@property (strong, nonatomic) NSArray *forecast; @end
- (void)weatherDataDidChangeChange:(NSNotification *)notification {
// Update Response & Forecast
[self setResponse:[notification userInfo]];
[self setForecast:self.response[@"daily"][@"data"]];
// Update View
[self updateView];
}
- (void)temperatureUnitDidChange:(NSNotification *)notification {
// Update View
[self updateView];
}
- (void)updateView {
}

الخطوة الأولى: المنافذ والإجراءات 

على الرغم من أن عرض المركز يحتوي على الكثير من المعلومات، إلا أنه ليس من الصعب بناؤه. دعونا نبدأ بإنشاء عدد من المنافذ وإجراء واحد جديد.قم بالتعديل على MTWeatherViewController.h  كما هو موضح في الأسفل. وقم بالاطلاع مرة أخري على التصميم الخاص بكريس لتفهم أكثر الموقع والغرض من كل عنصر في واجهة المستخدم. وبالمقارنة مع تصميم كريس نجد أن الفرق هو أننا سوف نستبدل رمز التقويم في أعلى اليمين مع زر التحديث الذي أنشأنا في البرنامج التعليمي السابق. وستقدم بيانات الطقس للساعات القادمة في عرض المجموعة، وهو ما يعني أن الطبقة MTWeatherViewController تحتاج لتتوافق مع بروتوكولات UICollectionViewDataSource، UICollectionViewDelegate ، وUICollectionViewDelegateFlowLayout.

#import <UIKit/UIKit.h> #import "MTLocationsViewController.h" @interface MTWeatherViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, MTLocationsViewControllerDelegate> @property (weak, nonatomic) IBOutlet UIButton *buttonLocation;@property (weak, nonatomic) IBOutlet UIButton *buttonRefresh; @property (weak, nonatomic) IBOutlet UILabel *labelDate;@property (weak, nonatomic) IBOutlet UILabel *labelTemp;@property (weak, nonatomic) IBOutlet UILabel *labelTime;@property (weak, nonatomic) IBOutlet UILabel *labelWind;@property (weak, nonatomic) IBOutlet UILabel *labelRain;@property (weak, nonatomic) IBOutlet UILabel *labelLocation; @property (weak, nonatomic) IBOutlet UICollectionView *collectionView; @end
كما نحتاج إلى إضافة حدث على زر الموقع في أعلى اليسار. قم بفتح MTWeatherViewController.m  وقم بإضافة الحدث الجديد المسمى openLeftView:. وفيه نقوم ببرمجة أنه يجب أن تبدل وحدة تحكم طريقة العرض لليسار. وتذكر أنه من الممكن أن يبدل من اليسار لليمين لفتح العرض على اليسار
- (IBAction)openLeftView:(id)sender {    [self.viewDeckController toggleLeftViewAnimated:YES];}

الخطوة الثانية: إنشاء واجهة المستخدم

قم بفتح 
MTWeatherViewController.xib  كما وقم بإنشاء واجهة المستخدم كما هي مبينة في الشكل 2. أثناء بناء واجهة المستخدم من المهم التأكد أن واجهة المستخدم تعرض بطريقة صحيحة على 3.5" screen و iPhone 5's 4. تستطيع اختبار هذا باختيار وحدة تحكم العرض وتغيير سمة الحجم من Attributes Inspector.
لتحقيق النتيجة المرجوة، تحتاج إلى  تعديل قيود التخطيط التلقائي من عناصر واجهة المستخدم. الهدف هو جعل بيانات الطقس تلتصق في الجزء العلوي من العرض، في نفس الوقت يتم لصقها على عرض المجموعة إلى أسفل. الرموز بجانب الوقت، العلامات الرياح، والأمطار هي instances من UIImageView.


تكوين التسميات والأزرار كما هو مبين في الشكل 2. وهذا يشمل التعديل المناسب لنص التسميات، وتحديد أنواع الزر إلى مخصص Custom ، بجعل مالك الملفات dataSourceو delegate و تحديد اتجاه شريط التمرير بشكل أفقي. أنا من محبي Gill Sans ولذلك  كان هذا نوع  الخط الي اخترته لهذا المشروع. قبل الرجوع إلى ملف بناء وحدة تحكم العرض للطقس، اشبك المنافذ والحدث الذي قمنا بانشاؤه مسبقاً. بالإضافة إلى التسميات والأزرار قمت أيضاً بإضافة صورة إلى عرض وحدة تحكم العرض ليتم عرضها كصورة خلفية
 كما ذكرت في المقدمة، يمكنك أن تجد الأعمال الفنية للتطبيق في ملفات ال source لهذا البرنامج التعليمي. أنشئ مجلد باسم Artwork  في مشروع Xcode  واسحب العمل الفني في هذا المجلد.

الخطوة الثالثة: ملء شاشة المستخدم

نحن حاليا نقوم بتسجيل رد تطبيق وحدة التنبؤ بالطقس 
Forecast API في وحدة تحكم ال  Xcode. للبدء في استخدام بيانات الطقس نحتاج لتعديلmethod fetchWeatherData كما هو موضح بالأسفل في ال completion block requestWeatherForCoordinate:completion: نقوم بإخفاء HUD التقدم وإرسال إشعار على الموضوع الرئيسي ونقوم باستخدام dispatch_async function وتمرير the queue من الموضوع الرئيسي كوسيط 
argument. قاموس الاشعارات userInfo هو الرد على الطلب
- (void)fetchWeatherData {    if ([[MTForecastClient sharedClient] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) return;     // Show Progress HUD    [SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeGradient];     // Query Forecast API    double lat = [[_location objectForKey:MTLocationKeyLatitude] doubleValue];    double lng = [[_location objectForKey:MTLocationKeyLongitude] doubleValue];    [[MTForecastClient sharedClient] requestWeatherForCoordinate:CLLocationCoordinate2DMake(lat, lng) completion:^(BOOL success, NSDictionary *response) {        // Dismiss Progress HUD        [SVProgressHUD dismiss];         if (response && [response isKindOfClass:[NSDictionary class]]) {            dispatch_async(dispatch_get_main_queue(), ^{                // Post Notification on Main Thread                NSNotification *notification = [NSNotification notificationWithName:MTRainWeatherDataDidChangeChangeNotification object:nil userInfo:response];                [[NSNotificationCenter defaultCenter] postNotification:notification];            });        }    }];}

وحدة تحكم الطقس والتنبؤ بحالته هما عبارة عن مراقبين لإشعارات 
MTRainWeatherDataDidChangeChangeNotification
وحدة تحكم عرض الطقس تستدعي weatherDataDidChangeChange: والتي تستدعي بدورها updateView والتي نستدعي فيها updateCurrentWeather ونقوم بتحديث العرض بارسال رسالة من reloadDat
- (void)updateView {    // Update Location Label    [self.labelLocation setText:[self.location objectForKey:MTLocationKeyCity]];     // Update Current Weather    [self updateCurrentWeather];     // Reload Collection View    [self.collectionView reloadData];}
قبل أن تنفذ updateCurrentWeather ، سيتم عرض قيم درجة الحرارة في أماكن مختلفة من التطبيق، ولأن التطبيق يدعم كل من قياس الفهرنهايت والسلزيوس فقد تصبح هذه العملية مرهقة. ولذلك فمن المفيد إنشاء class تركز على هذا بحيث لا نحتاج لتعقيد الكود أكثر بالجمل الشرطية وتحويلات درجات الحرارة من قياس لاخر.
الخطوة الرابعة: إنشاء class خاص بالإعدادات:
قبل أن إنشاء الclass الذي يعالج تحويلات درجة الحرارة، نحن بحاجة إلى تخزين درجة الحرارة الحالية في قاعدة البيانات الرئيسية للمستخدم. أعد النظر إلى MTConstants.h / .M وقم بتعريف ثابت اسمه ofMTRainUserDefaultsTemperatureUnit.
extern NSString * const MTRainUserDefaultsTemperatureUnit;

NSString * const MTRainUserDefaultsTemperatureUnit = @"temperatureUnit";
لجعل العمل مع الإعدادات أسهل، أقوم غالبا بإنشاء  فئة على NSUserDefaults والتي تسمح لي بسرعة وبشكل رائع للوصول إلى إعدادات التطبيق. اسمحوا لي أن أوضح ما أعنيه. أنشئ Objective-C category  جديدة (كما بالشكل 3)، وقم بتسميتها Helpers واجعلها فئة من NSUserDefaults (كما بالشكل 4)
في NSUserDefaults+Helpers.h نقوم بتعريف ثلاث methods كما هو موضح بالأسفل



#import <Foundation/Foundation.h> @interface NSUserDefaults (Helpers) #pragma mark -#pragma mark Temperature+ (BOOL)isDefaultCelcius;+ (void)setDefaultToCelcius;+ (void)setDefaultToFahrenheit; @end
على الرغم من أن هذه الأساليب ليست سحرية، إلا
 أنها مفيدة جدا. ال method الأولى، isDefaultCelcius، يخبرنا إذا تم تعيين وحدة درجة الحرارة إلى درجة مئوية أو لا. طريقتين أخرى تجعل من السهل جدا للتبديل بين فهرنهايت مئوية. ليس فقط لا نقوم بتحديث قاعدة البيانات التخلف المستخدم، ونحن أيضا نرسل اشعار ليخبر المراقبين بالتغيير الحاصل.

+ (BOOL)isDefaultCelcius {    return [[NSUserDefaults standardUserDefaults] integerForKey:MTRainUserDefaultsTemperatureUnit] == 1;} + (void)setDefaultToCelcius {    // Update User Defaults    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];    [ud setInteger:1 forKey:MTRainUserDefaultsTemperatureUnit];    [ud synchronize];     // Post Notification    [[NSNotificationCenter defaultCenter] postNotificationName:MTRainTemperatureUnitDidChangeNotification object:nil];} + (void)setDefaultToFahrenheit {    // Update User Defaults    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];    [ud setInteger:0 forKey:MTRainUserDefaultsTemperatureUnit];    [ud synchronize];     // Post Notification    [[NSNotificationCenter defaultCenter] postNotificationName:MTRainTemperatureUnitDidChangeNotification object:nil];}
 والآن سنقوم بإنشاء ال class  الخاص بالإعدادات والذي تكلمت عنه سابقاً. قم بإنشاء Objective-C class وقم بتسميته MTSettings و اجعله class فرعي من NSObject كما هو موضح بالشكل (5). في MTSettings.h  نقوم بتعريف method واحدة formatTemperature:في MTSettings.m نقوم باستيراد ال header من الفئة التي أنشأناه قبل لحظات ونقوم ببناء formatTemperature: كما هو موضح بالأسفل، ال method تقبل كائن من نوع NSNumber ، تقوم بتحويله إلىfloat و ترجع قيمة منسقة بناء على إعدادات درجة الحرارة.

#import <Foundation/Foundation.h>

@interface MTSettings : NSObject
#pragma mark -#pragma mark Convenience Methods+ (NSString *)formatTemperature:(NSNumber *)temperature;
@end
#import "NSUserDefaults+Helpers.h"
+ (NSString *)formatTemperature:(NSNumber *)temperature { float value = [temperature floatValue];
if ([NSUserDefaults isDefaultCelcius]) { value = (value - 32.0) * (5.0 / 9.0); }
return [NSString stringWithFormat:@"%.0f°", value];}
قبل أن نكمل، قم بإضافة جملة الاستيراد ل   MTSetting class إلى ملف ال header الخاص بالمشروع بحيث يمكننا استخدامه في مشروعنا.

#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 "MTSettings.h"    #import "MTConstants.h"#endif
الآن سنقوم ببناء updateCurrentWeather في MTWeatherViewControllerclass. بيانات الطقس الحالي هي جزء من الرد الذي تلقيناه فعلياً من تطبيق التنبؤ بحالة الطقس Forecast API. بناء updateCurrentWeatherستكون بخطوات سهلة واضحة. الأمر الوحيد الذي ستحتاج للانتباه إليه هو احتمالية ال precipitation. إذا كانت هذه القيمة صفر، المفتاح precipProbability هو متضمن كجزء من الرد. وهذا هو السبب أننا في البداية نبحث عن وجود المفتاح precipProbability في قاموس الرد.
- (void)updateCurrentWeather {    // Weather Data    NSDictionary *data = [self.response objectForKey:@"currently"];

    // Update Date Label    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];    [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];    [dateFormatter setDateFormat:@"EEEE, MMM d"];    NSDate *date = [NSDate dateWithTimeIntervalSince1970:[data[@"time"] doubleValue]];    [self.labelDate setText:[dateFormatter stringFromDate:date]];

    // Update Temperature Label    [self.labelTemp setText:[MTSettings formatTemperature:data[@"temperature"]]];
    // Update Time Label    NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init];    [timeFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];    [timeFormatter setDateFormat:@"ha"];    [self.labelTime setText:[timeFormatter stringFromDate:[NSDate date]]];
    // Update Wind Label    [self.labelWind setText:[NSString stringWithFormat:@"%.0fMP", [data[@"windSpeed"] floatValue]]];
    // Update Rain Label    float rainProbability = 0.0;    if (data[@"precipProbability"]) {        rainProbability = [data[@"precipProbability"] floatValue] * 100.0;    }
    [self.labelRain setText:[NSString stringWithFormat:@"%.0f%%", rainProbability]];}
الخطوة الخامسة: ملء عرض التجمع

لملء عرض التجمع نحتاج أولاً لإنشاء
  UICollectionViewCell ككلاس فرعي،
كما وقم بإنشاء 
 Objective-C class جديد، وسميه MTHourCell واجعله يكون ككلاس فرعي من UICollectionViewCell كما في الشكل(6)
إنشاء جدول مخصص أو خلايا عرض مخصصة قد تكون   شاقة ومضنية.


في واجهة ال MTHourCell قمنا بتعريف أربع خصائص من نوع UILabel. لن نفعل الكثير في MTHourcell.m  كما ترى في الأسفل. لفهم initWithFrame: method قم بزيارة التصميم الذي عرضته في بداية هذا المقال. أريد هنا أن أناقش بناء initWithFrame: بالتفصيل ولكن أود قبل ذلك أن أشير إلى استخدامي تعريف preprocessor للون كتابة التسميات. قمت بإضافة preprocess إلى MTConstants.h  لجعله متاحاً للمشروع الحالي،

انظر بالأسفل
#import <UIKit/UIKit.h>
@interface MTHourCell : UICollectionViewCell
@property (strong, nonatomic) UILabel *labelTime;@property (strong, nonatomic) UILabel *labelTemp;@property (strong, nonatomic) UILabel *labelWind;@property (strong, nonatomic) UILabel *labelRain;
@end
#import "MTHourCell.h"
#define kMTLabelBottomWidth 40.0#define kMTLabelBottomHeight 40.0
@interface MTHourCell ()
@end
@implementation MTHourCell
- (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame];
if (self) { // Helpers CGSize size = self.contentView.frame.size;
// Initialize Label Time self.labelTime = [[UILabel alloc] initWithFrame:CGRectMake(30.0, 0.0, 50.0, 40.0)];
// Configure Label Time [self.labelTime setBackgroundColor:[UIColor clearColor]]; [self.labelTime setTextColor:[UIColor whiteColor]]; [self.labelTime setFont:[UIFont fontWithName:@"GillSans-Light" size:18.0]]; [self.labelTime setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin)]; [self.contentView addSubview:self.labelTime];
// Initialize Label Temp self.labelTemp = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 46.0, 80.0, 44.0)];
// Configure Label Temp [self.labelTemp setBackgroundColor:[UIColor clearColor]]; [self.labelTemp setTextAlignment:NSTextAlignmentCenter]; [self.labelTemp setTextColor:kMTColorGray]; [self.labelTemp setFont:[UIFont fontWithName:@"GillSans-Bold" size:40.0]]; [self.labelTemp setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin)]; [self.contentView addSubview:self.labelTemp];
// Initialize Label Wind self.labelWind = [[UILabel alloc] initWithFrame:CGRectMake(0.0, size.height - kMTLabelBottomHeight, kMTLabelBottomWidth, kMTLabelBottomHeight)];
// Configure Label Wind [self.labelWind setBackgroundColor:[UIColor clearColor]]; [self.labelWind setTextAlignment:NSTextAlignmentCenter]; [self.labelWind setTextColor:kMTColorGray]; [self.labelWind setFont:[UIFont fontWithName:@"GillSans-Light" size:16.0]]; [self.labelWind setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin)]; [self.contentView addSubview:self.labelWind];
// Initialize Label Rain self.labelRain = [[UILabel alloc] initWithFrame:CGRectMake(size.width - kMTLabelBottomWidth, size.height - kMTLabelBottomHeight, kMTLabelBottomWidth, kMTLabelBottomHeight)];
// Configure Label Rain [self.labelRain setBackgroundColor:[UIColor clearColor]]; [self.labelRain setTextAlignment:NSTextAlignmentCenter]; [self.labelRain setTextColor:kMTColorGray]; [self.labelRain setFont:[UIFont fontWithName:@"GillSans-Light" size:16.0]]; [self.labelRain setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin)]; [self.contentView addSubview:self.labelRain];
// Background View UIImage *backgroundImage = [[UIImage imageNamed:@"background-hour-cell"] resizableImageWithCapInsets:UIEdgeInsetsMake(40.0, 10.0, 10.0, 10.0)]; UIImageView *backgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, size.width, size.height)]; [backgroundView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; [backgroundView setImage:backgroundImage]; [self setBackgroundView:backgroundView]; }
return self;}
@end
#define kMTColorGray [UIColor colorWithRed:0.737 green:0.737 blue:0.737 alpha:1.0]#define kMTColorGreen [UIColor colorWithRed:0.325 green:0.573 blue:0.388 alpha:1.0]#define kMTColorOrange [UIColor
colorWithRed:1.000 green:0.306 blue:0.373 alpha:1.0]
وكما ترى في الأسفل بناء بروتوكولات UICollectionViewDataSource UICollectionViewDelegateو UICollectionViewDelegateFlowLayout مشابهة جداً لبناء بروتوكولات UITableViewDataSource وUITableViewDelegate.
- (NSInteger)numberOfSectionsInCollectionView:
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return self.forecast ? 1 : 0;}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return [self.forecast count];}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { MTHourCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:HourCell forIndexPath:indexPath];
// Fetch Data NSDictionary *data = [self.forecast objectAtIndex:indexPath.row];
// Initialize Date Formatter NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init]; [timeFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; [timeFormatter setDateFormat:@"ha"]; NSDate *date = [NSDate dateWithTimeIntervalSince1970:[data[@"time"] doubleValue]]; // Configure Cell [cell.labelTime setText:[timeFormatter stringFromDate:date]]; [cell.labelTemp setText:[MTSettings formatTemperature:data[@"temperature"]]]; [cell.labelWind setText:[NSString stringWithFormat:@"%.0fMP", [data[@"windSpeed"] floatValue]]];
float rainProbability = 0.0; if (data[@"precipProbability"]) { rainProbability = [data[@"precipProbability"] floatValue] * 100.0; }
[cell.labelRain setText:[NSString stringWithFormat:@"%.0f%%", rainProbability]];
return cell;}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return CGSizeMake(80.0, 120.0);}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(0.0, 10.0, 0.0, 10.0);}
لجعل هذا يعمل نحتاج إلى:    (1)   استيراد MTHourCell header
(2)   تعريف ثابت لاستخدامه كمعرف لإعادة استعمال الخلية
(3)   اخبار عرض التجمع باستعمال MTHourCell class لتعريف خلايا جديدة
في viewDidLoad نقوم أيضاً بعمل خلفية العرض شفافة
#import "MTHourCell.h"
static NSString *HourCell = @"HourCell";
- (void)viewDidLoad { [super viewDidLoad];
// Load Location self.location = [[NSUserDefaults standardUserDefaults] objectForKey:MTRainUserDefaultsLocation];
if (!self.location) { [self.locationManager startUpdatingLocation]; }
// Configure Collection View [self.collectionView setBackgroundColor:[UIColor clearColor]]; [self.collectionView registerClass:[MTHourCell class] forCellWithReuseIdentifier:HourCell];}

1- العرض الصحيح لواجهة المستخدم
الخطوة الأولى: المنافذ

مع أن العرض الصحيح يعرض الكثير من البيانات، بناء MTForecastViewController class ليس معقداً. فنحن نبدأ بإنشاء منفذ لجدول العرض في MTForecastViewController.h  وملاءمة الكلاس مع بروتوكولات UITableViewDataSource و UITableViewDelegate


#import <UIKit/UIKit.h>

@interface MTForecastViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@end

الخطوة الثانية: إنشاء واجهة المستخدم

إنشاء واجهة المستخدم بسيطة كبساطة إضافة عرض جدول إلى عرض المتحكم، الاتصال بالمنفذ الذي قمنا بإنشائه قبل قليل وإعداد File's Owner  كجدول عرض dataSource و delegate  الشكل(7)



الخطوة الثالثة: ملء عرض الجدول

قبل أن نقوم بملء الجدول بالبيانات، نحتاج لإنشاء UITableViewCell subclass. قم بإنشاء Objective-C class جديد وسميه  MTDayCell، وقم بعمله كلاس فرعي من UITableViewCell كما في الشكل(8)
قم بفتح MTDayCell.h  وعرف 5 منافذ من نوع UILabe. كما في MTHourCell class، بناء MTDayCell لا يعد صعباً أبداً كما ترى في الأسفل.




#import <UIKit/UIKit.h>
@interface MTDayCell : UITableViewCell
@property (strong, nonatomic) UILabel *labelDay;@property (strong, nonatomic) UILabel *labelDate;@property (strong, nonatomic) UILabel *labelTemp;@property (strong, nonatomic) UILabel *labelWind;@property (strong, nonatomic) UILabel *labelRain;
@end

#import "MTDayCell.h"
#define kMTCalendarWidth 44.0#define kMTCalendarHeight 80.0#define kMTCalendarMarginLeft 60.0#define kMTLabelRightWidth 30.0#define kMTLabelRightHeight 14.0
@interface MTDayCell ()
@property (strong, nonatomic) UIImageView *imageViewCalendar;
@end
@implementation MTDayCell
#pragma mark -#pragma mark Initialization- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) { // Helpers CGSize size = self.contentView.frame.size;
// Configure Table View Cell [self setSelectionStyle:UITableViewCellSelectionStyleNone];
// Initialize Image View Clock self.imageViewCalendar = [[UIImageView alloc] initWithFrame:CGRectMake(kMTCalendarMarginLeft, 0.0, kMTCalendarWidth, kMTCalendarHeight)];
// Configure Image View Clock [self.imageViewCalendar setContentMode:UIViewContentModeCenter]; [self.imageViewCalendar setImage:[UIImage imageNamed:@"background-calendar-day-cell"]]; [self.imageViewCalendar setAutoresizingMask:(UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin)]; [self.contentView addSubview:self.imageViewCalendar];
// Initialize Label Day self.labelDay = [[UILabel alloc] initWithFrame:CGRectMake(kMTCalendarMarginLeft, 10.0, kMTCalendarWidth, 20.0)];
// Configure Label Day [self.labelDay setTextColor:[UIColor whiteColor]]; [self.labelDay setTextAlignment:NSTextAlignmentCenter]; [self.labelDay setBackgroundColor:[UIColor clearColor]]; [self.labelDay setFont:[UIFont fontWithName:@"GillSans" size:14.0]]; [self.labelDay setAutoresizingMask:(UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin)]; [self.contentView addSubview:self.labelDay];
// Initialize Label Date self.labelDate = [[UILabel alloc] initWithFrame:CGRectMake(kMTCalendarMarginLeft, 20.0, kMTCalendarWidth, 60.0)];
// Configure Label Date [self.labelDate setTextColor:kMTColorGray]; [self.labelDate setTextAlignment:NSTextAlignmentCenter]; [self.labelDate setBackgroundColor:[UIColor clearColor]]; [self.labelDate setFont:[UIFont fontWithName:@"GillSans" size:24.0]]; [self.labelDate setAutoresizingMask:(UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin)]; [self.contentView addSubview:self.labelDate];
// Initialize Label Wind self.labelWind = [[UILabel alloc] initWithFrame:CGRectMake(size.width - kMTLabelRightWidth, (size.height / 2.0) - kMTLabelRightHeight, kMTLabelRightWidth, kMTLabelRightHeight)];
// Configure Label Wind [self.labelWind setTextColor:kMTColorGray]; [self.labelWind setTextAlignment:NSTextAlignmentCenter]; [self.labelWind setBackgroundColor:[UIColor clearColor]]; [self.labelWind setFont:[UIFont fontWithName:@"GillSans" size:12.0]]; [self.labelWind setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin)]; [self.contentView addSubview:self.labelWind];
// Initialize Label Rain self.labelRain = [[UILabel alloc] initWithFrame:CGRectMake(size.width - kMTLabelRightWidth, (size.height / 2.0), kMTLabelRightWidth, kMTLabelRightHeight)];
// Configure Label Rain [self.labelRain setTextColor:kMTColorGray]; [self.labelRain setTextAlignment:NSTextAlignmentCenter]; [self.labelRain setBackgroundColor:[UIColor clearColor]]; [self.labelRain setFont:[UIFont fontWithName:@"GillSans" size:12.0]]; [self.labelRain setAutoresizingMask:(UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin)]; [self.contentView addSubview:self.labelRain];
// Initialize Label Temp self.labelTemp = [[UILabel alloc] initWithFrame:CGRectMake(kMTCalendarWidth + kMTCalendarMarginLeft + 12.0, 0.0, size.width - kMTCalendarWidth - kMTCalendarMarginLeft - kMTLabelRightWidth - 12.0, size.height)];
// Configure Label Temp [self.labelTemp setTextColor:kMTColorGray]; [self.labelTemp setTextAlignment:NSTextAlignmentCenter]; [self.labelTemp setBackgroundColor:[UIColor clearColor]]; [self.labelTemp setFont:[UIFont fontWithName:@"GillSans-Bold" size:40.0]]; [self.labelTemp setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; [self.contentView addSubview:self.labelTemp]; }
return self;}
@end
بناء بروتوكول مصدر بيانات الجدول مشابه جدا لذلك الخاص بعرض التجمع الذي عرضناه مسبقاً، كما قمنا ببناء method واحدة لبروتوكول عرض الجدول tableView:heightForRowAtIndexPath: لضبط ارتفاع الصف الى80.0.
(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return self.forecast ? 1 : 0;}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.forecast count];}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { MTDayCell *cell = [tableView dequeueReusableCellWithIdentifier:DayCell forIndexPath:indexPath];
// Fetch Data NSDictionary *data = [self.forecast objectAtIndex:indexPath.row];
// Initialize Date Formatter NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; NSDate *date = [NSDate dateWithTimeIntervalSince1970:[data[@"time"] doubleValue]];
// Configure Cell [dateFormatter setDateFormat:@"EEE"]; [cell.labelDay setText:[dateFormatter stringFromDate:date]];
[dateFormatter setDateFormat:@"d"]; [cell.labelDate setText:[dateFormatter stringFromDate:date]];
float tempMin = [data[@"temperatureMin"] floatValue]; float tempMax = [data[@"temperatureMax"] floatValue]; [cell.labelTemp setText:[NSString stringWithFormat:@"%.0f°/%.0f°", tempMin, tempMax]];
[cell.labelWind setText:[NSString stringWithFormat:@"%.0f", [data[@"windSpeed"] floatValue]]];
float rainProbability = 0.0; if (data[@"precipProbability"]) { rainProbability = [data[@"precipProbability"] floatValue] * 100.0; }
[cell.labelRain setText:[NSString stringWithFormat:@"%.0f", rainProbability]];
return cell;}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 80.0;}
لجعل كل هذا يعمل نحتاج إلى:
(1)استيراد ال header file  لل MTDayCell class
(2) تعريف ثابت لاستخدامه كمعرف لإعادة استعمال الخلية
(3) تسجيل ال MTDayCell على أنه classلتعريف اعادة استخدام الخلية في viewDidLoad في updateView، نقوم باعادة تحميل عرض الجدول في viewDidLoad نقوم أيضاً بعمل خلفية العرض شفافة
#import "MTDayCell.h"
static NSString *DayCell = @"DayCell";
- (void)viewDidLoad { [super viewDidLoad];
// Configure Table View [self.tableView registerClass:[MTDayCell class] forCellReuseIdentifier:DayCell];}
- (void)updateView { // Reload Table View [self.tableView reloadData];}


1- العرض الصحيح لواجهة المستخدم

الخطوة الأولى: تحديث واجهة المستخدم

من الواضح أننا نحتاج لعمل  بعض التغييرات على متحكم عرض المواقع في تصميم كريس. عرض جدول المواقع سيتضمن قطاعين أساسيين بدلاً من واحد. القطاع الأعلى سيعرض المواقع المخزنة بينما يحجز القطاع السفلي لإعداد درجات الحرارة. ابدأ بفتح MTLocationsViewController.xib  وقم بإزالة navigation bar الذي أضفناه في المقال السابق. الشكل(9)
مما يعني أننا نستطيع أيضاً إلاغاء المنفذ لزر التعديل في MTLocationsViewController.h   وحدث editLocations في MTLocationsViewController.m

الخطوة الثانية: إنشاء خلية الموقع

الخلايا التي تعرض المواقع لها زر حذف على اليسار، لجعله يعمل نحتاج لإنشاء خلية عرض جدول مخصص. قم بإنشاء UITableViewCellsubclass وقم بتسميته MTLocationCell كما بالشكل(10)
قم بفتح MTLocationCell.h  وأنشئ خاصيتين:
(1)   buttonDelete (UIButton)
(2)   labelLocation(UILabel).
وكما ترى بناء MTLocationCell أقل تعقيداً من MTHourCell و  MTDayCell




#import <UIKit/UIKit.h>
@interface MTLocationCell : UITableViewCell
@property (strong, nonatomic) UIButton *buttonDelete;@property (strong, nonatomic) UILabel *labelLocation;
@end


#import "MTLocationCell.h"
#define kMTButtonDeleteWidth 44.0
#define kMTLabelLocationMarginLeft 44.0
@implementation MTLocationCell
#pragma mark -#pragma mark Initialization- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) { // Helpers CGSize size = self.contentView.frame.size;
// Initialize Delete Button self.buttonDelete = [UIButton buttonWithType:UIButtonTypeCustom];
// Configure Delete Button [self.buttonDelete setFrame:CGRectMake(0.0, 0.0, kMTButtonDeleteWidth, size.height)]; [self.buttonDelete setImage:[UIImage imageNamed:@"button-delete-location-cell"] forState:UIControlStateNormal]; [self.buttonDelete setImage:[UIImage imageNamed:@"button-delete-location-cell"] forState:UIControlStateSelected]; [self.buttonDelete setImage:[UIImage imageNamed:@"button-delete-location-cell"] forState:UIControlStateDisabled]; [self.buttonDelete setImage:[UIImage imageNamed:@"button-delete-location-cell"] forState:UIControlStateHighlighted]; [self.buttonDelete setAutoresizingMask:(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin)]; [self.contentView addSubview:self.buttonDelete];
// Initialize Location Label self.labelLocation = [[UILabel alloc] initWithFrame:CGRectMake(kMTLabelLocationMarginLeft, 0.0, size.width - kMTLabelLocationMarginLeft, size.height)];
// Configure Text Label [self.labelLocation setTextColor:kMTColorGray]; [self.labelLocatio
n setBackgroundColor:[UIColor clearColor]]; [self.labelLocation setFont:[UIFont fontWithName:@"GillSans" size:20.0]]; [self.labelLocation setAutoresizingMask:(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin)]; [self.contentView addSubview:self.labelLocation]; }
return self;}
@end

الخطوة الثالثة: تحديث بروتوكول أصل بيانات عرض الجدول

لإنشاء التصميم نحتاج للتعديل على البروتوكولات UITableViewDataSourceو UITableViewDelegate. ابدأ باستيراد ملف ال header  للكلاس MTLocationCell وفئة NSUserDefaults التي أنشأناها مسبقاً.
عرض الجدول سيحتوي ثلاث أنواع من الخلايا  وسنقوم بتعريف ثلاث متغيرات لكل خلية نوع كما هو موضح في الأسفل
#import "MTLocationsViewController.h"
#import "MTLocationCell.h"#import "NSUserDefaults+Helpers.h"
@interface MTLocationsViewController ()
@property (strong, nonatomic) NSMutableArray *locations;
@end
static NSString *AddLocationCell = @"AddLocationCell";static NSString *LocationCell = @"LocationCell";static NSString *SettingsCell = @"SettingsCell";
في setupView نقوم بإعداد الجدول عن طريق:
(1)تحديد خاصية separatorStyle كي تكون UITableViewCellSeparatorStyleNone
(2)    تسجيل كلاس لكل متغير

 - (void)setupView {    // Setup Table View    [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];

    // Register Class for Cell Reuse    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:AddLocationCell];    [self.tableView registerClass:[MTLocationCell class] forCellReuseIdentifier:LocationCell];    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:SettingsCell];}
بروتوكول UITableViewDataSource يتغير significantly وبناء ال methods المختلفة تبدو معثدة نوعاً ما والسبب يرجع إلى وجود nested if statements
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 2;}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (section == 0) { return [self.locations count] + 1; } return 2;}
سنقوم أيضاً ببناء tableView:titleForHeaderInSection: و  tableView:viewForHeaderInSection: لاستبدال header section  الرئيسي بتصميم مخصص يتوافق مع تصميم التطبيق ككل. عند بناء tableView:viewForHeaderInSection: فمن المهم أيضا بناء tableView:heightForHeaderInSection

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {    switch (section) {        case 0: {            return NSLocalizedString(@"Locations", nil);            break;        }        default: {            return NSLocalizedString(@"Temperature", nil);            break;        }    }

    return nil;}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {    // Header Text    NSString *text = [self tableView:tableView titleForHeaderInSection:section];
    // Helpers    CGRect labelFrame = CGRectMake(12.0, 0.0, tableView.bounds.size.width, 44.0);
    // Initialize Label    UILabel *label = [[UILabel alloc] initWithFrame:labelFrame];
    // Configure Label    [label setText:text];    [label setTextColor:kMTColorOrange];    [label setFont:[UIFont fontWithName:@"GillSans" size:20.0]];    [label setBackgroundColor:[UIColor clearColor]];
    // Initialize View    UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, tableView.bounds.size.width, 34.0)];    [backgroundView setBackgroundColor:[UIColor clearColor]];    [backgroundView addSubview:label];
    return backgroundView;}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {    return 40.0;}

وهنا نقوم باستخدام tableView:heightForFooterInSection: لإنشاء بعض المسافات البيضاء بين القطاعات السفلية مما يعني أننا سوف نحتاج لبناء tableView:viewForFooterInSection:.
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { // Initialize View UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, tableView.bounds.size.width, 34.0)]; [backgroundView setBackgroundColor:[UIColor whiteColor]];
return backgroundView;}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { if (section == 0) { return 40.0; }
return 0.0;}
إنشاء tableView:cellForRowAtIndexPath:  أكثر تعقيداً وذلك يرجع إلى الثلاث أنواع من الخلايا في عرض الجدول.
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {    UITableViewCell *cell = nil;    if (indexPath.section == 0) {        if (indexPath.row == 0) {            cell = [tableView dequeueReusableCellWithIdentifier:AddLocationCell forIndexPath:indexPath];        } else {            cell = [tableView dequeueReusableCellWithIdentifier:LocationCell forIndexPath:indexPath];        }    } else {        cell = [tableView dequeueReusableCellWithIdentifier:SettingsCell forIndexPath:indexPath];    }    // Configure Cell    [self configureCell:cell atIndexPath:indexPath];    return cell;}
في configureCell:atIndexPath:، نقوم بإعداد كل خلية. وكما قلت مسبقاً التعقيد فيها يرجع إلى nested if statement. الضغط على زر الحذف في خلية الموقع يرسل رسالة من نوع deleteLocation:  إلى  متحكم عرض المواقع والآن سنقوم ببنائها:

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {    // Helpers    UIFont *fontLight = [UIFont fontWithName:@"GillSans-Light" size:18.0];    UIFont *fontRegular = [UIFont fontWithName:@"GillSans" size:18.0];

    // Background View Image    UIImage *backgroundImage = [[UIImage imageNamed:@"background-location-cell"] resizableImageWithCapInsets:UIEdgeInsetsMake(10.0, 0.0, 0.0, 10.0)];
    // Configure Table View Cell    [cell.textLabel setFont:fontLight];    [cell.textLabel setTextColor:kMTColorGray];    [cell.textLabel setBackgroundColor:[UIColor clearColor]];    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
    if (indexPath.section == 0) {        if (indexPath.row == 0) {            [cell.textLabel setText:@"Add Current Location"];            [cell.imageView setContentMode:UIViewContentModeCenter];            [cell.imageView setImage:[UIImage imageNamed:@"icon-add-location"]];
            // Background View Image            backgroundImage = [[UIImage imageNamed:@"background-add-location-cell"] resizableImageWithCapInsets:UIEdgeInsetsMake(10.0, 0.0, 0.0, 10.0)];
        } else {            // Fetch Location            NSDictionary *location = [self.locations objectAtIndex:(indexPath.row - 1)];
            // Configure Cell            [[(MTLocationCell *)cell buttonDelete] addTarget:self action:@selector(deleteLocation:) forControlEvents:UIControlEventTouchUpInside];            [[(MTLocationCell *)cell labelLocation] setText:[NSString stringWithFormat:@"%@, %@", location[MTLocationKeyCity], location[MTLocationKeyCountry]]];        }
    } else {        if (indexPath.row == 0) {            [cell.textLabel setText:NSLocalizedString(@"Fahrenheit", nil)];
            if ([NSUserDefaults isDefaultCelcius]) {                [cell.textLabel setFont:fontLight];                [cell.textLabel setTextColor:kMTColorGray];            } else {                [cell.textLabel setFont:fontRegular];                [cell.textLabel setTextColor:kMTColorGreen];            }
        } else {            [cell.textLabel setText:NSLocalizedString(@"Celsius", nil)];
            if ([NSUserDefaults isDefaultCelcius]) {                [cell.textLabel setFont:fontRegular];                [cell.textLabel setTextColor:kMTColorGreen];            } else {                [cell.textLabel setFont:fontLight];                [cell.textLabel setTextColor:kMTColorGray];            }        }    }
    if (backgroundImage) {        // Background View        UIImageView *backgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, cell.frame.size.width, cell.frame.size.height)];        [backgroundView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];        [backgroundView setImage:backgroundImage];        [cell setBackgroundView:backgroundView];    }}
والآن وبما أن المواقع يمكن حذفها بالضغط على زر الحذف فلا داعي لأن تكون قابلة للتعديل عليها. وهذا يعني أن بناء tableView:canEditRowAtIndexPath:  ممكن أن يقلل إلى إرجاع NO وبناء tableView:commitEditingStyle:forRowAtIndexPath: ممكن أن يتم إزالتها.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {    return NO;}

الخطوة الرابعة: تحديث بروتوكول عرض الجدول

في tableView:didSelectRowAtIndexPath نقوم بإضافة المزيد من التعقيد لتضمنها القطاع الثاني المحتوي على إعدادات درجات الحرارة ولكن باستخدام الفئة NSUserDefaults أصبح الأمر أسهل بكثير.

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    if (indexPath.section == 0) {        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];        }
    } else {        if (indexPath.row == 0 && [NSUserDefaults isDefaultCelcius]) {            [NSUserDefaults setDefaultToFahrenheit];        } else if (![NSUserDefaults isDefaultCelcius]) {            [NSUserDefaults setDefaultToCelcius];        }
        // Update Section        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];    }
    // Show Center View Controller    [self.viewDeckController closeLeftViewAnimated:YES];}
الصفوف في تصميم كريس أطول بقليل من الارتفاع المعروف وهو 44 نقطة. لتوضيح هذه التفصيلة سنقوم ببناء tableView:heightForRowAtIndexPath:  كما هو موضح بالأسفل:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {    return 50.0;}

الخطوة الخامسة: حذف المواقع

وأخيرا سنقوم بآخر خطوة وهي بناء deleteLocation:  والتي يتم استدعائها عند ضغط زر الحذف في خلية الموقع. فبمجرد معرفة index path للخلية التي يرجع إليها زر الحذف المضغوط نحتاج فقط للتعديل على مصفوفة المواقع، وقاعدة بيانات المستخدم و التعديل على عرض الجدول.

- (void)deleteLocation:(id)sender {    UITableViewCell *cell = (UITableViewCell *)[[(UIButton *)sender superview] superview];    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

    // Update Locations    [self.locations removeObjectAtIndex:(indexPath.row - 1)];
    // Update User Defaults    [[NSUserDefaults standardUserDefaults] setObject:self.locations forKey:MTRainUserDefaultsLocations];
    // Update Table View    [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];}
1- الخطوة الأخيرة:
لإنهاء هذا المشروع لنقوم باستبدال كل الصور بالصور الموجودة في تصميم كريس.
ابن وشغ
ل التطبيق لترى النتيجة النهائية. مع أننا قضينا بعض الوقت في إنشاء وتصميم هذا التطبيق، هناك بعض الأمور تحتاج ترتيب. مثلا سيكون من الجيد بناء آلية بحيث نستطيع عرض البيانات المخزنة للمستخدم طالما أن الرد من  تطبيق التنبؤ بالطقس لم يصل بعد. وهناك العديد من التحسينات الممكن إضافتها إلى هذا التطبيق.

الملخص

إنشاء واجهة المستخدم كان فيها الكثير من العمل، ولكن الكود المستخدم لم يكن بذلك التعقيد. آمل أن تكون هذه السلسلة التعليمية مدى روعة التصميم الذي يمكنك عمله للتطبيق الخاص بك.

الكــاتــب

    • مشاركة

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

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