久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

iOS7 最佳實踐:一個天氣應用案例(下)

 gljin_cn 2014-03-13
         原文出處: raywenderlich   譯文出處: sjpsega (@IORI_YAGAMI_7),。歡迎加入技術翻譯小組,。

開始

你有兩個選擇開始本教程:您可以使用在本教程的第1部分你已完成的項目,或者你可以在這里下載第1部分已完成的項目,。

在前面的教程中你創(chuàng)建了你的App的天氣模型 – 現(xiàn)在你需要使用OpenWeatherMap API為你的App來獲取一些數(shù)據(jù),。你將使用兩個類抽象數(shù)據(jù)抓取、分析,、存儲:WXClientWXManager,。

WXClient的唯一責任是創(chuàng)建API請求,并解析它們,;別人可以不用擔心用數(shù)據(jù)做什么以及如何存儲它,。劃分類的不同工作職責的設計模式被稱為關注點分離,。這使你的代碼更容易理解,擴展和維護,。

與ReactiveCocoa工作

確保你使用SimpleWeather.xcworkspace,打開WXClient.h并增加imports

1
2
@import CoreLocation;
#import <ReactiveCocoa/ReactiveCocoa/ReactiveCocoa.h>
1
注意:您可能之前沒有見過的@import指令,,它在Xcode5中被引入,是由蘋果公司看作是一個現(xiàn)代的,,更高效的替代 #import,。有一個非常好的教程,涵蓋了最新的Objective-C特性-[What’s New in Objective-C and Foundation in iOS 7](http://www./49850/whats-new-in-objective-c-and-foundation-in-ios-7),。

WXClient.h中添加下列四個方法到接口申明:

1
2
3
4
5
@import Foundation;
- (RACSignal *)fetchJSONFromURL:(NSURL *)url;
- (RACSignal *)fetchCurrentConditionsForLocation:(CLLocationCoordinate2D)coordinate;
- (RACSignal *)fetchHourlyForecastForLocation:(CLLocationCoordinate2D)coordinate;
- (RACSignal *)fetchDailyForecastForLocation:(CLLocationCoordinate2D)coordinate;

現(xiàn)在,,似乎是一個很好的機會來介紹ReactiveCocoa

ReactiveCocoa(RAC)是一個Objective-C的框架,,用于函數(shù)式反應型編程,,它提供了組合和轉化數(shù)據(jù)流的API。代替專注于編寫串行的代碼 – 執(zhí)行有序的代碼隊列 – 可以響應非確定性事件,。

GitHub上提供的a great overview of the benefits

  • 對未來數(shù)據(jù)的進行組合操作的能力,。
  • 減少狀態(tài)和可變性。
  • 用聲明的形式來定義行為和屬性之間的關系,。
  • 為異步操作帶來一個統(tǒng)一的,,高層次的接口。
  • 在KVO的基礎上建立一個優(yōu)雅的API,。

例如,,你可以監(jiān)聽username屬性的變化,用這樣的代碼:

1
2
3
[RACAble(self.username) subscribeNext:^(NSString *newName) {
    NSLog(@"%@", newName);
}];

subscribeNext這個block會在self.username屬性變化的時候執(zhí)行,。新的值會傳遞給這個block,。

您還可以合并信號并組合數(shù)據(jù)到一個組合數(shù)據(jù)中。下面的示例取自于ReactiveCocoa的github頁面:

1
2
3
4
5
6
7
8
[[RACSignal
    combineLatest:@[ RACAble(self.password), RACAble(self.passwordConfirmation) ]
           reduce:^(NSString *currentPassword, NSString *currentConfirmPassword) {
               return [NSNumber numberWithBool:[currentConfirmPassword isEqualToString:currentPassword]];
           }]
    subscribeNext:^(NSNumber *passwordsMatch) {
        self.createEnabled = [passwordsMatch boolValue];
    }];

RACSignal對象捕捉當前和未來的值,。信號可以被觀察者鏈接,,組合和反應。信號實際上不會執(zhí)行,,直到它被訂閱,。

