IOS_地圖_定位_天氣預報_Block回調_單例 – iPhone手機開發技術文章 iPhone軟體開發教學課程

H:/1021/00_block回調.h

/*
	通過block回調
 定義block代碼塊,目的是解析完成之後調用
 返回值是 void
 參數是 數組,裡面的每個成員是一個NSString*/

typedef void(^WeatherFinishedBlock)(NSArray *dataList);

@interface WeatherXMLPaser : NSObject
// 解析器解析數據,參數1是要解析的數據,參數2是解析完畢回調的代碼塊
- (void)parserWeatherData:(NSData *)data 
			completion:(WeatherFinishedBlock)completion;
@end

//--------------------------------------------------------
@interface WeatherXMLPaser() 
{
    
	// 成員記住block代碼塊
    WeatherFinishedBlock  _FinishedBlock;    
}
@end
//---------------------------------------------------------
#pragma mark - 成員方法
#pragma mark 解析器解析數據,參數1是要解析的數據,參數2是完畢時調用的代碼塊
- (void)parserWeatherData:(NSData *)data 
				completion:(WeatherFinishedBlock)completion
{
    // 0. 記錄塊代碼
    _FinishedBlock = completion;
    // 1. 實例化XML解析器
    NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
    // 2. 設置代理為 當前的WeatherXMLPaser
    [parser setDelegate:self];
    // 3. 解析器開始解析
    [parser parse];
}
//--------------------------------------------------------
#pragma mark - XML解析代理方法,結束解析文檔
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
    // 解析結束,調用代碼塊,參數是,解析完的成員NSString數組
	// 通過block代碼塊回調,通知調用方解析結果
    _FinishedBlock(_dataList);
}
//--------------------------------------------------------
// 1) 實例化單例 天氣XML解析器
    WeatherXMLPaser *parser = [WeatherXMLPaser sharedWeatherXMLPaser];
	// 2)解析器解析數據,參數1是要解析的數據,參數2是解析完畢要執行的代碼塊
			// 並且將解析完的數組 作為參數傳遞進來
    [parser parserWeatherData:data completion:^(NSArray *dataList) {
		// 解析完成瞭,打印輸出
        Weather *w = [Weather weatherWithArray:dataList];
    }];

H:/1021/00_Singleton單例.m

/*
	單例3步曲
	1,靜態實例變量
	2,類方法,allocWithZone
	3,類方法,sharedXXX
*/

// 單例第1步:靜態實例變量 
static WeatherXMLPaser *sharedInstance;

// 單例第2步:類方法,allocWithZone
+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super allocWithZone:zone];
    });
    return sharedInstance;
}

// 單例第3步:類方法,shared方法
+ (WeatherXMLPaser *)sharedWeatherXMLPaser
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
		// alloc方法會自動調用allocWithZone方法
        sharedInstance = [[WeatherXMLPaser alloc]init];
    });
    return sharedInstance;
}

H:/1021/01_地圖_MainViewController.m

