1. Trang chủ
  2. » Công Nghệ Thông Tin

Phát triển ứng dụng cho iPhone và iPad - part 36 pps

10 172 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 2,88 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The connection calls this method each time it receives a chunk of data so you simply append the received chunk to your responseData buffer: - voidconnection:NSURLConnection *connection

Trang 1

NSLog (@”connection:didReceiveResponse:”);

[self.responseData setLength:0];

}

LocationSearchViewController.m

Next, you will implement the connection:didReceiveData: method The connection calls this method each time it receives a chunk of data so you simply append the received chunk to your responseData buffer:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

NSLog (@”connection:didReceiveData:”);

// Append the received data to our responseData property [self.responseData appendData:data];

}

LocationSearchViewController.m

Now, you need to implement connectionDidFinishLoading This method runs when the connection has fi nished loading all of the requested data Here, you convert the response data to a string, clean up the connection, and call the method that you will write to parse the XML:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

NSLog (@”connectionDidFinishLoading:”);

// Convert the data to a string and log the response string NSString *responseString = [[NSString alloc]

initWithData:self.responseData encoding:NSUTF8StringEncoding];

NSLog(@”Response String: \n%@”, responseString);

[responseString release];

[connection release];

[self parseXML];

}

LocationSearchViewController.m

Finally, you will implement the connection:didFailWithError: method to log that an error occurred Remember that you will want to provide some more robust error handling and reporting

Trang 2

in a production application You will also probably want to give the user some feedback as to why

the error occurred Here is the implementation:

- (void)connection:(NSURLConnection *)connection

didFailWithError:(NSError *)error

{

NSLog (@”connection:didFailWithError:”);

NSLog (@”%@”,[error localizedDescription]);

[connection release];

}

LocationSearchViewController.m

Note that you don ’ t have to call a web service or a URL asynchronously, but it is certainly my

recommendation that you do so Alternatively, you could retrieve the XML from a URL directly

by calling the [[NSXMLParser alloc] initWithContentsOfURL] method However, using this

method will cause a loss of responsiveness in your application, as the main thread will block while

waiting for the response from the server Using the URL loading framework as you ’ ve done in this

example is asynchronous and will leave your interface responsive as the application downloads the

XML response Additionally, it gives you more control if you need to authenticate, or handle errors

more responsively as I described in the previous chapter

Defi ning the Result Class

The response XML that you receive from the web service contains a lot of information Although

you will not be using all of that information in the sample, you will parse it out and capture it To

hold this data, you will create a new class that represents an individual result Then, when you parse

the XML, you will create instances of this Result class, populate the data from the result of the

web service call, and add the Result to an array Here is the header for your Result class:

#import < Foundation/Foundation.h >

#import < MapKit/MapKit.h >

@interface Result : NSObject < MKAnnotation > {

NSString *title;

NSString *address;

NSString *city;

NSString *state;

NSString *phone;

double latitude;

double longitude;

float rating;

}

@property (nonatomic, retain) NSString *title;

@property (nonatomic, retain) NSString *address;

@property (nonatomic, retain) NSString *city;

@property (nonatomic, retain) NSString *state;

@property (nonatomic, retain) NSString *phone;

@property (nonatomic) double latitude;

Trang 3

@property (nonatomic) double longitude;

@property (nonatomic) float rating;

@end

Result.h

One thing to notice is that the MapKit.h header fi le is included You need to do this because you will use this class to provide annotation data for your MapView To accomplish this, you need to implement the MKAnnotation protocol To implement this protocol, you must provide a coordinate property that returns a CLLocationCoordinate2D struct This struct contains the coordinates of the point that you would like to annotate on the map You will also include a title property that will display a title for the map annotation, and a subtitle property that you will use to build the subtitle Here is the implementation for the Result class:

#import “Result.h”

@implementation Result

@synthesize title,address,city,state,phone,latitude,longitude,rating;