這意味著調用[mySignal fetchCurrentConditionsForLocation:someLocation];不會做什么,但創(chuàng)建并返回一個信號,。你將看到之后如何訂閱和反應,。

打開WXClient.m加入以下imports:

1
2
#import "WXCondition.h"
#import "WXDailyForecast.h"

在imports下,添加私有接口:

1
2
3
4
5
@interface WXClient ()
@property (nonatomic, strong) NSURLSession *session;
@end

這個接口用這個屬性來管理API請求的URL session,。

添加以下init放到到@implementation@end之間:

1
2
3
4
5
6
7
- (id)init {
    if (self = [super init]) {
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        _session = [NSURLSession sessionWithConfiguration:config];
    }
    return self;
}

使用defaultSessionConfiguration為您創(chuàng)建session,。

1
注意:如果你以前沒有了解過NSURLSession,看看我們的[NSURLSession教程](http://www./51127/nsurlsession-tutorial),,了解更多信息,。

構建信號

你需要一個主方法來建立一個信號從URL中取數(shù)據(jù),。你已經知道,需要三種方法來獲取當前狀況,,逐時預報及每日預報,。

不是寫三個獨立的方法,你可以遵守DRY(Don’t Repeat Yourself)的軟件設計理念,,使您的代碼容易維護,。

第一次看,以下的一些ReactiveCocoa部分可能看起來相當陌生,。別擔心,,你會一塊一塊理解他。

增加下列方法到WXClient.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (RACSignal *)fetchJSONFromURL:(NSURL *)url {
    NSLog(@"Fetching: %@",url.absoluteString);
    // 1
    return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 2
        NSURLSessionDataTask *dataTask = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            // TODO: Handle retrieved data
        }];
        // 3
        [dataTask resume];
        // 4
        return [RACDisposable disposableWithBlock:^{
            [dataTask cancel];
        }];
    }] doError:^(NSError *error) {
        // 5
        NSLog(@"%@",error);
    }];
}

通過一個一個注釋,,你會看到代碼執(zhí)行以下操作:

  1. 返回信號,。請記住,這將不會執(zhí)行,,直到這個信號被訂閱,。 - fetchJSONFromURL:創(chuàng)建一個對象給其他方法和對象使用;這種行為有時也被稱為工廠模式,。
  2. 創(chuàng)建一個NSURLSessionDataTask(在iOS7中加入)從URL取數(shù)據(jù),。你會在以后添加的數(shù)據(jù)解析。
  3. 一旦訂閱了信號,,啟動網(wǎng)絡請求。
  4. 創(chuàng)建并返回RACDisposable對象,,它處理當信號摧毀時的清理工作,。
  5. 增加了一個“side effect”,以記錄發(fā)生的任何錯誤,。side effect不訂閱信號,,相反,他們返回被連接到方法鏈的信號,。你只需添加一個side effect來記錄錯誤,。
1
如果你覺得需要更多一些背景知識,看看由Ash Furrow編寫的[這篇文章](http://www./blog/getting-started-with-reactivecocoa/),,以便更好地了解ReactiveCocoa的核心概念,。

-fetchJSONFromURL:中找到// TODO: Handle retrieved data ,替換為:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (! error) {
    NSError *jsonError = nil;
    id json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];
    if (! jsonError) {
        // 1
        [subscriber sendNext:json];
    }
    else {
        // 2
        [subscriber sendError:jsonError];
    }
}
else {
    // 2
    [subscriber sendError:error];
}
// 3
[subscriber sendCompleted];
  1. 當JSON數(shù)據(jù)存在并且沒有錯誤,,發(fā)送給訂閱者序列化后的JSON數(shù)組或字典,。
  2. 在任一情況下如果有一個錯誤,通知訂閱者,。
  3. 無論該請求成功還是失敗,,通知訂閱者請求已經完成,。

-fetchJSONFromURL:方法有點長,但它使你的特定的API請求方法變得很簡單,。

獲取當前狀況

還在WXClient.m中,,添加如下方法:

