SeismicXML范例解析

 SeismicXML例子程序演示如何使用NSXMLParser分析XML数据,主要分析从USGS的RSS feed。USGS提供世界各地的最近地震。它显示每个地震的位置,日期,震级。同时用颜色图形显示地震的严重性。此XML在后台分析且用分析的对象更新地震table view。
此代码中包含了一些比较好的思想,如多线程分析xml数据,异步下载,NSNotificationCente(在两个类中传递数据),UIActionSheet类实现,和其它一些技术细节。

官方例子相关:
SeismicXML地址:http://developer.apple.com/iphone/library/samplecode/SeismicXML/SeismicXML.zip

 

一、程序的运行效果


二、程序的思路
多线程的好处即提高程序执行效率,防止界面阻塞。
1、进入程序后,建立给定网址(RSS feed)的连接,程序会自动下载对应的RSS feed数据,如果网络连接无误,数据自动全部下载,这是XCode提供的内部回调函数。
2、增加消息中心,以便地震RSS feed解析成功或失败时通知
3、建立一个线程,把下载好的数据传递给新建线程类,把此线程添加到线程队列,通过注册的消息中心来通知被处理的数据,这点与LazyTableImages有区别,LazyTableImage是利用XCode的delegate等待线程结束后返回被处理后的数据。
3、在创建的线程中用NSXMLParser根据程序要求分析下载的数据,并添加到数组中。全部解析之后,通知AppDelegate
4、从线程类接到解析成功通知后,根据通知参数NSNotification指针,调用table view视图rootViewController,插入解析好的数组

下面记录一些具体的代码实现
1、网络连接并下载数据

声明
@interface SeismicXMLAppDelegate : NSObject <UIApplicationDelegate, NSXMLParserDelegate> 
{
    NSURLConnection *earthquakeFeedConnection; 
	NSMutableData *earthquakeData;	
}
实现
@interface SeismicXMLAppDelegate ()

@property (nonatomic, retain) NSURLConnection *earthquakeFeedConnection;
@property (nonatomic, retain) NSMutableData *earthquakeData;    
@end

#pragma mark -
#pragma mark SeismicXMLAppDelegate

@implementation SeismicXMLAppDelegate

@synthesize earthquakeFeedConnection;
@synthesize earthquakeData;

- (void)dealloc 
{
    [earthquakeFeedConnection cancel];
    [earthquakeFeedConnection release];
    
    [earthquakeData release];
    [super dealloc];
}
- (void)applicationDidFinishLaunching:(UIApplication *)application 
{
    static NSString *feedURLString = @"http://earthquake.usgs.gov/eqcenter/catalogs/7day-M2.5.xml";
    NSURLRequest *earthquakeURLRequest =
    [NSURLRequest requestWithURL:[NSURL URLWithString:feedURLString]];
    self.earthquakeFeedConnection =
    [[[NSURLConnection alloc] initWithRequest:earthquakeURLRequest delegate:self] autorelease];
    NSAssert(self.earthquakeFeedConnection != nil, @"Failure to create URL connection.");
}

