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() <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