1
2
3
4
5
6
7
8
9
10
11
- (RACSignal *)fetchCurrentConditionsForLocation:(CLLocationCoordinate2D)coordinate {
    // 1
    NSString *urlString = [NSString stringWithFormat:@"http://api./data/2.5/weather?lat=%f&lon=%f&units=imperial",coordinate.latitude, coordinate.longitude];
    NSURL *url = [NSURL URLWithString:urlString];
    // 2
    return [[self fetchJSONFromURL:url] map:^(NSDictionary *json) {
        // 3
        return [MTLJSONAdapter modelOfClass:[WXCondition class] fromJSONDictionary:json error:nil];
    }];
}
  1. 使用CLLocationCoordinate2D對象的經緯度數(shù)據(jù)來格式化URL。
  2. 用你剛剛建立的創(chuàng)建信號的方法,。由于返回值是一個信號,,你可以調用其他ReactiveCocoa的方法。 在這里,,您將返回值映射到一個不同的值 – 一個NSDictionary實例,。
  3. 使用MTLJSONAdapter來轉換JSON到WXCondition對象 – 使用MTLJSONSerializing協(xié)議創(chuàng)建的WXCondition

獲取逐時預報

現(xiàn)在添加根據(jù)坐標獲取逐時預報的方法到WXClient.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (RACSignal *)fetchHourlyForecastForLocation:(CLLocationCoordinate2D)coordinate {
    NSString *urlString = [NSString stringWithFormat:@"http://api./data/2.5/forecast?lat=%f&lon=%f&units=imperial&cnt=12",coordinate.latitude, coordinate.longitude];
    NSURL *url = [NSURL URLWithString:urlString];
    // 1
    return [[self fetchJSONFromURL:url] map:^(NSDictionary *json) {
        // 2
        RACSequence *list = [json[@"list"] rac_sequence];
        // 3
        return [<div class="list"></div> fromJSONDictionary:item error:nil];
        // 5
        }] array];
    }];
}

 

  1. 再次使用-fetchJSONFromUR方法,,映射JSON,。注意:重復使用該方法節(jié)省了多少代碼!
  2. 使用JSON的”list”key創(chuàng)建RACSequence,。 RACSequences讓你對列表進行ReactiveCocoa操作,。
  3. 映射新的對象列表。調用-map:方法,,針對列表中的每個對象,,返回新對象的列表。
  4. 再次使用MTLJSONAdapter來轉換JSON到WXCondition對象,。
  5. 使用RACSequence-map方法,,返回另一個RACSequence,所以用這個簡便的方法來獲得一個NSArray數(shù)據(jù),。

獲取每日預報

最后,,添加如下方法到WXClient.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (RACSignal *)fetchDailyForecastForLocation:(CLLocationCoordinate2D)coordinate {
    NSString *urlString = [NSString stringWithFormat:@"http://api./data/2.5/forecast/daily?lat=%f&lon=%f&units=imperial&cnt=7",coordinate.latitude, coordinate.longitude];
    NSURL *url = [NSURL URLWithString:urlString];
    // Use the generic fetch method and map results to convert into an array of Mantle objects
    return [[self fetchJSONFromURL:url] map:^(NSDictionary *json) {
        // Build a sequence from the list of raw JSON
        RACSequence *list = [json[@"list"] rac_sequence];
        // Use a function to map results from JSON to Mantle objects
        return [<div class="list"></div> fromJSONDictionary:item error:nil];
        }] array];
    }];
}

是不是看起來很熟悉?是的,,這個方法與-fetchHourlyForecastForLocation:方法非常像,。除了它使用WXDailyForecast代替WXCondition,并獲取每日預報,。

構建并運行您的App,,現(xiàn)在你不會看到任何新的東西,但這是一個很好機會松一口氣,,并確保沒有任何錯誤或警告,。

builregergerggrret-layout

管理并存儲你的數(shù)據(jù)

現(xiàn)在是時間來充實WXManager,這個類會把所有東西結合到一起,。這個類實現(xiàn)您App的一些關鍵功能:

  • 它使用單例設計模式,。
  • 它試圖找到設備的位置。
  • 找到位置后,它獲取相應的氣象數(shù)據(jù),。

