轉自:sjpsega's Blog
iOS7最佳實踐:一個天氣App案例(一)
iOS7最佳實踐:一個天氣App案例(二)
注:本文譯自:raywenderlich ios-7-best-practices-part-1,去除了跟主題無關的寒暄部分。
在這個兩部分的系列教程中,您將探索如何使用以下工具和技術來創建自己的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。為此,打開命令行程序,並輸入。
- which pod
你將會看到類似這樣的輸出:
- /usr/bin/pod
這決定於你如何管理Ruby gems,例如你使用rbenv或RVM,路徑可能有所不同。
如果命令行簡單的返回提示,或顯示pod not found,表示Cocoapods未安裝在你的機器上。可以查看我們的Cocoapods教程作為安裝說明。這也是一個很好的資源,如果你想更多得了解Cocoapods的話。
Podfiles是用來告訴Cocoapods哪些開源項目需要導入。
要創建你的第一個Cocoapod,首先在命令行中用cd命令導航到你的XCode項目所在的文件夾,在命令行中啟動編輯器,輸入
- platform :ios, '7.0'
- pod 'Mantle'
- pod 'LBBlurredImage'
- pod 'TSMessages'
- pod 'ReactiveCocoa'
這文件做了兩件事情:
1.告訴Cocoapods你的目標平台與版本,這裡的你目標是iOS 7.0。
2.列給Cocoapods一個項目所有需要引入和安裝的三方庫清單。
在命令行中輸入pod install進行安裝。
這可能需要花一到兩分鐘的時間去安裝各種包。你的命令行應該輸出如下所示:
- $ 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`.
- sjpsega注:若你之前安裝過Cocoapods的話,這裡安裝報錯的話,可以看看http://blog.cocoapods.org/Repairing-Our-Broken-Specs-Repository/ 修復問題
Cocoapods會在你的項目目錄中創建一堆新文件,但是,只有一個需要你關心,SimpleWeather.xcworkspace。
用Xcode打開SimpleWeather.xcworkspace。看看你的項目設置,現在有一個Pods項目在你的項目工作區,以及在Pods文件夾放著每一個你引入的庫,如下所示:
確保你已經選擇SimpleWeather項目,如圖所示: Select SimpleWeather Project
構建並運行您的App,以確保一切工作正常:
- 提示:您可能會注意到有一些項目生成警告。因為Cocoapods引入的項目,是由不同的開發者開發,並且不同的開發者對生成警告有不同的態度。通常,你應該可以忽略它們。只要確保沒有任何編譯器錯誤!
創建你的主視圖控制器
雖然這個App看起來復雜,但它還會通過一個單一的View Controller完成。現在,你將添加他。
選中SimpleWeather項目,單擊File/New/File,並且選擇Cocoa Touch/Objective-C class. 命名為WXController,並設置為UIViewController的子類。
確保Targeted for iPad和With XIB for user interface都沒有選中,如下圖所示: Create WXController
打開WXController.m然後用如下所示替換-viewDidLoad方法:
- - (void)viewDidLoad {
- [super viewDidLoad];
- // Remove this later
- self.view.backgroundColor = [UIColor redColor];
- }
現在打開AppDelegate.m,並且引入如下兩個class:
- #import "WXController.h"
- #import
眼尖的讀者會注意到WXController使用引號引入,TSMessage使用單括號引入。
回頭看下當你創建Podfile的時候,你使用Cocoapods引入TSMessage。Cocoapods創建TSMessage項目,並把它加入到工作空間。既然你從工作區的其他項目導入,可以使用尖括號代替引號。
代替-application:didFinishLaunchingWithOptions的內容:
- - (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;
- }
標號注釋的解釋:
1.初始化並設置WXController實例作為App的根視圖控制器。通常這個控制器是一個的UINavigationController或UITabBarController,但在當前情況下,你使用WXController的單個實例。
2.設置默認的視圖控制器來顯示你的TSMessages。這樣做,你將不再需要手動指定要使用的控制器來顯示警告。
構建並運行,看看你的新視圖控制器起作用了。
在紅色背景下,狀態欄有點不夠清晰。幸運的是,有一個簡單的方法,使狀態欄更清晰易讀。
在iOS7,UIViewController有一個新的API,用來控制狀態欄的外觀。打開WXController,直接添加下面的代碼到-viewDidLoad:方法下:
- - (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, 添加如下委托協議:
- /usr/bin/pod
0
現在打開WXController.m。 小提示:你可以使用Control-Command-Up的快捷鍵來實現.h和.m文件之間的快速切換。
添加如下代碼到WXController.m頂部:
- /usr/bin/pod
1
LBBlurredImage.h包含在Cocoapods引入的LBBlurredImage項目,你會使用這個庫來模糊背景圖片。
應該有一個空的私有接口樣板在WXController imports的下方。它具有以下屬性:
- /usr/bin/pod
2
現在,是時候在項目中創建並設置視圖。
下面是你App的分解圖,記住,table view將是透明的:
為了實現動態模糊效果,在你的App中,你會根據App的滾動來改變模糊圖像的alpha值。
打開WXController.m,使用如下代碼來,替換掉-viewDidLoad中設置背景色的代碼:
- /usr/bin/pod
3
這是非常簡單的代碼:
1.獲取並存儲屏幕高度。之後,你將在用分頁的方式來顯示所有天氣??數據時,使用它。
2.創建一個靜態的背景圖,並添加到視圖上。
3.使用LBBlurredImage來創建一個模糊的背景圖像,並設置alpha為0,使得開始backgroundImageView是可見的。
4.創建tableview來處理所有的數據呈現。 設置WXController為delegate和dataSource,以及滾動視圖的delegate。請注意,設置pagingEnabled為YES。
添加如下UITableView的delegate和dataSource的代碼到WXController.m的@implementation塊中:
- /usr/bin/pod
4
Pragma mark是組織代碼的很好的一種方式。
你的table view有兩個部分,一個是每小時的天氣預報,另一個用於每日播報。table view的section數目,設置為2。
天氣預報的cell是不可選擇的。給他們一個半透明的黑色背景和白色文字。
- /usr/bin/pod
5
最後,添加如下代碼到WXController.m:
- /usr/bin/pod
6
在WXController.m中,你的視圖控制器調用該方法來編排其子視圖。
構建並運行你的App,看看你的視圖如何堆疊。
仔細看,你會看到所有空的table cell的cell分隔線。
仍然在-viewDidLoad中,添加下面的代碼來設置你的布局框架和邊距:
- /usr/bin/pod
7
這是相當常規設置代碼,但這裡是怎麼回事:
1.設置table的header大小與屏幕相同。你將利用的UITableView的分頁來分隔頁面頁頭和每日每時的天氣預報部分。
2.創建inset(或padding)變量,以便您的所有標簽均勻分布並居中。
3.創建並初始化為各種視圖創建的高度變量。設置這些值作為常量,使得可以很容易地在需要的時候,配置和更改您的視圖設置。
4.使用常量和inset變量,為label和view創建框架。
5.復制圖標框,調整它,使文本具有一定的擴展空間,並將其移動到該圖標的右側。當我們把標簽添加到視圖,你會看到布局的效果。
添加如下代碼到-viewDidLoad:
- /usr/bin/pod
8
這是相當長的一塊代碼,但它真的只是在做設置各種控件的繁重工作。簡單的說:
1.設置當前view為你的table header。
2.構建每一個顯示氣象數據的標簽。
3.添加一個天氣圖標的圖像視圖。
構建並運行你的App,你應該可以看到你之前布局的所有所有view。下面的屏幕截圖顯示了使用手工布局的、所有標簽框在視覺上的顯示。
用手指輕輕推動table,當你滾動它的時候,應該會反彈。
獲取氣象數據
你會注意到,App顯示“Loading…”,但它不是真正地在工作。是時候獲取一些真正的天氣數據。
你會從OpenWeatherMap的API拉取數據。 OpenWeatherMap是一個非常棒的服務,旨在提供實時,准確,免費的天氣數據給任何人。雖然有很多天氣API,但他們大多要麼使用較舊的數據格式,如XML,或是有償服務 – 並且有時還相當昂貴。
你會遵循以下基本步驟,來獲你設備的位置的氣象數據:
1.找到設備的位置
2.從API端下載JSON數據
3.映射JSON到WXConditions和WXDailyForecasts
4.告訴UI有新數據了
開始創建你的天氣模型和數據管理類。單擊File/New/File…並選擇Cocoa Touch/Objective-C class。命名為WXClient並使其為NSObject的子類。
這樣再做三次創建以下類:
1.WXManager作為NSObject的子類
2.WXCondition作為MTLModel的子類
3.WXDailyForecast作為WXCondition的子類
全部完成?現在,你可以開始下一節,其中涉及映射和轉換您的天氣數據。
創建你的天氣模型
你的模型將使用Mantle,這使得數據映射和轉型非常簡單。
打開WXCondition.h如下列代碼,修改接口:
- /usr/bin/pod
9
MTLJSONSerializing協議告訴Mantle序列化該對象如何從JSON映射到Objective-C的屬性。
這些都是你的天氣數據的屬性。你將會使用這些屬性的get set方法,但是當你要擴展App,這是一種很好的方法來訪問數據。
這是一個簡單的輔助方法,從天氣狀況映射到圖像文件。
構建並運行App。失敗了……
原因是你沒有從你的Cocoapods項目中引入Mantle。解決方法是,在WXCondition.h中,你需要把MTLModel.h替換為#import 。
現在構建並運行App。成功了。你會看到一些新的警告,但你可以忽略他們。
首先,你需要處理未實現的-imageName方法。
打開WXCondition.m,添加如下方法:
- platform :ios, '7.0'
- pod 'Mantle'
- pod 'LBBlurredImage'
- pod 'TSMessages'
- pod 'ReactiveCocoa'
0
創建一個靜態的NSDictionary,因為WXCondition的每個實例都將使用相同的數據映射。
天氣狀況與圖像文件的關系(例如“01d”代表“weather-clear.png”)。
聲明獲取圖像文件名的公有方法。
看一看從OpenWeatherMap返回的JSON響應數據:
- platform :ios, '7.0'
- pod 'Mantle'
- pod 'LBBlurredImage'
- pod 'TSMessages'
- pod 'ReactiveCocoa'
1
你需要把嵌套的JSON值映射到Objective-C的屬性。嵌套的JSON值是元素,如溫度,即上面看到的main節點。
要做到這一點,你將利用的Objective-C的Key-Value Coding和Mantle的MTLJSONAdapter。
還在WXCondition.m,通過添加+JSONKeyPathsByPropertyKey方法,“JSON到模型屬性”的映射,且該方法是MTLJSONSerializing協議的require。
- platform :ios, '7.0'
- pod 'Mantle'
- pod 'LBBlurredImage'
- pod 'TSMessages'
- pod 'ReactiveCocoa'
2
在這個方法裡,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屬性設置的轉換器。
- platform :ios, '7.0'
- pod 'Mantle'
- pod 'LBBlurredImage'
- pod 'TSMessages'
- pod 'ReactiveCocoa'
3
使用blocks做屬性的轉換的工作,並返回一個MTLValueTransformer返回值。
您只需要詳細說明Unix時間和NSDate之間進行轉換一次,就可以重用-dateJSONTransformer方法為sunrise和sunset屬性做轉換。
下一個值轉型有點討厭,但它只是使用OpenWeatherMap的API,並自己的格式化JSON響應方式的結果。weather鍵對應的值是一個JSON數組,但你只關注單一的天氣狀況。
在WXCondition.m中,使用dateJSONTransformer相同的結構,您可以創建一個NSArray和NSString的之間的轉換。該解決方案提供如下:
- platform :ios, '7.0'
- pod 'Mantle'
- pod 'LBBlurredImage'
- pod 'TSMessages'
- pod 'ReactiveCocoa'
4
最後的轉換器只是為了格式化。 OpenWeatherAPI使用每秒/米的風速。由於您的App使用英制系統,你需要將其轉換為每小時/英裡。
在WXCondition.m的實現中添加以下轉換器的方法和宏定義。
- platform :ios, '7.0'
- pod 'Mantle'
- pod 'LBBlurredImage'
- pod 'TSMessages'
- pod 'ReactiveCocoa'
5
在OpenWeatherMap的API中有一個小的差異,你必須處理。看一看在位於當前狀況的響應和每日預測反應之間的溫度:
- platform :ios, '7.0'
- pod 'Mantle'
- pod 'LBBlurredImage'
- pod 'TSMessages'
- pod 'ReactiveCocoa'
6
current的第一個key是main,最高溫度存儲在key temp_max中,而daily forecast的第一個key是temp,最高溫度存儲在key max中。
key Temperature的差異放在一邊,其他都一樣。所以,你真正需要做的是修改daily forecasts的鍵映射。
打開WXDailyForecast.m重寫+JSONKeyPathsByPropertyKey方法:
- platform :ios, '7.0'
- pod 'Mantle'
- pod 'LBBlurredImage'
- pod 'TSMessages'
- pod 'ReactiveCocoa'
7
獲取WXCondition的映射,並創建它的可變副本。
你需要為daily forecast做的是改變max和min鍵映射。
返回新的映射。
構建並運行您的App,看起來和上次運行沒什麼改變,但好的一點是,App編譯和運行沒有任何錯誤。
何去何從?
你可以從這裡這裡完整程序。
在這部分教程中,您使用Cocoapods設置項目,增加視圖到控制器,編排視圖,並建立模型來反映你抓取的氣象數據。該App還沒有充分發揮作用,但是你成功用純代碼創建視圖,並學習了如何使用Mantle映射和轉換JSON數據。
接下來看看教程的第二部分,你將充實你的App,從weather API獲取數據,並在UI上顯示。您將使用新的iOS7 NSURLSession去下載數據,以及使用ReactiveCocoa把位置查找,天氣數據抓取和UI更新事件綁在一起。
iOS 7最佳實踐:一個天氣App案例