Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Learn about the NSXMLParser in iOS

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: [email protected]]]></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 enter image description here

not exactly but Similar.

but first i need to know how i can work with the Parser

Best regards CTS

like image 762
CTSchmidt Avatar asked Jan 30 '13 16:01

CTSchmidt


2 Answers

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:

right callout accessory

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

iPhone details screen

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

iPad right callout accessory

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:

iPad popover

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
like image 100
Rob Avatar answered Nov 16 '22 05:11

Rob


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.

like image 45
Prajwal Udupa Avatar answered Nov 16 '22 03:11

Prajwal Udupa