打開WXManager.h使用以下代碼來替換其內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@import Foundation;
@import CoreLocation;
#import <ReactiveCocoa/ReactiveCocoa/ReactiveCocoa.h>
// 1
#import "WXCondition.h"
@interface WXManager : NSObject
<CLLocationManagerDelegate>
// 2
+ (instancetype)sharedManager;
// 3
@property (nonatomic, strong, readonly) CLLocation *currentLocation;
@property (nonatomic, strong, readonly) WXCondition *currentCondition;
@property (nonatomic, strong, readonly) NSArray *hourlyForecast;
@property (nonatomic, strong, readonly) NSArray *dailyForecast;
// 4
- (void)findCurrentLocation;
@end
  1. 請注意,,你沒有引入WXDailyForecast.h,你會始終使用WXCondition作為預報的類,。 WXDailyForecast的存在是為了幫助Mantle轉換JSON到Objective-C,。
  2. 使用instancetype而不是WXManager,子類將返回適當?shù)念愋汀?/li>
  3. 這些屬性將存儲您的數(shù)據(jù),。由于WXManager是一個單例,,這些屬性可以任意訪問。設置公共屬性為只讀,,因為只有管理者能更改這些值,。
  4. 這個方法啟動或刷新整個位置和天氣的查找過程。

現(xiàn)在打開WXManager.m并添加如下imports到文件頂部:

1
2
#import "WXClient.h"
#import <TSMessages/TSMessage.h>

在imports下方,,粘貼如下私有接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface WXManager ()
// 1
@property (nonatomic, strong, readwrite) WXCondition *currentCondition;
@property (nonatomic, strong, readwrite) CLLocation *currentLocation;
@property (nonatomic, strong, readwrite) NSArray *hourlyForecast;
@property (nonatomic, strong, readwrite) NSArray *dailyForecast;
// 2
@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, assign) BOOL isFirstUpdate;
@property (nonatomic, strong) WXClient *client;
@end
  1. 聲明你在公共接口中添加的相同的屬性,,但是這一次把他們定義為可讀寫,因此您可以在后臺更改他們,。
  2. 為查找定位和數(shù)據(jù)抓取聲明一些私有變量,。

添加如下通用的單例構造器到@implementation@end?中間:

1
2
3
4
5
6
7
8
9
+ (instancetype)sharedManager {
    static id _sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedManager = [[self alloc] init];
    });
    return _sharedManager;
}

然后,你需要設置你的屬性和觀察者,。

添加如下方法到WXManager.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
- (id)init {
    if (self = [super init]) {
        // 1
        _locationManager = [[CLLocationManager alloc] init];
        _locationManager.delegate = self;
        // 2
        _client = [[WXClient alloc] init];
        // 3
        [[[[RACObserve(self, currentLocation)
            // 4
            ignore:nil]
            // 5
           // Flatten and subscribe to all 3 signals when currentLocation updates
           flattenMap:^(CLLocation *newLocation) {
               return [RACSignal merge:@[
                                         [self updateCurrentConditions],
                                         [self updateDailyForecast],
                                         [self updateHourlyForecast]
                                         ]];
            // 6
           }] deliverOn:RACScheduler.mainThreadScheduler]
           // 7
         subscribeError:^(NSError *error) {
             [TSMessage showNotificationWithTitle:@"Error"
                                         subtitle:@"There was a problem fetching the latest weather."
                                             type:TSMessageNotificationTypeError];
         }];
    }
    return self;
}

