I learn about current NSXMLParser. I read many tutorials but none could tell me exactly how it works. there is a tutorial on StackOverflow? My problem is this KML read structur in my app and display with MKMapView
My Parser class looks like:
#import <Foundation/Foundation.h>
@interface Parser : NSXMLParser
@property (nonatomic, strong) NSString *rowElementName; // this is the element name that identifies a new row of data in the XML
@property (nonatomic, strong) NSArray *attributeNames; // this is the array of attributes we might want to retrieve for that element name
@property (nonatomic, strong) NSArray *elementNames; // this is the list of sub element names for which we're retrieving values
@property (nonatomic, strong) NSMutableArray *items; // after parsing, this is the array of parsed items
@end
and
#import "Parser.h"
@interface Parser () <NSXMLParserDelegate>
@property (nonatomic, strong) NSMutableDictionary *item; // while parsing, this is the item currently being parsed
@property (nonatomic, strong) NSMutableString *elementValue; // this is the element within that item being parsed
@end
@implementation Parser
- (id)initWithContentsOfURL:(NSURL *)url
{
self = [super initWithContentsOfURL:url];
if (self)
{
self.delegate = self;
}
return self;
}
- (id)initWithData:(NSData *)data
{
self = [super initWithData:data];
if (self)
{
self.delegate = self;
}
return self;
}
- (id)initWithStream:(NSInputStream *)stream
{
self = [super initWithStream:stream];
if (self)
{
self.delegate = self;
}
return self;
}
#pragma mark - NSXMLParserDelegate methods
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
self.items = [[NSMutableArray alloc] init];
if (!self.rowElementName)
NSLog(@"%s Warning: Failed to specify row identifier element name", __FUNCTION__);
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:self.rowElementName])
{
self.item = [[NSMutableDictionary alloc] init];
for (NSString *attributeName in self.attributeNames)
{
id attributeValue = [attributeDict valueForKey:attributeName];
if (attributeValue)
[self.item setObject:attributeValue forKey:attributeName];
}
}
else if ([self.elementNames containsObject:elementName])
{
self.elementValue = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (self.elementValue)
{
[self.elementValue appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:self.rowElementName])
{
[self.items addObject:self.item];
self.item = nil;
}
else if ([self.elementNames containsObject:elementName])
{
[self.item setValue:self.elementValue forKey:elementName];
self.elementValue = nil;
}
}
@end
created by rob
my XML-file:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.1">
<Document>
<name>Filename.kml</name>
<Style id="style1">
<IconStyle>
<Icon>
<href>http://imageshack.us/a/img825/9079/pinvi.png</href>
</Icon>
</IconStyle>
</Style>
<Placemark>
<name><![CDATA[Blankenese]]></name>
<Snippet><![CDATA[Blankeneser Bahnhofstr. 9 22587 Hamburg +49-(0)40-866 06 50 +49-(0)40-86 60 65 60]]></Snippet>
<description><![CDATA[<img src="http://www.engelvoelkers.com/shops/de-blankenese-res.jpg"><br/>Blankeneser Bahnhofstr. 9<br/>22587 Hamburg<br/>Telefon: +49-(0)40-866 06 50<br/>Fax: +49-(0)40-86 60 65 60<br/><a href="http://www.engelvoelkers.com/elbe">Website</a><br/>E-Mail: Blankenese@engelvoelkers.com]]></description>
<styleUrl>#style1</styleUrl>
<Point>
<coordinates>9.811470,53.559441</coordinates>
</Point>
</Placemark>
</Document>
</kml>
My goal is to get all the Informations in the <description>
tag and displayed it like it Google-Maps do
not exactly but Similar.
but first i need to know how i can work with the Parser
Best regards CTS
There were two fundamentally different components to this question, parsing and annotating maps. I'll focus on the map annotation here, as I think I covered the parsing questions here: Try to load a created Map in MKMapView. At the end of this answer, though, I include some references to Apple's parsing documentation if you're just trying to get your arms around NSXMLParser
.
Annotating maps
The common model in mapping apps on the iPhone is to not show a popover with rich content on the maps view itself, but rather, due to the iPhone's limited real estate, to just show the standard callout, but set the rightCalloutAccessoryView
to be a disclosure indicator, which, if you tap on it, will segue to that next view with the details. Thus, by using UIMapViewDelegate
methods, you can have a mapView:viewForAnnotation:
that says:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"loc"];
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return annotationView;
}
This yields the following user interface:
You can then have a mapView:annotationView:calloutAccessoryControlTapped:
like so:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self performSegueWithIdentifier:@"DetailsIphone" sender:view];
}
You can use that to go to your details screen. (I'm just doing a modal segue to a view controller with a web view, passing the annotation in the prepareForSegue
, and the viewDidLoad
is grabbing the html, etc. The details here are unremarkable. I assume you can transition to your own details screen and design something prettier than this quick and dirty web view ... I'm just demonstrating that we can grab the HTML for a placemark out of the KML file):
So, while the iPhone really shouldn't be using popovers on the map itself, on the iPad, you can use them. You can create the rightCalloutAccessoryView
in a similar manner (though maybe use the "info" button rather than the detail disclosure):
But here you can have mapView:annotationView:calloutAccessoryControlTapped:
actually generate the popover rather than doing a modal transition:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
//CGRect frame = view.frame;
[mapView deselectAnnotation:view.annotation animated:YES];
DetailsViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"DetailsPopover"];
controller.annotation = view.annotation;
self.popover = [[UIPopoverController alloc] initWithContentViewController:controller];
self.popover.delegate = self;
[self.popover presentPopoverFromRect:view.frame
inView:view.superview
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
which yields:
By the way, this roughly approximates how the iPad Maps app does it (when you click on a pin, it shows you a callout with an "info" button), which if you then click on the info button, it shows you the popover with details.
Alternatively, you could have the click on the pin take you right to your popover, bypassing the intervening callout. To do so, you first, though, have to disable the callout on the annotation view, itself:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"loc"];
annotationView.canShowCallout = NO;
return annotationView;
}
But you then have to respond to mapView:didSelectAnnotationView:
:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
[mapView deselectAnnotation:view.annotation animated:YES];
DetailsViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"DetailsPopover"];
controller.annotation = view.annotation;
self.popover = [[UIPopoverController alloc] initWithContentViewController:controller];
self.popover.delegate = self;
[self.popover presentPopoverFromRect:view.frame
inView:view.superview
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
Theoretically, you could do something like this on the iPhone, but since you can't use UIPopoverController
, you'd have to use some third-party popover (or write your own). I've heard some people allege that Apple has rejected iPhone apps for using popover views, though I can neither confirm that, nor say whether that's a hard and fast rule. I just know that the Apple and Google iPhone mapping apps do not use large popover views on the iPhone maps apps (Apple segues to another view, Google has it appear at the bottom of the screen). And if you think about it, if the pin is right in the center and you tried to generate a large popover pointing to that pin, it might get cramped.
Anyway, those are the options for using rightCalloutAccessoryView
settings and/or disabling canShowCallout
and showing the popover directly.
Parsing references:
- Apple Event-Driven XML Programming Guide
- NSXMLParser Class Reference
Map view callout reference:
- Customizing the MKAnnotation Callout bubble
The Parser works like this, The root tag is opened and then sub tags are searched. When there are no more nested tags the data is read and here you can have an option on how do you want to save your data. There are many other parser available. Go through gDataXMLParser. I have used it and works pretty well and make sure to call the parser recursively if there are nested tags.
If you are looking for a specific tag use the string to find the sub string ie;From <b>
to </b>
something like this and read the text within.