#pragma mark -
#pragma mark NSURLConnection delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
{
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
    if ((([httpResponse statusCode]/100) == 2) && [[response MIMEType] isEqual:@"application/atom+xml"])
	{
        self.earthquakeData = [NSMutableData data];
    } 
	else 
	{
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:
                                  NSLocalizedString(@"HTTP Error",
                                                    @"Error message displayed when receving a connection error.")
                                                             forKey:NSLocalizedDescriptionKey];
        NSError *error = [NSError errorWithDomain:@"HTTP" code:[httpResponse statusCode] userInfo:userInfo];
        [self handleError:error];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{
    [earthquakeData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;   
    if ([error code] == kCFURLErrorNotConnectedToInternet)
	{
        NSDictionary *userInfo =
        [NSDictionary dictionaryWithObject:
         NSLocalizedString(@"No Connection Error",
                           @"Error message displayed when not connected to the Internet.")
                                    forKey:NSLocalizedDescriptionKey];
        NSError *noConnectionError = [NSError errorWithDomain:NSCocoaErrorDomain
                                                         code:kCFURLErrorNotConnectedToInternet
                                                     userInfo:userInfo];
        [self handleError:noConnectionError];
    }
	else
	{
        // otherwise handle the error generically
        [self handleError:error];
    }
    self.earthquakeFeedConnection = nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{
    self.earthquakeFeedConnection = nil;
    ParseOperation *parseOperation = [[ParseOperation alloc] initWithData:self.earthquakeData];
    [self.parseQueue addOperation:parseOperation];
    [parseOperation release];   // once added to the NSOperationQueue it's retained, we don't need it anymore
    
    self.earthquakeData = nil;
}

2、创建线程队列并把新建线程增加到队列中 

头文件
@interface SeismicXMLAppDelegate : NSObject <UIApplicationDelegate, NSXMLParserDelegate>
{  
@private
    NSOperationQueue *parseQueue; 
}
实现
#import "ParseOperation.h"

#pragma mark SeismicXMLAppDelegate () 
@interface SeismicXMLAppDelegate ()
@property (nonatomic, retain) NSOperationQueue *parseQueue;  
@end

#pragma mark -
#pragma mark SeismicXMLAppDelegate
@implementation SeismicXMLAppDelegate
@synthesize parseQueue;

- (void)dealloc 
{
    [parseQueue release];
    [super dealloc];
}

- (void)applicationDidFinishLaunching:(UIApplication *)application 
{
    parseQueue = [NSOperationQueue new]; 
}

#pragma mark -
#pragma mark NSURLConnection delegate methods
- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{
    ParseOperation *parseOperation = [[ParseOperation alloc] initWithData:self.earthquakeData];
    [self.parseQueue addOperation:parseOperation];
    [parseOperation release];
}
@end

3、增加通知中心

AppDelegate中实现
- (void)dealloc 
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kAddEarthquakesNotif object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kEarthquakesErrorNotif object:nil];
        [super dealloc];
}
- (void)applicationDidFinishLaunching:(UIApplication *)application 
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(addEarthquakes:)
                                                 name:kAddEarthquakesNotif
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(earthquakesError:)
                                                 name:kEarthquakesErrorNotif
                                               object:nil];
}
- (void)addEarthquakes:(NSNotification *)notif
{
    assert([NSThread isMainThread]);
    
    [self addEarthquakesToList:[[notif userInfo] valueForKey:kEarthquakeResultsKey]];
}
- (void)earthquakesError:(NSNotification *)notif
{
    assert([NSThread isMainThread]);
    
    [self handleError:[[notif userInfo] valueForKey:kEarthquakesMsgErrorKey]];
}
- (void)addEarthquakesToList:(NSArray *)earthquakes
{
    [self.rootViewController insertEarthquakes:earthquakes];
}
线程头文件ParseOperation.h
extern NSString *kAddEarthquakesNotif;
extern NSString *kEarthquakeResultsKey;

extern NSString *kEarthquakesErrorNotif;
extern NSString *kEarthquakesMsgErrorKey;
线程实现文件ParseOperation.m
NSString *kAddEarthquakesNotif = @"AddEarthquakesNotif";
NSString *kEarthquakeResultsKey = @"EarthquakeResultsKey";
NSString *kEarthquakesErrorNotif = @"EarthquakeErrorNotif";
NSString *kEarthquakesMsgErrorKey = @"EarthquakesMsgErrorKey";
- (void)addEarthquakesToList:(NSArray *)earthquakes 
{
    assert([NSThread isMainThread]);
    
    [[NSNotificationCenter defaultCenter] postNotificationName:kAddEarthquakesNotif
                                                        object:self
                                                      userInfo:[NSDictionary dictionaryWithObject:earthquakes
                                                                                           forKey:kEarthquakeResultsKey]]; 
}
- (void)handleEarthquakesError:(NSError *)parseError {
    [[NSNotificationCenter defaultCenter] postNotificationName:kEarthquakesErrorNotif
                                                    object:self
                                                  userInfo:[NSDictionary dictionaryWithObject:parseError
                                                                                       forKey:kEarthquakesMsgErrorKey]];
}


4、格式化日期