你正使用更多的ReactiveCocoa方法來觀察和反應數(shù)值的變化,。上面這些你做了:

  1. 創(chuàng)建一個位置管理器,并設置它的delegate為self,。
  2. 為管理器創(chuàng)建WXClient對象,。這里處理所有的網(wǎng)絡請求和數(shù)據(jù)分析,這是關注點分離的最佳實踐,。
  3. 管理器使用一個返回信號的ReactiveCocoa腳本來觀察自身的currentLocation,。這與KVO類似,但更為強大,。
  4. 為了繼續(xù)執(zhí)行方法鏈,currentLocation必須不為nil,。
  5. - flattenMap:非常類似于-map:,,但不是映射每一個值,它把數(shù)據(jù)變得扁平,,并返回包含三個信號中的一個對象,。通過這種方式,你可以考慮將三個進程作為單個工作單元,。
  6. 將信號傳遞給主線程上的觀察者,。
  7. 這不是很好的做法,在你的模型中進行UI交互,但出于演示的目的,,每當發(fā)生錯誤時,,會顯示一個banner。

接下來,,為了顯示準確的天氣預報,,我們需要確定設備的位置。

查找你的位置

下一步,,你要添加當位置查找到,,觸發(fā)抓取天氣數(shù)據(jù)的代碼。

添加如下代碼到WXManager.m的實現(xiàn)塊中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)findCurrentLocation {
    self.isFirstUpdate = YES;
    [self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    // 1
    if (self.isFirstUpdate) {
        self.isFirstUpdate = NO;
        return;
    }
    CLLocation *location = [locations lastObject];
    // 2
    if (location.horizontalAccuracy > 0) {
        // 3
        self.currentLocation = location;
        [self.locationManager stopUpdatingLocation];
    }
}
  1. 忽略第一個位置更新,,因為它一般是緩存值,。
  2. 一旦你獲得一定精度的位置,停止進一步的更新,。
  3. 設置currentLocation,,將觸發(fā)您之前在init中設置的RACObservable。

獲取氣象數(shù)據(jù)

最后,,是時候添加在客戶端上調用并保存數(shù)據(jù)的三個獲取方法,。將三個方法捆綁起來,被之前在init方法中添加的RACObservable訂閱,。您將返回客戶端返回的,,能被訂閱的,相同的信號,。

所有的屬性設置發(fā)生在-doNext:中,。

添加如下代碼到WXManager.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (RACSignal *)updateCurrentConditions {
    return [[self.client fetchCurrentConditionsForLocation:self.currentLocation.coordinate] doNext:^(WXCondition *condition) {
        self.currentCondition = condition;
    }];
}
- (RACSignal *)updateHourlyForecast {
    return [[self.client fetchHourlyForecastForLocation:self.currentLocation.coordinate] doNext:^(NSArray *conditions) {
        self.hourlyForecast = conditions;
    }];
}
- (RACSignal *)updateDailyForecast {
    return [[self.client fetchDailyForecastForLocation:self.currentLocation.coordinate] doNext:^(NSArray *conditions) {
        self.dailyForecast = conditions;
    }];
}

它看起來像將一切都連接起來,并蓄勢待發(fā),。別急,!這App實際上并沒有告訴管理者做任何事情。 打開WXController.m并導入這管理者到文件的頂部,,如下所示:

1
#import "WXManager.h"

添加如下代碼到-viewDidLoad:的最后:

1
[[WXManager sharedManager] findCurrentLocation];

這告訴管理類,,開始尋找設備的當前位置。

構建并運行您的App,,系統(tǒng)會提示您是否允許使用位置服務,。你仍然不會看到任何UI的更新,但檢查控制臺日志,,你會看到類似以下內容:

1
2
3
2013-11-05 08:38:48.886 WeatherTutorial[17097:70b] Fetching: http://api./data/2.5/weather?lat=37.785834&lon=-122.406417&units=imperial
2013-11-05 08:38:48.886 WeatherTutorial[17097:70b] Fetching: http://api./data/2.5/forecast/daily?lat=37.785834&lon=-122.406417&units=imperial&cnt=7
2013-11-05 08:38:48.886 WeatherTutorial[17097:70b] Fetching: http://api./data/2.5/forecast?lat=37.785834&lon=-122.406417&units=imperial&cnt=12

這些輸出代表你的代碼工作正常,,網(wǎng)絡請求正常執(zhí)行。

連接接口

這是最后一次展示所有獲取,,映射和存儲的數(shù)據(jù),。您將使用ReactiveCocoa來觀察WXManager單例的變化和當新數(shù)據(jù)到達時更新界面,。

還在WXController.m,到- viewDidLoad的底部,,并添加下面的代碼到[[WXManager sharedManager] findCurrentLocation];之前:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1
[[RACObserve([WXManager sharedManager], currentCondition)
  // 2
  deliverOn:RACScheduler.mainThreadScheduler]
 subscribeNext:^(WXCondition *newCondition) {
     // 3
     temperatureLabel.text = [NSString stringWithFormat:@"%.0f°",newCondition.temperature.floatValue];
     conditionsLabel.text = [newCondition.condition capitalizedString];
     cityLabel.text = [newCondition.locationName capitalizedString];
     // 4
     iconView.image = [UIImage imageNamed:[newCondition imageName]];
 }];
  1. 觀察WXManager單例的currentCondition,。
  2. 傳遞在主線程上的任何變化,因為你正在更新UI,。
  3. 使用氣象數(shù)據(jù)更新文本標簽,;你為文本標簽使用newCondition的數(shù)據(jù),而不是單例,。訂閱者的參數(shù)保證是最新值,。
  4. 使用映射的圖像文件名來創(chuàng)建一個圖像,并將其設置為視圖的圖標,。

構建并運行您的App,,你會看到當前溫度,當前狀況和表示當前狀況的圖標,。所有的數(shù)據(jù)都是實時的,。但是,如果你的位置是舊金山,,它似乎總是約65度,。Lucky San Franciscans! :]