- (void)dealloc { [title release];

[address release];

[city release];

[state release];

[phone release];

[super dealloc];

} -(CLLocationCoordinate2D) coordinate {

CLLocationCoordinate2D retVal;

retVal.latitude = self.latitude;

retVal.longitude = self.longitude;

return retVal;

}

- (NSString *)subtitle { NSString *retVal = [[NSString alloc] initWithFormat:@”%@”,phone];

[retVal autorelease];

return retVal;

}

@end

Result.m

The dealloc method is straightforward It simply releases the memory allocated by the properties maintained by the class

Trang 4

The coordinate method implements the getter for the coordinate property that is required to

implement the MKAnnotation protocol You may have noticed that a property called coordinate

was not declared, nor was a coordinate property synthesized In Objective - C, properties are simply

a convenience Behind the scenes, using properties and the dot syntax simply calls the appropriate

getter or setter methods Therefore, instead of defi ning a property, you simply implement the getter

method that the MKAnnotation protocol requires

The implementation of the coordinate method is straightforward You take the latitude

and longitude that you received from the web service call and package it up into a

CLLocationCoordinate2D struct as defi ned by the protocol Then, you just return that struct

You implement the subtitle property in the same way Instead of defi ning it as a property, you

simply implement the getter method In this case, you want the subtitle to be the phone number of

the business

Parsing the Response XML

Now that you have defi ned your Result class, you can begin parsing the response XML

and building your result set Before you start, you need to make some additions to your

LocationSearchViewController.h header fi le Add an import statement for your new

Result class:

#import “Result.h”

Add a new parseXML method declaration to the class interface:

- (void) parseXML;

Add instance variables to hold an individual result, an NSMutableArray that will hold the list of all of

the results, and an NSMutableString that will hold the characters captured during the XML parsing:

Result *aResult;

NSMutableArray *results;

NSMutableString *capturedCharacters;

Finally, defi ne a new property for the results array:

@property (nonatomic, retain) NSMutableArray *results;

Move into the LocationSearchViewController.m implementation fi le and synthesize the new

results property:

@synthesize mapView,searchBar, currentLocation,responseData,results ;

Add code to viewDidUnload and dealloc to clean up the results property:

- (void)viewDidUnload {

// Release any retained subviews of the main view

// e.g self.myOutlet = nil;

self.mapView = nil;

Trang 5

self.searchBar = nil;

self.results = nil;

self.currentLocation=nil;

}

- (void)dealloc { [mapView release];

[searchBar release];

[currentLocation release];

[results release];

[super dealloc];

}

LocationSearchViewController.m

Now you are ready to implement the parseXML method You call this method from the connectionDidFinishLoading NSURLConnection delegate method when you fi nish receiving the XML response from the web service Here is the implementation:

- (void) parseXML { NSLog (@”parseXML”);

// Initialize the parser with our NSData from the RSS feed NSXMLParser *xmlParser = [[NSXMLParser alloc]

initWithData:self.responseData];

// Set the delegate to self [xmlParser setDelegate:self];

// Start the parser

if (![xmlParser parse]) {

NSLog (@”An error occurred in the parsing”);

} // Clean up the parser [xmlParser release];

}

LocationSearchViewController.m

In this method, you fi rst declare an instance of an NSXMLParser and initialize it with the response data that you received from the web service Next, you set the parser ’ s delegate to self Then, you tell the parser to start parsing the XML Finally, you release the parser

Remember that the NSXMLParser is a SAX parser, which is event driven Therefore, you need to implement the delegate methods that the parser calls as parsing events occur

Trang 6

First, you will implement the didStartElement method The parser calls this method each time a

begin - element tag, such as < Title > , is found:

- (void) parser:(NSXMLParser *)parser

didStartElement:(NSString *)elementName

namespaceURI:(NSString *)namespaceURI

qualifiedName:(NSString *)qualifiedName