//  MainViewController.m
//  01.地圖
//  Created by apple on 13-10-21.
//  Copyright (c) 2013年 itcast. All rights reserved.
/*
#import 
@interface MainViewController : UIViewController
@end
*/
#import "MainViewController.h"
#import 
//#import "MyAnnotation.h"
#import "MyAnnotation2.h"
@interface MainViewController () 
{
    MKMapView *_mapView;
}
@end
@implementation MainViewController
/**
 1. 地圖跟蹤模式
 
 MKUserTrackingMode None = 0             不跟蹤用戶位置
 MKUserTrackingMode Follow               跟蹤用戶位置
 MKUserTrackingMode FollowWithHeading    帶方向跟蹤用戶位置(汽車車頭方向)
 
 2. 地圖模式
 MKMapType Standard = 0,                 標準地圖(最省電的模式)
 MKMapType Satellite,                    衛星地圖
 MKMapType Hybrid                        混合地圖(最費電)
 
 3. 設置地圖顯示區域,距離以米為單位(iOS7升級的,不再自動調整地圖現實比例)
 MKCoordinateRegion MKCoordinateRegionMakeWithDistance
 
 4. 添加大頭針
 addAnnotation:(id )annotation
 
 凡是遵守MKAnnotation協議的對象都可以成為大頭針
 
 5. 自定義大頭針,地圖視圖是支持大頭針視圖重用的!
 
 如果在mapView:(MKMapView *)mapView viewForAnnotation:
									(id)annotation
 方法中,返回nil,地圖視圖會使用默認的方法繪制大頭針
 
 如果重寫瞭mapView:viewForAnnotation方法,在程序中,調用
 addAnnotation:annotation方法時,
			annotation會以參數的形式傳遞給自定義大頭針視圖的方法
 
 提示:如果是自定義大頭針視圖,需要設置canShowCallout屬性,
									才能夠和視圖進行交互
 
 6. 如果重寫瞭mapView:viewForAnnotation方法
 
 跟蹤用戶信息時,同樣會調用該方法!
 
 如果既要跟蹤用戶信息,同時又要顯示大頭針
 (譬如:顯示汽車位置,同時顯示加油站的大頭針)
 如果傳入的annotation不是自定義大頭針視圖,直接返回nil,
 使用地圖默認的方法繪制大頭針
 如果是自定義視圖,則設置大頭針屬性
 */