ureferfergfi-wiring

ReactiveCocoa的綁定

ReactiveCocoa為iOS帶來了自己的Cocoa綁定的形式。

不知道是什么綁定,?簡而言之,,他們是一種提供了保持模型和視圖的數(shù)據(jù)同步而無需編寫大量”膠水代碼”的手段,它們允許你建立一個視圖和數(shù)據(jù)塊之間的連接,, “結合”它們,,使得一方的變化反映到另一個中的技術。

這是一個非常強大的概念,,不是嗎,?

1
注意:要獲得更多的綁定實例代碼,請查看[ReactiveCocoa Readme](https://github.com/ReactiveCocoa/ReactiveCocoa),。

添加如下代碼到你上一步添加的代碼后面:

1
2
3
4
5
6
7
8
9
10
11
// 1
RAC(hiloLabel, text) = [[RACSignal combineLatest:@[
                        // 2
                        RACObserve([WXManager sharedManager], currentCondition.tempHigh),
                        RACObserve([WXManager sharedManager], currentCondition.tempLow)]
                        // 3
                        reduce:^(NSNumber *hi, NSNumber *low) {
                            return [NSString  stringWithFormat:@"%.0f° / %.0f°",hi.floatValue,low.floatValue];
                        }]
                        // 4
                        deliverOn:RACScheduler.mainThreadScheduler];

上面的代碼結合高溫,、低溫的值到hiloLabel的text屬性??纯茨阃瓿闪耸裁矗?/p>

  1. RAC(…)宏有助于保持語法整潔,。從該信號的返回值將被分配給hiloLabel對象的text
  2. 觀察currentCondition的高溫和低溫,。合并信號,,并使用兩者最新的值,。當任一數(shù)據(jù)變化時,,信號就會觸發(fā)。
  3. 從合并的信號中,減少數(shù)值,,轉換成一個單一的數(shù)據(jù),,注意參數(shù)的順序與信號的順序相匹配。
  4. 同樣,,因為你正在處理UI界面,,所以把所有東西都傳遞到主線程。

構建并運行你的App,。你應該看到在左下方的高/低溫度label更新了:

ui-wregerggriring-hilo

在Table View中顯示數(shù)據(jù)

現(xiàn)在,,你已經獲取所有的數(shù)據(jù),你可以在table view中整齊地顯示出來,。你會在分頁的table view中顯示最近6小時的每時播報和每日預報,。該App會顯示三個頁面:一個是當前狀況,一個是逐時預報,,以及一個每日預報,。

之前,你可以添加單元格到table view,,你需要初始化和配置一些日期格式化,。

WXController.m最頂端的私有接口處,添加下列兩個屬性

1
2
@property (nonatomic, strong) NSDateFormatter *hourlyFormatter;
@property (nonatomic, strong) NSDateFormatter *dailyFormatter;

由于創(chuàng)建日期格式化非常昂貴,,我們將在init方法中實例化他們,,并使用這些變量去存儲他們的引用。

還在WXController.m中,,添加如下代碼到@implementation中:

1
2
3
4
5
6
7
8
9
10
- (id)init {
    if (self = [super init]) {
        _hourlyFormatter = [[NSDateFormatter alloc] init];
        _hourlyFormatter.dateFormat = @"h a";
        _dailyFormatter = [[NSDateFormatter alloc] init];
        _dailyFormatter.dateFormat = @"EEEE";
    }
    return self;
}

你可能想知道為什么在-init中初始化這些日期格式化,,而不是在-viewDidLoad中初始化他們。好問題,!

實際上-viewDidLoad可以在一個視圖控制器的生命周期中多次調用,。 NSDateFormatter對象的初始化是昂貴的,而將它們放置在你的-init,,會確保被你的視圖控制器初始化一次,。

WXController.m中,尋找tableView:numberOfRowsInSection:,并用如下代碼更換TODOreturn

1
2
3
4
5
6
// 1
if (section == 0) {
    return MIN([[WXManager sharedManager].hourlyForecast count], 6) + 1;
}
// 2
return MIN([[WXManager sharedManager].dailyForecast count], 6) + 1;
  1. 第一部分是對的逐時預報,。使用最近6小時的預預報,,并添加了一個作為頁眉的單元格。
  2. 接下來的部分是每日預報,。使用最近6天的每日預報,,并添加了一個作為頁眉的單元格。
1
注意:您使用表格單元格作為標題,,而不是內置的,、具有粘性的滾動行為的標題,。這個table view設置了分頁,粘性滾動行為看起來會很奇怪,。

WXController.m找到tableView:cellForRowAtIndexPath:,并用如下代碼更換TODO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if (indexPath.section == 0) {
    // 1
    if (indexPath.row == 0) {
        [self configureHeaderCell:cell title:@"Hourly Forecast"];
    }
    else {
        // 2
        WXCondition *weather = [WXManager sharedManager].hourlyForecast[indexPath.row - 1];
        [self configureHourlyCell:cell weather:weather];
    }
}
else if (indexPath.section == 1) {
    // 1
    if (indexPath.row == 0) {
        [self configureHeaderCell:cell title:@"Daily Forecast"];
    }
    else {
        // 3
        WXCondition *weather = [WXManager sharedManager].dailyForecast[indexPath.row - 1];
        [self configureDailyCell:cell weather:weather];
    }
}
  1. 每個部分的第一行是標題單元格,。
  2. 獲取每小時的天氣和使用自定義配置方法配置cell。
  3. 獲取每天的天氣,,并使用另一個自定義配置方法配置cell,。

最后,添加如下代碼到WXController.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
// 1
- (void)configureHeaderCell:(UITableViewCell *)cell title:(NSString *)title {
    cell.textLabel.font = [UIFont fontWithName:@"HelveticaNeue-Medium" size:18];
    cell.textLabel.text = title;
    cell.detailTextLabel.text = @"";
    cell.imageView.image = nil;
}
// 2
- (void)configureHourlyCell:(UITableViewCell *)cell weather:(WXCondition *)weather {
    cell.textLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:18];
    cell.detailTextLabel.font = [UIFont fontWithName:@"HelveticaNeue-Medium" size:18];
    cell.textLabel.text = [self.hourlyFormatter stringFromDate:weather.date];
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%.0f°",weather.temperature.floatValue];
    cell.imageView.image = [UIImage imageNamed:[weather imageName]];
    cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
}
// 3
- (void)configureDailyCell:(UITableViewCell *)cell weather:(WXCondition *)weather {
    cell.textLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:18];
    cell.detailTextLabel.font = [UIFont fontWithName:@"HelveticaNeue-Medium" size:18];
    cell.textLabel.text = [self.dailyFormatter stringFromDate:weather.date];
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%.0f° / %.0f°",
                                  weather.tempHigh.floatValue,
                                  weather.tempLow.floatValue];
    cell.imageView.image = [UIImage imageNamed:[weather imageName]];
    cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
}
  1. 配置和添加文本到作為section頁眉單元格,。你會重用此為每日每時的預測部分,。
  2. 格式化逐時預報的單元格。
  3. 格式化每日預報的單元格,。