attributes:(NSDictionary *)attributeDict {

NSLog (@”didStartElement”);

// Check to see which element we have found

if ([elementName isEqualToString:@”Result”]) {

// Create a new Result object

aResult = [[Result alloc] init];

}

else if ([elementName isEqualToString:@”Title”]||

[elementName isEqualToString:@”Address”]||

[elementName isEqualToString:@”City”]||

[elementName isEqualToString:@”State”]||

[elementName isEqualToString:@”Phone”]||

[elementName isEqualToString:@”Latitude”]||

[elementName isEqualToString:@”Longitude”]||

[elementName isEqualToString:@”AverageRating”])

{

// Initialize the capturedCharacters instance variable

capturedCharacters = [[NSMutableString alloc] initWithCapacity:100];

}

}

LocationSearchViewController.m

In this code, you check the name of the element that you are currently processing If the element is

a Result , you create a new instance of your Result class to hold the result If the name is another

fi eld that you are interested in, you allocate and initialize the capturedCharacters instance

variable in preparation for the characters to come

Next, you will implement the foundCharacters method The parser calls this method any time

that it encounters characters inside an element You implement the foundCharacters method to

append the characters to the capturedCharacters instance variable, if the variable is not nil If

capturedCharacters is nil , you are not interested in the characters so you do nothing Here is

the code:

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {

if (capturedCharacters != nil) {

[capturedCharacters appendString:string];

}

}

LocationSearchViewController.m

Trang 7

Now, you need to implement the didEndElement method The parser calls this method when an element ends This method is a bit verbose, but its functionality is straightforward When you use

a SAX parser, you will often fi nd yourself writing a function like this that has a giant if/else if block This is the nature of working with a SAX parser because you need to code one method to handle ending any element Without further ado, here is the code:

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

NSLog (@”didEndElement”);

// Check to see which element we have ended

if ([elementName isEqualToString:@”Result”]) {

// Add the result to the array [results addObject:aResult];

// release the Result object [aResult release];

aResult=nil;

} else if ([elementName isEqualToString:@”Title”] & & aResult!=nil) { // Set the appropriate property

aResult.title = capturedCharacters;

} else if ([elementName isEqualToString:@”Address”] & & aResult!=nil) { // Set the appropriate property

aResult.address = capturedCharacters;

} else if ([elementName isEqualToString:@”City”] & & aResult!=nil) { // Set the appropriate property

aResult.city = capturedCharacters;

} else if ([elementName isEqualToString:@”State”] & & aResult!=nil) { // Set the appropriate property

aResult.state = capturedCharacters;

} else if ([elementName isEqualToString:@”Phone”] & & aResult!=nil) { // Set the appropriate property

aResult.phone = capturedCharacters;

} else if ([elementName isEqualToString:@”Latitude”] & & aResult!=nil) { // Set the appropriate property

aResult.latitude = [capturedCharacters doubleValue];

} else if ([elementName isEqualToString:@”Longitude”] & & aResult!=nil) { // Set the appropriate property

Trang 8

aResult.longitude = [capturedCharacters doubleValue];

}

else if ([elementName isEqualToString:@”AverageRating”] & & aResult!=nil) {

// Set the appropriate property

aResult.rating = [capturedCharacters floatValue];

}

// So we don’t have to release capturedCharacters in every else if block

if ([elementName isEqualToString:@”Title”]||

[elementName isEqualToString:@”Address”]||

[elementName isEqualToString:@”City”]||

[elementName isEqualToString:@”State”]||

[elementName isEqualToString:@”Phone”]||

[elementName isEqualToString:@”Latitude”]||

[elementName isEqualToString:@”Longitude”]||

[elementName isEqualToString:@”AverageRating”])

{

// Release the capturedCharacters instance variable

[capturedCharacters release];

capturedCharacters = nil;

}

}

LocationSearchViewController.m

As I said, it ’ s verbose However, it is actually simple The fi rst part of the if statement checks

to see if you are ending a Result element If so, you add the aResult object to the results

array, release aResult , and set it to nil Every other else if clause of that if/else if block

simply sets the appropriate property of your Result object The last piece of the code releases the

capturedCharacters instance variable and sets it to nil

