在這個兩部分的系列教程中,您將探索如何使用以下工具和技術來建立自己的App:
- Cocoapods
- Manual layout in code(純程式碼佈局)
- ReactiveCocoa
- OpenWeatherMap
本教程專為熟悉基本知識的、但還沒有接觸到太多高階主題的中級開發者而設計。本教程也是想要去探索Objective-C函式程式設計一個很好的開始。
開始
開啟Xcode並執行File\New\Project
。選擇Application\Empty Application
。將專案命名為SimpleWeather
,單擊下一步,選擇一個目錄去儲存你的專案,然後點選Create。 現在,你的基礎專案已經完成。下一步是整合你的第三方工具。但首先你要關閉Xcode
,確保他不會影響下一步。
Cocoapods
你將要下載Cocoapods的程式碼,在Xcode專案中新增檔案來使用,並配置專案需要的設定。
Mantle
Mantle是由於Github團隊開發的,目的是去除Objective-C把JSON資料轉為NSObject子類的所有樣板程式碼。Mantle也能做資料轉換,通過一種神奇的方式把JSON原始資料(strings, ints, floats)轉換為複雜資料,比如NSDate, NSURL, 甚至是自定義類。
LBBlurredImage
LBBlurredImage是一個繼承自UIImageView,輕而易舉使影象模糊的專案。你將僅僅用一行程式碼來建立一個神奇的模糊效果。
TSMessages
TSMessages 是另一個非常簡單的庫,用來顯示浮層警告和通知。當出現錯誤資訊而不直接影響使用者的時候,最好使用浮層來代替模態視窗(例如UIAlertView),這樣你將盡可能減少對使用者的影響。
你將只用TSMessages,在網路失去連線或API錯誤的時候。如果發生錯誤,你將看到類似這樣的一個浮層:
ReactiveCocoa
最後,你將使用到ReactiveCocoa,他也來自於GitHub團隊。ReactiveCocoa給Objective-C帶來了函式程式設計,類似與.NET的Reactive Extensions。你將在第二部分花費大部分時間去實現ReactiveCocoa。
設定你的Cocoapods
設定你的Cocoapods,先要確保你已經安裝了Cocoapods。為此,開啟命令列程式,並輸入。
1 |
which pod |
你將會看到類似這樣的輸出:
1 |
/usr/bin/pod |
這決定於你如何管理Ruby gems,例如你使用rbenv或RVM,路徑可能有所不同。
如果命令列簡單的返回提示,或顯示pod not found
,表示Cocoapods未安裝在你的機器上。可以檢視我們的Cocoapods教程作為安裝說明。這也是一個很好的資源,如果你想更多得了解Cocoapods的話。
1 |
sjpsega注:Cocoapods中文安裝教程可以看這篇 http://geeklu.com/2013/06/cocoapods-101/ |
Podfiles是用來告訴Cocoapods哪些開源專案需要匯入。
要建立你的第一個Cocoapod,首先在命令列中用cd
命令導航到你的XCode專案所在的資料夾,在命令列中啟動編輯器,輸入
1 2 3 4 5 6 |
platform :ios, '7.0' pod 'Mantle' pod 'LBBlurredImage' pod 'TSMessages' pod 'ReactiveCocoa' |
這檔案做了兩件事情:
- 告訴Cocoapods你的目標平臺與版本,這裡的你目標是iOS 7.0。
- 列給Cocoapods一個專案所有需要引入和安裝的三方庫清單。
在命令列中輸入pod install
進行安裝。
這可能需要花一到兩分鐘的時間去安裝各種包。你的命令列應該輸出如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ pod install Analyzing dependencies CocoaPods 0.28.0 is available. Downloading dependencies Installing HexColors (2.2.1) Installing LBBlurredImage (0.1.0) Installing Mantle (1.3.1) Installing ReactiveCocoa (2.1.7) Installing TSMessages (0.9.4) Generating Pods project Integrating client project [!] From now on use `SimpleWeather.xcworkspace`. |
1 |
sjpsega注:若你之前安裝過Cocoapods的話,這裡安裝報錯的話,可以看看http://blog.cocoapods.org/Repairing-Our-Broken-Specs-Repository/ 修復問題 |
Cocoapods會在你的專案目錄中建立一堆新檔案,但是,只有一個需要你關心,SimpleWeather.xcworkspace
。
用Xcode開啟SimpleWeather.xcworkspace
。看看你的專案設定,現在有一個Pods專案在你的專案工作區,以及在Pods資料夾放著每一個你引入的庫,如下所示:
確保你已經選擇SimpleWeather專案,如圖所示:
構建並執行您的App,以確保一切工作正常:
1 |
提示:您可能會注意到有一些專案生成警告。因為Cocoapods引入的專案,是由不同的開發者開發,並且不同的開發者對生成警告有不同的態度。通常,你應該可以忽略它們。只要確保沒有任何編譯器錯誤! |
建立你的主檢視控制器
雖然這個App看起來複雜,但它還會通過一個單一的View Controller完成。現在,你將新增他。
選中SimpleWeather
專案,單擊File\New\File
,並且選擇Cocoa Touch\Objective-C class
. 命名為WXController
,並設定為UIViewController
的子類。
確保Targeted for iPad
和With XIB for user interface
都沒有選中,如下圖所示:
開啟WXController.m
然後用如下所示替換-viewDidLoad
方法:
1 2 3 4 5 6 |
- (void)viewDidLoad { [super viewDidLoad]; // Remove this later self.view.backgroundColor = [UIColor redColor]; } |
現在開啟AppDelegate.m
,並且引入如下兩個class:
1 2 |
#import "WXController.h" #import <TSMessage.h> |
眼尖的讀者會注意到WXController
使用引號引入,TSMessage
使用單括號引入。
回頭看下當你建立Podfile的時候,你使用Cocoapods引入TSMessage
。Cocoapods建立TSMessage專案,並把它加入到工作空間。既然你從工作區的其他專案匯入,可以使用尖括號代替引號。
代替-application:didFinishLaunchingWithOptions
的內容:
1 2 3 4 5 6 7 8 9 10 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // 1 self.window.rootViewController = [[WXController alloc] init]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; // 2 [TSMessage setDefaultViewController: self.window.rootViewController]; return YES; } |
標號註釋的解釋:
- 初始化並設定
WXController
例項作為App的根檢視控制器。通常這個控制器是一個的UINavigationController
或UITabBarController
,但在當前情況下,你使用WXController
的單個例項。 - 設定預設的檢視控制器來顯示你的TSMessages。這樣做,你將不再需要手動指定要使用的控制器來顯示警告。
構建並執行,看看你的新檢視控制器起作用了。
在紅色背景下,狀態列有點不夠清晰。幸運的是,有一個簡單的方法,使狀態列更清晰易讀。
在iOS7,UIViewController有一個新的API,用來控制狀態列的外觀。開啟WXController
,直接新增下面的程式碼到-viewDidLoad:
方法下:
1 2 3 |
- (UIStatusBarStyle)preferredStatusBarStyle { return UIStatusBarStyleLightContent; } |
再次構建並執行,你將看到狀態列如下的變化:
設定你的App檢視
現在是時候讓你的App接近生活。下載這個專案的圖片,並解壓縮到一個合適的位置。這個壓縮包的背景圖片出自Flickr使用者idleformat之手,天氣圖片出自Dribbble使用者heeyeun之手。
切換回Xcode,單擊File\Add Files to “SimpleWeather”
….定位到你剛剛解壓縮的圖片資料夾並選擇它。選擇Copy items into destination group’s folder (if needed)
,然後單擊Add
。
開啟WXController.h
, 新增如下委託協議:
1 |
<UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate> |
現在開啟WXController.m
。 小提示:你可以使用Control-Command-Up
的快捷鍵來實現.h
和.m
檔案之間的快速切換。
新增如下程式碼到WXController.m
頂部:
1 |
#import <LBBlurredImage/UIImageView+LBBlurredImage.h> |
LBBlurredImage.h
包含在Cocoapods引入的LBBlurredImage
專案,你會使用這個庫來模糊背景圖片。
應該有一個空的私有介面樣板在WXController
imports的下方。它具有以下屬性:
1 2 3 4 5 6 7 8 |
@interface WXController () @property (nonatomic, strong) UIImageView *backgroundImageView; @property (nonatomic, strong) UIImageView *blurredImageView; @property (nonatomic, strong) UITableView *tableView; @property (nonatomic, assign) CGFloat screenHeight; @end |
現在,是時候在專案中建立並設定檢視。
下面是你App的分解圖,記住,table view將是透明的:
為了實現動態模糊效果,在你的App中,你會根據App的滾動來改變模糊影象的alpha值。
開啟WXController.m
,使用如下程式碼來,替換掉-viewDidLoad
中設定背景色的程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// 1 self.screenHeight = [UIScreen mainScreen].bounds.size.height; UIImage *background = [UIImage imageNamed:@"bg"]; // 2 self.backgroundImageView = [[UIImageView alloc] initWithImage:background]; self.backgroundImageView.contentMode = UIViewContentModeScaleAspectFill; [self.view addSubview:self.backgroundImageView]; // 3 self.blurredImageView = [[UIImageView alloc] init]; self.blurredImageView.contentMode = UIViewContentModeScaleAspectFill; self.blurredImageView.alpha = 0; [self.blurredImageView setImageToBlur:background blurRadius:10 completionBlock:nil]; [self.view addSubview:self.blurredImageView]; // 4 self.tableView = [[UITableView alloc] init]; self.tableView.backgroundColor = [UIColor clearColor]; self.tableView.delegate = self; self.tableView.dataSource = self; self.tableView.separatorColor = [UIColor colorWithWhite:1 alpha:0.2]; self.tableView.pagingEnabled = YES; [self.view addSubview:self.tableView]; |
這是非常簡單的程式碼:
- 獲取並儲存螢幕高度。之後,你將在用分頁的方式來顯示所有天氣資料時,使用它。
- 建立一個靜態的背景圖,並新增到檢視上。
- 使用LBBlurredImage來建立一個模糊的背景影象,並設定alpha為0,使得開始
backgroundImageView
是可見的。 - 建立tableview來處理所有的資料呈現。 設定WXController為delegate和dataSource,以及滾動檢視的delegate。請注意,設定
pagingEnabled
為YES
。
新增如下UITableView的delegate和dataSource的程式碼到WXController.m
的@implementation
塊中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// 1 #pragma mark - UITableViewDataSource // 2 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 2; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // TODO: Return count of forecast return 0; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"CellIdentifier"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (! cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier]; } // 3 cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.backgroundColor = [UIColor colorWithWhite:0 alpha:0.2]; cell.textLabel.textColor = [UIColor whiteColor]; cell.detailTextLabel.textColor = [UIColor whiteColor]; // TODO: Setup the cell return cell; } #pragma mark - UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // TODO: Determine cell height based on screen return 44; } |
Pragma mark
是組織程式碼的很好的一種方式。- 你的table view有兩個部分,一個是每小時的天氣預報,另一個用於每日播報。table view的section數目,設定為2。
- 天氣預報的cell是不可選擇的。給他們一個半透明的黑色背景和白色文字。
1 |
注意:使用格式化的註釋 // TODO:可以幫助Xcode找到需要以後完成的程式碼。你還可以使用 Show Document Items(Control-6)來檢視TODO項。 |
最後,新增如下程式碼到WXController.m
:
1 2 3 4 5 6 7 8 9 |
- (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; CGRect bounds = self.view.bounds; self.backgroundImageView.frame = bounds; self.blurredImageView.frame = bounds; self.tableView.frame = bounds; } |
在WXController.m
中,你的檢視控制器呼叫該方法來編排其子檢視。
構建並執行你的App,看看你的檢視如何堆疊。
仔細看,你會看到所有空的table cell的cell分隔線。
仍然在-viewDidLoad
中,新增下面的程式碼來設定你的佈局框架和邊距:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// 1 CGRect headerFrame = [UIScreen mainScreen].bounds; // 2 CGFloat inset = 20; // 3 CGFloat temperatureHeight = 110; CGFloat hiloHeight = 40; CGFloat iconHeight = 30; // 4 CGRect hiloFrame = CGRectMake(inset, headerFrame.size.height - hiloHeight, headerFrame.size.width - (2 * inset), hiloHeight); CGRect temperatureFrame = CGRectMake(inset, headerFrame.size.height - (temperatureHeight + hiloHeight), headerFrame.size.width - (2 * inset), temperatureHeight); CGRect iconFrame = CGRectMake(inset, temperatureFrame.origin.y - iconHeight, iconHeight, iconHeight); // 5 CGRect conditionsFrame = iconFrame; conditionsFrame.size.width = self.view.bounds.size.width - (((2 * inset) + iconHeight) + 10); conditionsFrame.origin.x = iconFrame.origin.x + (iconHeight + 10); |
這是相當常規設定程式碼,但這裡是怎麼回事:
- 設定table的header大小與螢幕相同。你將利用的UITableView的分頁來分隔頁面頁頭和每日每時的天氣預報部分。
- 建立inset(或padding)變數,以便您的所有標籤均勻分佈並居中。
- 建立並初始化為各種檢視建立的高度變數。設定這些值作為常量,使得可以很容易地在需要的時候,配置和更改您的檢視設定。
- 使用常量和inset變數,為label和view建立框架。
- 複製圖示框,調整它,使文字具有一定的擴充套件空間,並將其移動到該圖示的右側。當我們把標籤新增到檢視,你會看到佈局的效果。
新增如下程式碼到-viewDidLoad
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
// 1 UIView *header = [[UIView alloc] initWithFrame:headerFrame]; header.backgroundColor = [UIColor clearColor]; self.tableView.tableHeaderView = header; // 2 // bottom left UILabel *temperatureLabel = [[UILabel alloc] initWithFrame:temperatureFrame]; temperatureLabel.backgroundColor = [UIColor clearColor]; temperatureLabel.textColor = [UIColor whiteColor]; temperatureLabel.text = @"0°"; temperatureLabel.font = [UIFont fontWithName:@"HelveticaNeue-UltraLight" size:120]; [header addSubview:temperatureLabel]; // bottom left UILabel *hiloLabel = [[UILabel alloc] initWithFrame:hiloFrame]; hiloLabel.backgroundColor = [UIColor clearColor]; hiloLabel.textColor = [UIColor whiteColor]; hiloLabel.text = @"0° / 0°"; hiloLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:28]; [header addSubview:hiloLabel]; // top UILabel *cityLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, self.view.bounds.size.width, 30)]; cityLabel.backgroundColor = [UIColor clearColor]; cityLabel.textColor = [UIColor whiteColor]; cityLabel.text = @"Loading..."; cityLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:18]; cityLabel.textAlignment = NSTextAlignmentCenter; [header addSubview:cityLabel]; UILabel *conditionsLabel = [[UILabel alloc] initWithFrame:conditionsFrame]; conditionsLabel.backgroundColor = [UIColor clearColor]; conditionsLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:18]; conditionsLabel.textColor = [UIColor whiteColor]; [header addSubview:conditionsLabel]; // 3 // bottom left UIImageView *iconView = [[UIImageView alloc] initWithFrame:iconFrame]; iconView.contentMode = UIViewContentModeScaleAspectFit; iconView.backgroundColor = [UIColor clearColor]; [header addSubview:iconView]; |
這是相當長的一塊程式碼,但它真的只是在做設定各種控制元件的繁重工作。簡單的說:
- 設定當前view為你的table header。
- 構建每一個顯示氣象資料的標籤。
- 新增一個天氣圖示的影象檢視。
構建並執行你的App,你應該可以看到你之前佈局的所有所有view。下面的螢幕截圖顯示了使用手工佈局的、所有標籤框在視覺上的顯示。
用手指輕輕推動table,當你滾動它的時候,應該會反彈。
獲取氣象資料
你會注意到,App顯示“Loading…”,但它不是真正地在工作。是時候獲取一些真正的天氣資料。
你會從OpenWeatherMap的API拉取資料。 OpenWeatherMap是一個非常棒的服務,旨在提供實時,準確,免費的天氣資料給任何人。雖然有很多天氣API,但他們大多要麼使用較舊的資料格式,如XML,或是有償服務 – 並且有時還相當昂貴。
你會遵循以下基本步驟,來獲你裝置的位置的氣象資料:
- 找到裝置的位置
- 從API端下載JSON資料
- 對映JSON到
WXConditions
和WXDailyForecasts
- 告訴UI有新資料了
開始建立你的天氣模型和資料管理類。單擊File\New\File…
並選擇Cocoa Touch\Objective-C class
。命名為WXClient
並使其為NSObject
的子類。
這樣再做三次建立以下類:
WXManager
作為NSObject
的子類WXCondition
作為MTLModel
的子類WXDailyForecast
作為WXCondition
的子類
全部完成?現在,你可以開始下一節,其中涉及對映和轉換您的天氣資料。
建立你的天氣模型
你的模型將使用Mantle,這使得資料對映和轉型非常簡單。
開啟WXCondition.h
如下列程式碼,修改介面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// 1 @interface WXCondition : MTLModel <MTLJSONSerializing> // 2 @property (nonatomic, strong) NSDate *date; @property (nonatomic, strong) NSNumber *humidity; @property (nonatomic, strong) NSNumber *temperature; @property (nonatomic, strong) NSNumber *tempHigh; @property (nonatomic, strong) NSNumber *tempLow; @property (nonatomic, strong) NSString *locationName; @property (nonatomic, strong) NSDate *sunrise; @property (nonatomic, strong) NSDate *sunset; @property (nonatomic, strong) NSString *conditionDescription; @property (nonatomic, strong) NSString *condition; @property (nonatomic, strong) NSNumber *windBearing; @property (nonatomic, strong) NSNumber *windSpeed; @property (nonatomic, strong) NSString *icon; // 3 - (NSString *)imageName; @end |
MTLJSONSerializing
協議告訴Mantle序列化該物件如何從JSON對映到Objective-C的屬性。- 這些都是你的天氣資料的屬性。你將會使用這些屬性的get set方法,但是當你要擴充套件App,這是一種很好的方法來訪問資料。
- 這是一個簡單的輔助方法,從天氣狀況對映到影象檔案。
構建並執行App。失敗了……
原因是你沒有從你的Cocoapods專案中引入Mantle
。解決方法是,在WXCondition.h
中,你需要把MTLModel.h
替換為#import <Mantle.h>
。
現在構建並執行App。成功了。你會看到一些新的警告,但你可以忽略他們。
首先,你需要處理未實現的-imageName
方法。
開啟WXCondition.m
,新增如下方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
+ (NSDictionary *)imageMap { // 1 static NSDictionary *_imageMap = nil; if (! _imageMap) { // 2 _imageMap = @{ @"01d" : @"weather-clear", @"02d" : @"weather-few", @"03d" : @"weather-few", @"04d" : @"weather-broken", @"09d" : @"weather-shower", @"10d" : @"weather-rain", @"11d" : @"weather-tstorm", @"13d" : @"weather-snow", @"50d" : @"weather-mist", @"01n" : @"weather-moon", @"02n" : @"weather-few-night", @"03n" : @"weather-few-night", @"04n" : @"weather-broken", @"09n" : @"weather-shower", @"10n" : @"weather-rain-night", @"11n" : @"weather-tstorm", @"13n" : @"weather-snow", @"50n" : @"weather-mist", }; } return _imageMap; } // 3 - (NSString *)imageName { return [WXCondition imageMap][self.icon]; } |
- 建立一個靜態的NSDictionary,因為WXCondition的每個例項都將使用相同的資料對映。
- 天氣狀況與影象檔案的關係(例如“01d”代表“weather-clear.png”)。
- 宣告獲取影象檔名的公有方法。
看一看從OpenWeatherMap返回的JSON響應資料:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
{ "dt": 1384279857, "id": 5391959, "main": { "humidity": 69, "pressure": 1025, "temp": 62.29, "temp_max": 69.01, "temp_min": 57.2 }, "name": "San Francisco", "weather": [ { "description": "haze", "icon": "50d", "id": 721, "main": "Haze" } ] } |
你需要把巢狀的JSON值對映到Objective-C的屬性。巢狀的JSON值是元素,如溫度,即上面看到的main
節點。
要做到這一點,你將利用的Objective-C的Key-Value Coding和Mantle的MTLJSONAdapter。
還在WXCondition.m
,通過新增+JSONKeyPathsByPropertyKey
方法,“JSON到模型屬性”的對映,且該方法是MTLJSONSerializing
協議的require。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
+ (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"date": @"dt", @"locationName": @"name", @"humidity": @"main.humidity", @"temperature": @"main.temp", @"tempHigh": @"main.temp_max", @"tempLow": @"main.temp_min", @"sunrise": @"sys.sunrise", @"sunset": @"sys.sunset", @"conditionDescription": @"weather.description", @"condition": @"weather.main", @"icon": @"weather.icon", @"windBearing": @"wind.deg", @"windSpeed": @"wind.speed" }; } |
在這個方法裡,dictionary的key是WXCondition
的屬性名稱,而dictionary的value是JSON的路徑。
您可能已經注意到,這裡有一個從JSON資料對映到Objective-C屬性的問題。屬性date
是NSDate
型別的,但JSON有一個Unix時間型別(sjpsega注:即從1970年1月1日0時0分0秒起至現在的總秒數)的NSInteger值。你需要完成兩者之間的轉換。
Mantle正好有一個功能來為你解決這個問題:MTLValueTransformer。這個類允許你宣告一個block,詳細說明值的相互轉換。
Mantle的轉換器語法有點怪。要建立一個為一個特定屬性的轉換器,,您可以新增一個以屬性名開頭和JSONTransformer
結尾的類方法。 可能看實際程式碼比試圖解釋它更容易理解,所以在WXCondition.m
中新增以下為NSDate屬性設定的轉換器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
+ (NSValueTransformer *)dateJSONTransformer { // 1 return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString *str) { return [NSDate dateWithTimeIntervalSince1970:str.floatValue]; } reverseBlock:^(NSDate *date) { return [NSString stringWithFormat:@"%f",[date timeIntervalSince1970]]; }]; } // 2 + (NSValueTransformer *)sunriseJSONTransformer { return [self dateJSONTransformer]; } + (NSValueTransformer *)sunsetJSONTransformer { return [self dateJSONTransformer]; } |
- 使用blocks做屬性的轉換的工作,並返回一個MTLValueTransformer返回值。
- 您只需要詳細說明Unix時間和NSDate之間進行轉換一次,就可以重用
-dateJSONTransformer
方法為sunrise和sunset屬性做轉換。
下一個值轉型有點討厭,但它只是使用OpenWeatherMap的API,並自己的格式化JSON響應方式的結果。weather
鍵對應的值是一個JSON陣列,但你只關注單一的天氣狀況。
在WXCondition.m
中,使用dateJSONTransformer
相同的結構,您可以建立一個NSArray和NSString的之間的轉換。該解決方案提供如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
+ (NSValueTransformer *)conditionDescriptionJSONTransformer { return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSArray *values) { return [values firstObject]; } reverseBlock:^(NSString *str) { return @[str]; }]; } + (NSValueTransformer *)conditionJSONTransformer { return [self conditionDescriptionJSONTransformer]; } + (NSValueTransformer *)iconJSONTransformer { return [self conditionDescriptionJSONTransformer]; } |
最後的轉換器只是為了格式化。 OpenWeatherAPI使用每秒/米的風速。由於您的App使用英制系統,你需要將其轉換為每小時/英里。
在WXCondition.m
的實現中新增以下轉換器的方法和巨集定義。
1 2 3 4 5 6 7 8 9 |
#define MPS_TO_MPH 2.23694f + (NSValueTransformer *)windSpeedJSONTransformer { return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSNumber *num) { return @(num.floatValue*MPS_TO_MPH); } reverseBlock:^(NSNumber *speed) { return @(speed.floatValue/MPS_TO_MPH); }]; } |
在OpenWeatherMap的API中有一個小的差異,你必須處理。看一看在位於當前狀況的響應和每日預測反應之間的溫度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// current "main": { "grnd_level": 1021.87, "humidity": 64, "pressure": 1021.87, "sea_level": 1030.6, "temp": 58.09, "temp_max": 58.09, "temp_min": 58.09 } // daily forecast "temp": { "day": 58.14, "eve": 58.14, "max": 58.14, "min": 57.18, "morn": 58.14, "night": 57.18 } |
current
的第一個key是main
,最高溫度儲存在key temp_max
中,而daily forecast
的第一個key是temp
,最高溫度儲存在key max
中。
key Temperature的差異放在一邊,其他都一樣。所以,你真正需要做的是修改daily forecasts的鍵對映。
開啟WXDailyForecast.m
重寫+JSONKeyPathsByPropertyKey
方法:
1 2 3 4 5 6 7 8 9 |
+ (NSDictionary *)JSONKeyPathsByPropertyKey { // 1 NSMutableDictionary *paths = [[super JSONKeyPathsByPropertyKey] mutableCopy]; // 2 paths[@"tempHigh"] = @"temp.max"; paths[@"tempLow"] = @"temp.min"; // 3 return paths; } |
- 獲取
WXCondition
的對映,並建立它的可變副本。 - 你需要為daily forecast做的是改變max和min鍵對映。
- 返回新的對映。
構建並執行您的App,看起來和上次執行沒什麼改變,但好的一點是,App編譯和執行沒有任何錯誤。
何去何從?
你可以從這裡下載完整程式。
在這部分教程中,您使用Cocoapods設定專案,增加檢視到控制器,編排檢視,並建立模型來反映你抓取的氣象資料。該App還沒有充分發揮作用,但是你成功用純程式碼建立檢視,並學習瞭如何使用Mantle對映和轉換JSON資料。
接下來看看教程的第二部分,你將充實你的App,從weather API獲取資料,並在UI上顯示。您將使用新的iOS7 NSURLSession去下載資料,以及使用ReactiveCocoa
把位置查詢,天氣資料抓取和UI更新事件綁在一起。