構建并運行您的App,,嘗試滾動你的table view,并…等一下,。什么都沒顯示,!怎么辦?

如果你已經使用過的UITableView,,可能你之前遇到過問題,。這個table沒有重新加載!

為了解決這個問題,,你需要添加另一個針對每時預報和每日預報屬性的ReactiveCocoa觀察,。

WXController.m-viewDidLoad中,添加下列代碼到其他ReactiveCocoa觀察代碼中:

1
2
3
4
5
6
7
8
9
10
11
[[RACObserve([WXManager sharedManager], hourlyForecast)
       deliverOn:RACScheduler.mainThreadScheduler]
   subscribeNext:^(NSArray *newForecast) {
       [self.tableView reloadData];
   }];
[[RACObserve([WXManager sharedManager], dailyForecast)
       deliverOn:RACScheduler.mainThreadScheduler]
   subscribeNext:^(NSArray *newForecast) {
       [self.tableView reloadData];
   }];

構建并運行App,;滾動table view,,你將看到填充的所有預報數(shù)據(jù)。

unalifwefewfegned-heights

給你的App添加效果

本頁面為每時和每日預報不會占滿整個屏幕,。幸運的是,,有一個非常簡單的修復辦法。在本教程前期,,您在-viewDidLoad中獲得屏幕高度,。