The last delegate method that you will implement is parserDidEndDocument The parser calls this

method when it has fi nished parsing the document In this method, you will call a method of your

class, plotResults , which will plot your results on the map:

- (void)parserDidEndDocument:(NSXMLParser *)parser {

NSLog (@”parserDidEndDocument”);

// Plot the results on the map

[self plotResults];

}

LocationSearchViewController.m

You are now fi nished with the XML parser delegate methods Next, you will take a brief look at

MapKit and then implement the plotResults method

Trang 9

Using MapKit

The MapKit framework enables you to display maps within your application You can programmatically add annotations to the map as you are doing in this example The major feature

of the framework is the MKMapView user interface control that you add to your views to make maps available in your application

You are not limited to the basic annotation styles provided by the framework You can build your own annotation view classes and use them as annotations on the map For the sake of simplicity, this example does not do that

Finally, the framework provides functionality to determine the user ’ s current location and display it

on the map You will implement this feature in the viewDidLoad method You will also implement the MKMapViewDelegate protocol to use colored pins for your annotations

To get started, you will have to modify the LocationSearchViewController.h header fi le You need to declare that you are implementing the MKMapViewDelegate protocol:

@interface LocationSearchViewController : UIViewController < CLLocationManagerDelegate,UISearchBarDelegate, MKMapViewDelegate >

LocationSearchViewController.h

Next, you will add the plotResults method to the interface:

- (void) plotResults;

You are now completely fi nished with the LocationSearchViewController.h header fi le Here is the complete header so that you can verify that your code is coordinated with the example:

#import < UIKit/UIKit.h >

#import < MapKit/MapKit.h >

#import < CoreLocation/CoreLocation.h >

#import “Result.h”

@interface LocationSearchViewController : UIViewController < CLLocationManagerDelegate,UISearchBarDelegate, MKMapViewDelegate >

MKMapView* mapView;

UISearchBar *searchBar;

CLLocation* currentLocation;

NSMutableData *responseData;

NSMutableString *capturedCharacters;

Result *aResult;

NSMutableArray *results;

}

@property (nonatomic, retain) IBOutlet MKMapView* mapView;

@property (nonatomic, retain) IBOutlet UISearchBar *searchBar;

@property (nonatomic, retain) CLLocation* currentLocation;

Trang 10

@property (nonatomic, retain) NSMutableData *responseData;

@property (nonatomic, retain) NSMutableArray *results;

- (void) parseXML;

- (void) plotResults;

@end

LocationSearchViewController.h

Now you need to move into the implementation fi le The fi rst thing that you want to do is center

the map on the current location of the device To do this, you will implement the Core Location

delegate method locationManager:didUpdateToLocation:fromLocation: If you recall from the

section entitled “ Core Location, ” the location manager calls this method when it determines that

the device has moved Here is the complete implementation:

- (void)locationManager:(CLLocationManager *)manager

didUpdateToLocation:(CLLocation *)newLocation

fromLocation:(CLLocation *)oldLocation {

self.currentLocation = newLocation;

// Create a mapkit region based on the location

// Span defines the area covered by the map in degrees

MKCoordinateSpan span;

span.latitudeDelta = 0.05;

span.longitudeDelta = 0.05;

// Region struct defines the map to show based on center coordinate and span

MKCoordinateRegion region;

region.center = newLocation.coordinate;

region.span = span;

// Update the map to display the current location

[mapView setRegion:region animated:YES];

// Stop core location services to conserve battery

[manager stopUpdatingLocation];

}

LocationSearchViewController.m

First, you will set the currentLocation property to the current location of the device Then,

you create an MKCoordinateSpan struct This struct defi nes the area that you want to display on

the map You are declaring that you would like the map to display 0.05 degrees of latitude and

longitude The span determines how far in you want to zoom the map A larger span results in a

larger area displayed on the map, thus a lower zoom factor A small span zooms in on a small area

therefore producing a high zoom factor

Ngày đăng: 04/07/2014, 21:20