头文件:ParseOperation.h
@private
NSDateFormatter *dateFormatter;
实现文件ParseOperation.m
- (id)initWithData:(NSData *)parseData
{
    if (self = [super init]) 
	{    
        dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
        [dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]];
		//Aug 5,2011 11:39:30 PM
        [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
}
}
- (void)dealloc 
{
    [dateFormatter release];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
                                      namespaceURI:(NSString *)namespaceURI
                                     qualifiedName:(NSString *)qName
{   
  self.currentEarthquakeObject.date =
            [dateFormatter dateFromString:self.currentParsedCharacterData];
}
头文件:RootViewController.h
@interface RootViewController : UITableViewController <UIActionSheetDelegate> {
    NSDateFormatter *dateFormatter;
}
@property (nonatomic, retain, readonly) NSDateFormatter *dateFormatter;
实现文件RootViewController.m
- (void)dealloc {
     [dateFormatter release];
    [super dealloc];
}
- (NSDateFormatter *)dateFormatter {
    if (dateFormatter == nil) {
        dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
        [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
        [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
    }
    return dateFormatter;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     dateLabel.text = [NSString stringWithFormat:@"%@", [self.dateFormatter stringFromDate:earthquake.date]];
 	return cell;
}

5、分解字符串

// <title>M 3.6, Virgin Islands region<title/>
NSScanner *scanner = [NSScanner scannerWithString:self.currentParsedCharacterData];
if ([scanner scanString:@"M " intoString:NULL]) 
{
    CGFloat magnitude;
    if ([scanner scanFloat:&magnitude])
    {
	self.currentEarthquakeObject.magnitude = magnitude;
	if ([scanner scanString:@", " intoString:NULL]) 
	{
	    NSString *location = nil;	   
	    if ([scanner scanUpToCharactersFromSet:[NSCharacterSet illegalCharacterSet] intoString:&location]) 				
	    {
		self.currentEarthquakeObject.location = location;
	    }
	}
    }
}
// 18.6477 -66.7452
NSScanner *scanner = [NSScanner scannerWithString:self.currentParsedCharacterData];
double latitude, longitude;
if ([scanner scanDouble:&latitude])
{
    if ([scanner scanDouble:&longitude])
    {
	self.currentEarthquakeObject.latitude = latitude;
	self.currentEarthquakeObject.longitude = longitude;
    }
}


6、KVO模型

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.earthquakeList = [NSMutableArray array];
    // KVO: listen for changes to our earthquake data source for table view updates
    [self addObserver:self forKeyPath:@"earthquakeList" options:0 context:NULL];
}
- (void)viewDidUnload {
    [super viewDidUnload];
        self.earthquakeList = nil;
    [self removeObserver:self forKeyPath:@"earthquakeList"];
}
#pragma mark -
#pragma mark KVO support

- (void)insertEarthquakes:(NSArray *)earthquakes
{
    // this will allow us as an observer to notified (see observeValueForKeyPath)
    // so we can update our UITableView
    //
    [self willChangeValueForKey:@"earthquakeList"];
    [self.earthquakeList addObjectsFromArray:earthquakes];
    [self didChangeValueForKey:@"earthquakeList"];
}

// listen for changes to the earthquake list coming from our app delegate.
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    [self.tableView reloadData];
}


7、自定义cell,使用view的Tags而不是子类化cell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    // Each subview in the cell will be identified by a unique tag.
    static NSUInteger const kLocationLabelTag = 2;
    static NSUInteger const kDateLabelTag = 3;
    static NSUInteger const kMagnitudeLabelTag = 4;
    static NSUInteger const kMagnitudeImageTag = 5;
    
    // Declare references to the subviews which will display the earthquake data.
    UILabel *locationLabel = nil;
    UILabel *dateLabel = nil;
    UILabel *magnitudeLabel = nil;
    UIImageView *magnitudeImage = nil;
    
	static NSString *kEarthquakeCellID = @"EarthquakeCellID";    
  	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kEarthquakeCellID];
	if (cell == nil) 
	{
        // No reusable cell was available, so we create a new cell and configure its subviews.
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:kEarthquakeCellID] autorelease];
        
        locationLabel = [[[UILabel alloc] initWithFrame:CGRectMake(10, 3, 190, 20)] autorelease];
        locationLabel.tag = kLocationLabelTag;
        locationLabel.font = [UIFont boldSystemFontOfSize:14];
        [cell.contentView addSubview:locationLabel];
        
        dateLabel = [[[UILabel alloc] initWithFrame:CGRectMake(10, 28, 170, 14)] autorelease];
        dateLabel.tag = kDateLabelTag;
        dateLabel.font = [UIFont systemFontOfSize:10];
        [cell.contentView addSubview:dateLabel];

        magnitudeLabel = [[[UILabel alloc] initWithFrame:CGRectMake(277, 9, 170, 29)] autorelease];
        magnitudeLabel.tag = kMagnitudeLabelTag;
        magnitudeLabel.font = [UIFont boldSystemFontOfSize:24];
        magnitudeLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
        [cell.contentView addSubview:magnitudeLabel];
        
        magnitudeImage = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"5.0.png"]] autorelease];
        CGRect imageFrame = magnitudeImage.frame;
        imageFrame.origin = CGPointMake(180, 2);
        magnitudeImage.frame = imageFrame;
        magnitudeImage.tag = kMagnitudeImageTag;
        magnitudeImage.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
        [cell.contentView addSubview:magnitudeImage];
    } 
	else 
	{
        // A reusable cell was available, so we just need to get a reference to the subviews
        // using their tags. 
        locationLabel = (UILabel *)[cell.contentView viewWithTag:kLocationLabelTag];
        dateLabel = (UILabel *)[cell.contentView viewWithTag:kDateLabelTag];
        magnitudeLabel = (UILabel *)[cell.contentView viewWithTag:kMagnitudeLabelTag];
        magnitudeImage = (UIImageView *)[cell.contentView viewWithTag:kMagnitudeImageTag];
    }
    
    // Get the specific earthquake for this row.
	Earthquake *earthquake = [earthquakeList objectAtIndex:indexPath.row];
    
    // Set the relevant data for each subview in the cell.
    locationLabel.text = earthquake.location;
    dateLabel.text = [NSString stringWithFormat:@"%@", [self.dateFormatter stringFromDate:earthquake.date]];
    magnitudeLabel.text = [NSString stringWithFormat:@"%.1f", earthquake.magnitude];
    magnitudeImage.image = [self imageForMagnitude:earthquake.magnitude];

	return cell;
}