WXController.m中,查找table view的委托方法-tableView:heightForRowAtIndexPath:,,并且替換TODOreturn的代碼:

1
2
NSInteger cellCount = [self tableView:tableView numberOfRowsInSection:indexPath.section];
return self.screenHeight / (CGFloat)cellCount;

屏幕高度由一定數(shù)量的cell所分割,,所以所有cell的總高度等于屏幕的高度。

構建并運行你的App,;table view填滿了整個屏幕,,如下所示:

alignrefewfed-heights1

最后要做的是把我在本教程的第一部分開頭提到的模糊效果引入。當你滾動預報頁面,,模糊效果應該動態(tài)顯示,。

添加下列scroll delegate到WXController.m最底部:

1
2
3
4
5
6
7
8
9
10
11
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    // 1
    CGFloat height = scrollView.bounds.size.height;
    CGFloat position = MAX(scrollView.contentOffset.y, 0.0);
    // 2
    CGFloat percent = MIN(position / height, 1.0);
    // 3
    self.blurredImageView.alpha = percent;
}
  1. 獲取滾動視圖的高度和內容偏移量,。與0偏移量做比較,因此試圖滾動table低于初始位置將不會影響模糊效果,。
  2. 偏移量除以高度,并且最大值為1,,所以alpha上限為1,。
  3. 當你滾動的時候,把結果值賦給模糊圖像的alpha屬性,,來更改模糊圖像,。

構建并運行App,滾動你的table view,,并查看這令人驚異的模糊效果:

with-efewfweffblur

何去何從,?

在本教程中你已經完成了很多內容:您使用CocoaPods創(chuàng)建了一個項目,完全用代碼書寫了一個視圖結構,,創(chuàng)建數(shù)據(jù)模型和管理類,,并使用函數(shù)式編程將他們連接到一起!

您可以從這里下載該項目的完成版本,。

這個App還有很多酷的東西可以去做,。一個好的開始是使用Flickr API來查找基于設備位置的背景圖像。

還有,,你的應用程序只處理溫度和狀態(tài);有什么其他的天氣信息能融入你的App,?


    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,,謹防詐騙,。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報,。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多