/* 通过block回调 定义block代码块,目的是解析完成之后调用 返回值是 void 参数是 数组,里面的每个成员是一个NSString*/ typedef void(^WeatherFinishedBlock)(NSArray *dataList); @interface WeatherXMLPaser : NSObject // 解析器解析数据,参数1是要解析的数据,参数2是解析完毕回调的代码块 - (void)parserWeatherData:(NSData *)data completion:(WeatherFinishedBlock)completion; @end //-------------------------------------------------------- @interface WeatherXMLPaser() <NSXMLParserDelegate> { // 成员记住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 <UIKit/UIKit.h> @interface MainViewController : UIViewController @end */ #import "MainViewController.h" #import <MapKit/MapKit.h> //#import "MyAnnotation.h" #import "MyAnnotation2.h" @interface MainViewController () <MKMapViewDelegate> { 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 <MKAnnotation>)annotation 凡是遵守MKAnnotation协议的对象都可以成为大头针 5. 自定义大头针,地图视图是支持大头针视图重用的! 如果在mapView:(MKMapView *)mapView viewForAnnotation: (id<MKAnnotation>)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<MKAnnotation>)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 <Foundation/Foundation.h> #import <MapKit/MapKit.h> // 大头针,遵守协议 <MKAnnotation> @interface MyAnnotation : NSObject <MKAnnotation> // 提示,因为要给对象属性赋值,所以此处实例化对象方法不能用工厂方法, 原因就是类方法中,无法访问对象的成员变量 // 坐标,标题,子标题 - (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 <Foundation/Foundation.h> #import <MapKit/MapKit.h> // 自定义大头针,遵守协议 <MKAnnotation> @interface MyAnnotation2 : NSObject <MKAnnotation> // 坐标,标题,副标题 @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 <CoreLocation/CoreLocation.h> @interface MainViewController () <CLLocationManagerDelegate> { // 成员: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 <MapKit/MapKit.h> #import <CoreLocation/CoreLocation.h> #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 () <MKMapViewDelegate> { // 操作队列 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 = @"http://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<MKAnnotation>)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 <Foundation/Foundation.h> @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: @"<Weather: %p, cityName: %@, todayInfo: %@, wind: %@, imageName: %@, temperature: %@>", 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 <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface WeatherAnnonation : NSObject <MKAnnotation> // 覆盖协议里面的成员,坐标,标题,副标题,图片名 @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 <Foundation/Foundation.h> /* 定义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 必须遵守协议<NSXMLParserDelegate> @interface WeatherXMLPaser() <NSXMLParserDelegate> { // 自定义block代码块 WeatherFinishedBlock _FinishedBlock; // 解析结果的字符串数组,根节点是<ArrayOfString>,其余节点名全是<String> 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>,其余节点名全是<String> // 如果节点名是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 { // 根节点是<ArrayOfString>,其余节点名全是<String> NSString *result = [NSString stringWithString:_tempStr]; // 如果结束的节点是</String>,就把拼装好的文本节点,添加到数组_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