8、UIActionSheet的使用

@interface RootViewController : UITableViewController <UIActionSheetDelegate> {  
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{	
    UIActionSheet *sheet =
        [[UIActionSheet alloc] initWithTitle:
            NSLocalizedString(@"External App Sheet Title",
                              @"Title for sheet displayed with options for displaying Earthquake data in other applications")
                               delegate:self
                      cancelButtonTitle:NSLocalizedString(@"Cancel", @"Cancel")
                 destructiveButtonTitle:nil
                      otherButtonTitles:NSLocalizedString(@"Show USGS Site in Safari", @"Show USGS Site in Safari"),
                                        NSLocalizedString(@"Show Location in Maps", @"Show Location in Maps"),
                                        nil];
    [sheet showInView:self.view];
    [sheet release];
}

// Called when the user selects an option in the sheet. The sheet will automatically be dismissed.
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex 
{
    NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
    Earthquake *earthquake = (Earthquake *)[earthquakeList objectAtIndex:selectedIndexPath.row];
    switch (buttonIndex) 
	{
        case 0: 
		{
            NSURL *webLink = [earthquake USGSWebLink];
            [[UIApplication sharedApplication] openURL:webLink];
        }
			break;
        case 1: 
		{
            NSString *mapsQuery =
                [NSString stringWithFormat:@"http://maps.google.com/maps?z=6&t=h&ll=%f,%f",
                                            earthquake.latitude, earthquake.longitude];
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mapsQuery]];
        } break;
        default:
            break;
    }
    [self.tableView deselectRowAtIndexPath:selectedIndexPath animated:YES];
}


三、结束语
官方例子中一般有比较好的程序思路和结构设计,代码健壮性也比较好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值