#pragma mark - 實例化視圖
- (void)loadView
{	
	// 設置全屏
    self.view = [[UIView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame];
    // 1. 實例化地圖視圖
    MKMapView *mapView = [[MKMapView alloc]initWithFrame:self.view.bounds];
    [self.view addSubview:mapView];
	// 設置MapView的代理為當前控制器
    [mapView setDelegate:self];
    // 2. 設置跟蹤用戶位置的模式
    [mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
    // 3. 設置地圖的類型
    [mapView setMapType:MKMapTypeHybrid];
    _mapView = mapView;
}
- (void)viewDidLoad
{
    [super viewDidLoad];
	// 根據經緯度,生成Coordinate2D坐標
    CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(+30.06055580, +116.34273116);
	// 默認大頭針,通過Coordinate2D生成大頭針 annotation
    MyAnnotation *annotation = [[MyAnnotation alloc]initWithCoordinate:coord title:@"我的地盤" subtitle:nil];
	// 自定義大頭針,可以通過setter方法生成大頭針 annotation2
    MyAnnotation2 *annotation = [[MyAnnotation2 alloc]init];
    [annotation setCoordinate:coord];
    [annotation setTitle:@"我的地盤"];
    [annotation setIcon:@"head0.png"];
    NSLog(@"%p %@", annotation, annotation);
    // 添加大頭針到mapView
    [_mapView addAnnotation:annotation];
    // 自定義大頭針,可以通過setter方法生成大頭針 annotation2
    MyAnnotation2 *annotation2 = [[MyAnnotation2 alloc]init];
	// 根據經緯度,生成Coordinate2D坐標
    CLLocationCoordinate2D coord2 = CLLocationCoordinate2DMake(+50.06055580, +116.34273116);
    [annotation2 setCoordinate:coord2];
    [annotation2 setTitle:@"MJ"];
    [annotation2 setIcon:@"head0.png"];
    // 添加大頭針到mapView
    [_mapView addAnnotation:annotation2];
    // 根據Coordinate2D坐標的經緯度,生成Location對象
    CLLocation *location1 = [[CLLocation alloc]initWithLatitude:coord.latitude longitude:coord.longitude];
	// 根據Coordinate2D坐標的經緯度,生成Location對象
    CLLocation *location2 = [[CLLocation alloc]initWithLatitude:coord2.latitude longitude:coord2.longitude];
	// 計算兩個Location對象之間的距離
    CLLocationDistance distance = [location1 distanceFromLocation:location2];
    NSLog(@"兩點間距離 %f", distance);
}
#pragma mark - 地圖代理方法
#pragma mark 每次用戶位置變化都會被調用,意味著非常費電
- (void)mapView:(MKMapView *)mapView
			didUpdateUserLocation:(MKUserLocation *)userLocation
{
    NSLog(@"%@ %@", userLocation.location, userLocation.title);
    // 利用location中的經緯度設置地圖顯示的坐標區域CoordinateRegion
	// 參數2,和參數3的意思是:X,Y半徑
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(
							userLocation.location.coordinate, 100.0, 100.0);
    // 設置地圖的顯示區域,以用戶所在位置為中心點,半徑為100米
    [mapView setRegion:region animated:YES];
}
#pragma mark - 自定義大頭針視圖,參數中的annotation就是添加到mapView的大頭針
- (MKAnnotationView *)mapView:(MKMapView *)mapView
					  viewForAnnotation:(id)annotation
{
    // 如果傳入的annotation不是自定義大頭針視圖,直接返回nil,
	// 即使用地圖默認的方法繪制大頭針
    // 如果是自定義視圖,才要設置大頭針屬性,牢記~~~~
    if (![annotation isKindOfClass:[MyAnnotation2 class]]) {
        return nil;
    }
	// 同cell,標準優化代碼
    static NSString *ID = @"ID";
    MKAnnotationView *view = [mapView
				dequeueReusableAnnotationViewWithIdentifier:ID];
    // 如果沒有找到可重用的大頭針視圖,才實例化新的
    if (view == nil) {
        view = [[MKAnnotationView alloc]initWithAnnotation:annotation
										reuseIdentifier:ID];
        // 點擊大頭針,可以突顯出來
		view.canShowCallout = YES;
    }
    // 設置大頭針視圖獨一無二的屬性
    // 1) 如果大頭針視圖是從緩沖池取出的,必須要重新設置大頭針
    [view setAnnotation:annotation];
    // 2) 設置大頭針圖像,需手動轉成MyAnnotation2 *,才能使用子類的特有屬性
    [view setImage:[UIImage imageNamed:((MyAnnotation2 *)annotation).icon]];
    return view;
}
@end

H:/1021/01_地圖_MyAnnotation.m

//  MyAnnotation.m
//  01.地圖
//  Created by apple on 13-10-21.
//  Copyright (c) 2013年 itcast. All rights reserved.
/*
#import 
#import 
// 大頭針,遵守協議 
@interface MyAnnotation : NSObject 
// 提示,因為要給對象屬性賦值,所以此處實例化對象方法不能用工廠方法,
		原因就是類方法中,無法訪問對象的成員變量
// 坐標,標題,子標題
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate 
					title:(NSString *)title subtitle:(NSString *)subtitle;
@end
*/
#import "MyAnnotation.h"
@interface MyAnnotation()
{
    CLLocationCoordinate2D  _coordinate;
    NSString                *_title;
    NSString                *_subtitle;
}
@property (strong, nonatomic) NSString *strong_str1;
@property (copy, nonatomic) NSString *copy_str2;
@end
@implementation MyAnnotation
/*
 copy   copy常用於NSString,目的是改變新的不影響舊的
		copy出來的對象是不可變對象,
		而mutableCopy出來的是可變對象
		因此,隻有對不可變對象進行copy的時候,相當於retain
		
		屬性是非arc的,但是在arc中同樣可以使用,表示對象是可以復制的
        使用copy描述符,在給對象賦值時,會建立對象的副本
 
        在非arc開發中,字符串類型NSString通常使用copy描述符
 
        copy屬性通常被稱為深復制
 strong 屬於arc的,在非arc中不可以使用,等同於非arc中的retain
        使用strong描述符,在給對象賦值時,會建立對象的指針副本

        strong屬性通常被稱為淺復制
 
        在性能上strong會略微比copy要好,建議大傢在日常開發中使用strong。
 */
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate
					title:(NSString *)title subtitle:(NSString *)subtitle
{
    if (self = [super init]) {
		// 為成員賦值
        _coordinate = coordinate;
        _title = title;
        _subtitle = subtitle;
        
        NSMutableString *string = [NSMutableString string];
        [string setString:@"oldValue"];
        // strong 淺拷貝,隻是建立指針副本		
        self.strong_str1 = string;
		// copy 深拷貝,建立對象副本
        self.copy_str2 = string;
		// 因此,string和strong_str1兩個指針指向的是同一個地址
		// 而copy_str2指向的是一個新的地址(新復制的對象的地址)
        NSLog(@"%p %p %p", string, self.strong_str1, self.copy_str2);
        
        [string setString:@"newValue"];
		// string和strong_str1指向同一個,故結果是newValue
		// copy_str2指向的是一個新開的地址,故結果依然是oldValue
        NSLog(@"%@ %@ %@", string, self.strong_str1, self.copy_str2);
    }
    return self;
}
#pragma mark - 隻讀屬性,即隻有getter方法
- (CLLocationCoordinate2D)coordinate
{
    return _coordinate;
}
- (NSString *)title
{
    return _title;
}
- (NSString *)subtitle
{
    return _subtitle;
}
@end

H:/1021/01_地圖_MyAnnotation2.h

//
//  MyAnnotation2.h
//  01.地圖
//
//  Created by apple on 13-10-21.
//  Copyright (c) 2013年 itcast. All rights reserved.
//

#import 
#import 
// 自定義大頭針,遵守協議 
@interface MyAnnotation2 : NSObject 
// 坐標,標題,副標題
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

// 大頭針圖標的名字
@property (nonatomic, strong) NSString *icon;

@end

H:/1021/02_定位_MainViewController.m

//  MainViewController.m
//  02.定位
//  Created by apple on 13-10-21.
//  Copyright (c) 2013年 itcast. All rights reserved.
#import "MainViewController.h"
#import 
@interface MainViewController () 
{
	// 成員:LocationManager定位管理器
    CLLocationManager   *_locationManager;
	// 成員:Geocoder 地理位置編碼器
    CLGeocoder          *_geocoder;
}
@end
@implementation MainViewController
/*
 1. 要使用定位服務,都是從CLLocationManager開始的
 
 2. 在實際應用開發中,需要判斷用戶的定位服務是否打開,
	如果沒有打開,需要提示用戶
    直接用定位管理器的類方法locationServicesEnabled可以判斷。
 
 3. 在大多數情況下CLLocation的精度不如MKMapView高,但是因為不使用UIMapView,
    相對性能較好!
 
 4. 使用CLLocation時,最好設置定位精度,以省電
 
 kCLLocationAccuracy Best;               // 最佳精度(最耗電)
 kCLLocationAccuracy NearestTenMeters;   // 最近10米范圍內定位
 kCLLocationAccuracy HundredMeters;      // 百米
 kCLLocationAccuracy Kilometer;          // 千米
 kCLLocationAccuracy ThreeKilometers;    // 3000米
 
    使用startUpdatingLocation可以開始定位用戶位置
    如果不需要持續跟蹤用戶的行蹤,定位之後,
	最好stopUpdatingLocation替用戶省電!
 
 5. 根據經緯度計算地名
 
 - (void)reverseGeocodeLocation:(CLLocation *)location
		  completionHandler:(CLGeocodeCompletionHandler)completionHandler;
 
 6. 根據地名計算經緯度
 
 - (void)geocodeAddressString:(NSString *)addressString 
			completionHandler:(CLGeocodeCompletionHandler)completionHandler;
 */
- (void)viewDidLoad
{
    [super viewDidLoad];
    // 1. 判斷定位服務是否可用
    if ([CLLocationManager locationServicesEnabled]) {
        // 1) 實例化定位管理器
        _locationManager = [[CLLocationManager alloc]init];
        // 2) 設置定位管理器的代理,當位置變化時,會調用代理的方法
        [_locationManager setDelegate:self];
        // 3) 設置定位管理器的精度
        [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];        
        // 4) 開啟用戶定位功能
        [_locationManager startUpdatingLocation];
		
        // 5) 實例化geocoder
        _geocoder = [[CLGeocoder alloc]init];
        // 根據地名,反向解析出坐標
        [_geocoder geocodeAddressString:@"西湖"
				completionHandler:^(NSArray *placemarks, NSError *error) {
            // placemarks 地點、地標
            CLPlacemark *placemark = placemarks[0];
            NSLog(@"%@ %@", placemark.location, placemark.country);
        }];
    } else {	
        NSLog(@"沒有開啟定位服務");
    }
}
#pragma mark - 定位管理器代理方法
#pragma mark 更新位置,隻要用戶的位置發生變化,就會被調用,非常費電!
- (void)locationManager:(CLLocationManager *)manager
							didUpdateLocations:(NSArray *)locations
{
	// 數組locations中隻有一個位置
    NSLog(@"%@", locations[0]);
	// 根據地名,反向解析出坐標
    [_geocoder reverseGeocodeLocation:locations[0]
			   completionHandler:^(NSArray *placemarks, NSError *error) {
        // placemarks 地點、地標
        CLPlacemark *placemark = placemarks[0];
		// 中國北京市昌平區回龍觀地區建材城西路67號
        NSLog(@"%@", placemark);
    }];
}
@end

H:/1021/03_天氣預報_MainViewController.m

//  MainViewController.m
//  03.天氣預報
//  Created by apple on 13-10-21.
//  Copyright (c) 2013年 itcast. All rights reserved.
#import "MainViewController.h"
#import 
#import 
#import "WeatherXMLPaser.h"
#import "Weather.h"
#import "WeatherAnnonation.h"
/*
	天氣預報項目流程
	1,POST請求抓起網絡數據
	2,XML解析response的數據
	3,MapView
	4,XML返回的地址信息,利用Geocoder地理編碼器獲得經緯度,設置大頭針位置
	5,XML中的圖片名作大頭針annotation的自定義image
	6,大頭針的title顯示城市名和溫度和空氣質量PM2.5 PM10
	7,大頭針的subtitle顯示天氣詳情
*/
@interface MainViewController () 
{
    // 操作隊列
    NSOperationQueue    *_queue;
    // 地圖視圖
    MKMapView           *_mapView;
    // 地理編碼器
    CLGeocoder          *_geocoder;
}
@end
@implementation MainViewController
/*
 在開發網絡應用時,通常服務器考慮到負載的問題,會禁止同一個地址,
 連續多次提交請求

 大多數這種情況下,服務器隻響應一次!
 
 解決辦法:隔一秒抓一次!
 
 思路:
 
 1) 抓取城市天氣信息的數據,不能夠並發執行,要開個新線程,即在background運行
	需要依次執行=>NSURLConntection需要發送同步請求
 2) 如果單純使用同步請求,會阻塞主線程,影響用戶體驗
 3) 新開一個線程,在後臺依次抓取所有城市的數據
 */
#pragma mark - 實例化視圖
- (void)loadView
{
    self.view = [[UIView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame];
    _mapView = [[MKMapView alloc]initWithFrame:self.view.bounds];
    // 1. 如果需要旋轉屏幕,同時自動調整視圖大小
    [_mapView setAutoresizingMask:UIViewAutoresizingFlexibleHeight |
										UIViewAutoresizingFlexibleWidth];
    // 2. 添加到根視圖
    [self.view addSubview:_mapView];
    // 3. 設置mapView的代理 為當前控制器
    [_mapView setDelegate:self];
}
#pragma mark - 加載數據
- (void)viewDidLoad
{
    [super viewDidLoad];
    _queue = [[NSOperationQueue alloc]init];
    _geocoder = [[CLGeocoder alloc]init];
    // 在後臺線程加載城市數據
    [self performSelectorInBackground:@selector(loadWeatherData) 
									  withObject:nil];
}
#pragma mark 自定義方法,加載城市天氣數據,後臺運行
- (void)loadWeatherData
{
    NSLog(@"%@", [NSThread currentThread]);
    [self loadWeatherDataWithCityName:@"北京"];
    [NSThread sleepForTimeInterval:1.0f];
    [self loadWeatherDataWithCityName:@"重慶"];
    [NSThread sleepForTimeInterval:1.0f];
    [self loadWeatherDataWithCityName:@"上海"];
}
#pragma mark 自定義方法,POST請求,抓取網絡天氣數據
- (void)loadWeatherDataWithCityName:(NSString *)cityName
{
    // 1. NSURL
    NSString *urlString = @"https://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName";
    NSURL *url = [NSURL URLWithString:urlString];
    // 2. NSMutableURLRequest,POST請求
    NSMutableURLRequest *request = [NSMutableURLRequest 
							requestWithURL:url 
							cachePolicy:NSURLRequestUseProtocolCachePolicy 
							timeoutInterval:2.0f];
    // 1) 指定數據體
    NSString *bodyString = [NSString stringWithFormat:@"theCityName=%@",
														cityName];
	// 中文必須轉碼 NSUTF8StringEncoding
    NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
	// 設置請求體
    [request setHTTPBody:bodyData];
    // 2) 指定http請求方式
    [request setHTTPMethod:@"POST"];
    // 3. NSURLConnection,同步請求,response用於接收返回的內容
    NSURLResponse *response = nil;
    NSData *data = [NSURLConnection sendSynchronousRequest:request 
					returningResponse:&response error:nil];
    // 1) 實例化單例 天氣XML解析器
    WeatherXMLPaser *parser = [WeatherXMLPaser sharedWeatherXMLPaser];
    // 2) 解析
    [parser parserWeatherData:data completion:^(NSArray *dataList) {
		// 解析完成的回調方法中,填充Model
        Weather *w = [Weather weatherWithArray:dataList];
        // 根據城市名稱,使用地理編碼器獲取到對應的經緯度,然後設置大頭針的位置
        [_geocoder geocodeAddressString:w.cityName 
				completionHandler:^(NSArray *placemarks, NSError *error) {
            // 地標有個location成員,location裡面有2D坐標
            CLPlacemark *placemark = placemarks[0];
            // 大頭針安插在此
            WeatherAnnonation *annonation = [[WeatherAnnonation alloc]init];
            // 指定大頭針的經緯度位置
            annonation.coordinate = placemark.location.coordinate;           
            annonation.title = [NSString stringWithFormat:@"%@ %@", w.cityName, w.temperature];
            annonation.subtitle = [NSString stringWithFormat:@"%@ %@", w.todayInfo, w.wind];
            annonation.imageName = w.imageName;            
            [_mapView addAnnotation:annonation];
        }];
    }];
}
#pragma mark - 地圖視圖代理方法,viewForAnnotation
- (MKAnnotationView *)mapView:(MKMapView *)mapView 
			viewForAnnotation:(id)annotation
{
	// 要判斷isKindOfClass,如果不是自定義的,就用默認的,即return nil
	// 如果傳入的annotation不是自定義大頭針視圖,直接返回nil,
	// 即使用地圖默認的方法繪制大頭針
    // 如果是自定義視圖,才要設置大頭針屬性,牢記~~~~
    if (![annotation isKindOfClass:[WeatherAnnonation class]]) {
        return nil;
    }
    static NSString *ID = @"ID";
    MKAnnotationView *view = [mapView 
				dequeueReusableAnnotationViewWithIdentifier:ID];
    if (view == nil) {
        view = [[MKAnnotationView alloc]initWithAnnotation:annotation 
												reuseIdentifier:ID];
		// 設置大頭針,可以被點擊後呼出
        view.canShowCallout = YES;
    }
    // 設置大頭針視圖獨一無二的屬性
    // 1) 如果大頭針視圖是從緩沖池取出的,必須要重新設置大頭針
    view.annotation = annotation;
    // 設置圖像
	// 2) 設置大頭針圖像,需手動轉成WeatherAnnonation *,才能使用子類的特有屬性
    [view setImage:[UIImage imageNamed:((WeatherAnnonation *)annotation).icon]];
    return view;
}
@end

H:/1021/03_天氣預報_Weather.m

//  Weather.m
//  03.天氣預報
//  Created by apple on 13-10-21.
/*
//  Weather.h
//  03.天氣預報
#import 
@interface Weather : NSObject
#pragma mark 工廠方法
+ (Weather *)weatherWithArray:(NSArray *)array;
// 1. 城市名
@property (strong, nonatomic) NSString *cityName;
// 2. 今天的信息
@property (strong, nonatomic) NSString *todayInfo;
// 3. 風向
@property (strong, nonatomic) NSString *wind;
// 4. 圖片名
@property (strong, nonatomic) NSString *imageName;
// 5. 氣溫
@property (strong, nonatomic) NSString *temperature;
@end

*/
#import "Weather.h"
@implementation Weather
+ (Weather *)weatherWithArray:(NSArray *)array
{
    Weather *w = [[Weather alloc]init];
    w.cityName = array[1];
    w.todayInfo = array[6];
    w.wind = array[7];
    w.imageName = array[8];
    w.temperature = array[5];
    return w;
}
// 重寫toString方法
- (NSString *)description
{
    return [NSString stringWithFormat:
			@"",
			self, self.cityName, self.todayInfo,
			self.wind, self.imageName, self.temperature];
}
@end

H:/1021/03_天氣預報_WeatherAnnonation.h

//  WeatherAnnonation.h
//  03.天氣預報
//  Created by apple on 13-10-21.
//  Copyright (c) 2013年 itcast. All rights reserved.

#import 
#import 
@interface WeatherAnnonation : NSObject 
// 覆蓋協議裡面的成員,坐標,標題,副標題,圖片名
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
@property (strong, nonatomic) NSString *imageName;
@end

H:/1021/03_天氣預報_WeatherXMLPaser.h

//  WeatherXMLPaser.h
//  03.天氣預報
//  Created by apple on 13-10-21.
#import 
/*
 定義block代碼塊,目的是解析完成之後調用
 返回值是 void
 參數是 數組,裡面的每個成員是一個NSString
*/

typedef void(^WeatherFinishedBlock)(NSArray *dataList);

@interface WeatherXMLPaser : NSObject

// 單例,返回解析器對象
+ (WeatherXMLPaser *)sharedWeatherXMLPaser;
// 解析器解析數據,參數1是要解析的數據,參數2是解析完畢回調的代碼塊
- (void)parserWeatherData:(NSData *)data 
			completion:(WeatherFinishedBlock)completion;
@end

H:/1021/03_天氣預報_WeatherXMLPaser.m

//  WeatherXMLPaser.m
//  03.天氣預報
//  Created by apple on 13-10-21.
/*
	天氣預報項目流程
	1,POST請求抓起網絡數據
	2,XML解析response的數據
	3,MapView
	4,XML返回的地址信息,利用Geocoder地理編碼器獲得經緯度,設置大頭針位置
	5,XML中的圖片名作大頭針annotation的自定義image
	6,大頭針的title顯示城市名和溫度和空氣質量PM2.5 PM10
	7,大頭針的subtitle顯示天氣詳情
*/
#import "WeatherXMLPaser.h"
// 單例第1步:靜態實例變量 
static WeatherXMLPaser *sharedInstance;
// 要想解析XML 必須遵守協議
@interface WeatherXMLPaser() 
{
    
	// 自定義block代碼塊
    WeatherFinishedBlock  _FinishedBlock;
    // 解析結果的字符串數組,根節點是,其餘節點名全是
    NSMutableArray          *_dataList;    
	// 臨時文本字符串
    NSMutableString         *_tempStr;
}
@end
@implementation WeatherXMLPaser
/*  單例模板寫法
 1. 靜態實例變量   static WeatherXMLPaser *sharedInstance;
 2. allocWithZone
 3. shared方法*/
 
#pragma mark - 單例方法
// 單例第2步:類方法,allocWithZone
+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super allocWithZone:zone];
    });
    return sharedInstance;
}
// 單例,返回解析器對象
// 單例第3步:類方法,shared方法
+ (WeatherXMLPaser *)sharedWeatherXMLPaser
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[WeatherXMLPaser alloc]init];
    });
    return sharedInstance;
}
#pragma mark - 成員方法
#pragma mark 解析器解析數據,參數1是要解析的數據,參數2是完畢時調用的代碼塊
- (void)parserWeatherData:(NSData *)data 
				completion:(WeatherFinishedBlock)completion
{
    // 0. 記錄塊代碼
    _FinishedBlock = completion;
    // 1. 實例化XML解析器
    NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
    // 2. 設置代理為 當前的WeatherXMLPaser
    [parser setDelegate:self];
    // 3. 解析器開始解析
    [parser parse];
}
#pragma mark - XML解析代理方法
#pragma mark 5. 結束解析文檔
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
    // 解析結束,調用代碼塊,參數是,解析完的NSString數組
	// 通過block代碼塊回調,通知調用方解析結果
    _FinishedBlock(_dataList);
}
#pragma mark 2. 開始解析元素節點
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
					namespaceURI:(NSString *)namespaceURI 
					qualifiedName:(NSString *)qName 
					attributes:(NSDictionary *)attributeDict
{
	// 根節點是,其餘節點名全是
	// 如果節點名是ArrayOfString,說明是根節點,準備好數組_dataList,裝數據
    if ([elementName isEqualToString:@"ArrayOfString"]) {
        if (_dataList) {
            [_dataList removeAllObjects];
        }
    }
    // 無論是什麼節點開始瞭,都要將臨時文本清空,用於拼裝文本節點
    [_tempStr setString:@""];
}
#pragma mark 4. 結束解析節點,重點
- (void)parser:(NSXMLParser *)parser 
				didEndElement:(NSString *)elementName 
				namespaceURI:(NSString *)namespaceURI 
				qualifiedName:(NSString *)qName
{
	// 根節點是,其餘節點名全是	
    NSString *result = [NSString stringWithString:_tempStr];
	// 如果結束的節點是,就把拼裝好的文本節點,添加到數組_dataList
    if ([elementName isEqualToString:@"string"]) {
        [_dataList addObject:result];
    }
}
#pragma mark 1. 開始解析文檔,初始化準備工作
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
    // 懶加載臨時文本字符串
    if (_tempStr == nil) {
        _tempStr = [NSMutableString string];
    }
    // 懶加載結果數組
    if (_dataList == nil) {
        _dataList = [NSMutableArray array];
    }
}
#pragma mark 6. 解析出錯
- (void)parser:(NSXMLParser *)parser 
			parseErrorOccurred:(NSError *)parseError
{
    NSLog(@"解析出錯 %@", parseError.localizedDescription);
	// 置空臨時文本
    [_tempStr setString:@""];
}
#pragma mark 3. 發現文本內容(一個文本節點可能會解析多次)
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    // 追加文本
    [_tempStr appendString:string];
}
@end

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。