360iDev MapKit Presentation - Denver 2009

44
Maps on the iPhone Using MapKit for Fun and Profit 1 Wednesday, September 30, 2009

description

The Presentation I gave at 360iDev in Denver 2009

Transcript of 360iDev MapKit Presentation - Denver 2009

Page 1: 360iDev MapKit Presentation - Denver 2009

Maps on the iPhone

Using MapKit for Fun and Profit

1Wednesday, September 30, 2009

Page 2: 360iDev MapKit Presentation - Denver 2009

Add A MKMapView

2Wednesday, September 30, 2009

Page 3: 360iDev MapKit Presentation - Denver 2009

Tweak Parameters

Then Build and Go...

Show the blue breathing dot

3Wednesday, September 30, 2009

Page 4: 360iDev MapKit Presentation - Denver 2009

And Bada-Boom A Map App

4Wednesday, September 30, 2009

Page 5: 360iDev MapKit Presentation - Denver 2009

But we want more, like the breathing

Blue Dot

5Wednesday, September 30, 2009

Page 6: 360iDev MapKit Presentation - Denver 2009

Where we are Headed

Animated ‘Breathing’

‘Left’ Action - Shake Map

‘Right’ Action - Details

6Wednesday, September 30, 2009

Page 7: 360iDev MapKit Presentation - Denver 2009

Stuff To Do

• View Based Project• Parse XML from USGS• Create Earthquakes• Store Earthquakes• Add Annotations• Provide Annotation Views• Respond to Events

7Wednesday, September 30, 2009

Page 8: 360iDev MapKit Presentation - Denver 2009

View

Based Project

Connect the MapView to our VC

8Wednesday, September 30, 2009

Page 9: 360iDev MapKit Presentation - Denver 2009

NSOperationQueue

eventloop

operationthread

EarthquakeParser

NSOperation

NSXMLParser

didStartElement:foundCharcters:

didEndElement:

initWithContentsOfURL:

parse

parseForData:

EarthquakeParserDelegate(MapQuakesViewController)

addEarthquake:

invokeOnMainThread:

parserFinished

addOperation:

Parse XML

Don’t block the main event thread!

Parse XM

L

9Wednesday, September 30, 2009

Page 10: 360iDev MapKit Presentation - Denver 2009

NSOperationQueue

operationthread NSOperation

EarthquakeParseraddOperation:

parseForData:

Kick Off The Parse parseForData: is on an alternate thread, won’t block event thread while downloading the XML

Parse XM

L

10Wednesday, September 30, 2009

Page 11: 360iDev MapKit Presentation - Denver 2009

EarthquakeParser

NSXMLParser

initWithContentsOfURL:

parse

Parse XML on the alternate thread

Each XML Event comes to the parser’s delegate on the alternate thread

Parse XM

L

11Wednesday, September 30, 2009

Page 12: 360iDev MapKit Presentation - Denver 2009

NSXMLParser Kick-off- (BOOL)parseForData { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; NSURL *url = [NSURL URLWithString:feedURLString]; BOOL success = NO; NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url]; [parser setDelegate:self]; [parser setShouldProcessNamespaces:NO]; [parser setShouldReportNamespacePrefixes:NO]; [parser setShouldResolveExternalEntities:NO]; success = [parser parse]; NSError *parseError = [parser parserError]; if (parseError) {! NSLog(@"parse error = %@", parseError); } [parser release]; [pool drain]; return success;}

Parse XM

L

12Wednesday, September 30, 2009

Page 13: 360iDev MapKit Presentation - Denver 2009

EarthquakeParser

NSXMLParser

didStartElement:foundCharcters:

didEndElement:

Parse XML on the alternate thread

Each XML Event comes to the parser’s delegate on the alternate thread

Create Earthquakes

13Wednesday, September 30, 2009

Page 14: 360iDev MapKit Presentation - Denver 2009

Entry Element starts and Earthquake

Create Earthquakes

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { if(nil != qName) { elementName = qName; // swap for the qName if we have a name space } if ([elementName isEqualToString:@"entry"]) { self.currentEarthquake = [[[Earthquake alloc] init] autorelease]; } else if([elementName isEqualToString:@"link"]) { // ignore the related content and just grab the alternate if ([[attributeDict valueForKey:@"rel"] isEqualToString:@"alternate"]) { NSString *link = [attributeDict valueForKey:@"href"]; self.currentEarthquake.detailsURL = [NSString stringWithFormat:@"http://earthquake.usgs.gov/%@", link]; } } else if([elementName isEqualToString:@"title"] || [elementName isEqualToString:@"updated"] || [elementName isEqualToString:@"id"] || [elementName isEqualToString:@"georss:point"] || [elementName isEqualToString:@"georss:elev"]) { self.propertyValue = [NSMutableString string]; } else { self.propertyValue = nil; }}

14Wednesday, September 30, 2009

Page 15: 360iDev MapKit Presentation - Denver 2009

eventloop

EarthquakeParser

EarthquakeParserDelegate(MapQuakesViewController)

addEarthquake:

invokeOnMainThread:

Back to the Main Thread

Let the main thread know an earthquake was found by pushing the addEarthquake: and parserFinished methods onto the main thread

Store Earthquakes

15Wednesday, September 30, 2009

Page 16: 360iDev MapKit Presentation - Denver 2009

Ending Element Pushes Earthquake

Store Earthquakes

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { ... } else if([elementName isEqualToString:@"entry"]) { Earthquake *quake = self.currentEarthquake; self.currentEarthquake = nil; [(id)[self delegate] performSelectorOnMainThread:@selector(addEarthquake:) withObject:quake waitUntilDone:NO]; }}

16Wednesday, September 30, 2009

Page 17: 360iDev MapKit Presentation - Denver 2009

MapQuakesVC holds the Earthquakes

- (void)addEarthquake:(Earthquake *)earthquake { [self.earthquakes addObject:earthquake];}

Store Earthquakes

17Wednesday, September 30, 2009

Page 18: 360iDev MapKit Presentation - Denver 2009

eventloop

EarthquakeParser

EarthquakeParserDelegate(MapQuakesViewController)

invokeOnMainThread:

parserFinished

Back to the Main Thread

Once all the XML is parsed we tell the delegate which displays the earthquakes in the form of an annotation.

Add A

nnotations

18Wednesday, September 30, 2009

Page 19: 360iDev MapKit Presentation - Denver 2009

Back to the Main Thread Again

Add A

nnotations

- (void)parserDidEndDocument:(NSXMLParser *)parser { [(id)[self delegate] performSelectorOnMainThread:@selector(parserFinished) withObject:nil waitUntilDone:NO]; [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; [self autorelease];}

19Wednesday, September 30, 2009

Page 20: 360iDev MapKit Presentation - Denver 2009

Display Earthquakes as Annotations

- (void)parserFinished { [self displayEarthquakes];}

Add A

nnotations

20Wednesday, September 30, 2009

Page 21: 360iDev MapKit Presentation - Denver 2009

Remove Old Annotations

- (void)displayEarthquakes { [self removeAnnotations]; NSArray *visibleQuakes = [self sortAndFilterEarthquakes]; // limit the numer to 100 if(visibleQuakes.count > 100) { // remove the earthquakes from the top of the // list until we are down to 100 NSUInteger removeCount = visibleQuakes.count - 100; NSRange keepers = {removeCount, 100}; // location and length visibleQuakes = [visibleQuakes subarrayWithRange:keepers]; } for(Earthquake *earthquake in visibleQuakes) { EarthquakeAnnotation *eqAnn = [EarthquakeAnnotation annotationWithEarthquake:earthquake]; [self.mapView addAnnotation:eqAnn]; }}

Add A

nnotations

21Wednesday, September 30, 2009

Page 22: 360iDev MapKit Presentation - Denver 2009

- (void)displayEarthquakes { [self removeAnnotations]; NSArray *visibleQuakes = [self sortAndFilterEarthquakes]; // limit the numer to 100 if(visibleQuakes.count > 100) { // remove the earthquakes from the top of the // list until we are down to 100 NSUInteger removeCount = visibleQuakes.count - 100; NSRange keepers = {removeCount, 100}; // location and length visibleQuakes = [visibleQuakes subarrayWithRange:keepers]; } for(Earthquake *earthquake in visibleQuakes) { EarthquakeAnnotation *eqAnn = [EarthquakeAnnotation annotationWithEarthquake:earthquake]; [self.mapView addAnnotation:eqAnn]; }}

Find Visible Quakes Sorted by Magnitude

Add A

nnotations

22Wednesday, September 30, 2009

Page 23: 360iDev MapKit Presentation - Denver 2009

Limit Earthquakes to 100

Add A

nnotations

- (void)displayEarthquakes { [self removeAnnotations]; NSArray *visibleQuakes = [self sortAndFilterEarthquakes]; // limit the numer to 100 if(visibleQuakes.count > 100) { // remove the earthquakes from the top of the // list until we are down to 100 NSUInteger removeCount = visibleQuakes.count - 100; NSRange keepers = {removeCount, 100}; // location and length visibleQuakes = [visibleQuakes subarrayWithRange:keepers]; } for(Earthquake *earthquake in visibleQuakes) { EarthquakeAnnotation *eqAnn = [EarthquakeAnnotation annotationWithEarthquake:earthquake]; [self.mapView addAnnotation:eqAnn]; }}

23Wednesday, September 30, 2009

Page 24: 360iDev MapKit Presentation - Denver 2009

- (void)displayEarthquakes { [self removeAnnotations]; NSArray *visibleQuakes = [self sortAndFilterEarthquakes]; // limit the numer to 100 if(visibleQuakes.count > 100) { // remove the earthquakes from the top of the // list until we are down to 100 NSUInteger removeCount = visibleQuakes.count - 100; NSRange keepers = {removeCount, 100}; // location and length visibleQuakes = [visibleQuakes subarrayWithRange:keepers]; } for(Earthquake *earthquake in visibleQuakes) { EarthquakeAnnotation *eqAnn = [EarthquakeAnnotation annotationWithEarthquake:earthquake]; [self.mapView addAnnotation:eqAnn]; }}

Find and Display Earthquakes

Add A

nnotations

24Wednesday, September 30, 2009

Page 25: 360iDev MapKit Presentation - Denver 2009

Remove old Earthquake Annotations

- (void) removeAnnotations { // remove the old annotations but // don't modify the array while iterating NSArray *annotationsCopy = [self.mapView.annotations copy]; for(id annotation in annotationsCopy) { if([[annotation class] isSubclassOfClass:[EarthquakeAnnotation class]]) { [self.mapView removeAnnotation:annotation]; } } [annotationsCopy release]; }

Add A

nnotations

25Wednesday, September 30, 2009

Page 26: 360iDev MapKit Presentation - Denver 2009

Filter Visible Earthquakes

- (NSArray *)sortAndFilterEarthquakes { // find the visible earthquakes MKCoordinateRegion region = [self.mapView region]; LocationBoundingBox bbox = LocationBoundingBoxMake(region.center, region.span); NSPredicate *latPred = [NSPredicate predicateWithFormat: @"latitude BETWEEN {%@, %@}", [NSNumber numberWithFloat:bbox.min.latitude], [NSNumber numberWithFloat:bbox.max.latitude]]; NSPredicate *lonPred = [NSPredicate predicateWithFormat: @"longitude BETWEEN {%@, %@}", [NSNumber numberWithFloat:bbox.min.longitude], [NSNumber numberWithFloat:bbox.max.longitude]]; NSArray *predicates = [NSArray arrayWithObjects:latPred, lonPred, nil]; NSPredicate *locationPred = [NSCompoundPredicate andPredicateWithSubpredicates:predicates]; NSArray *quakes = [self.earthquakes filteredArrayUsingPredicate:locationPred]; NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"magnitude" ascending:YES]; NSArray *descriptors = [NSArray arrayWithObject:descriptor]; NSArray *sortedEarthquakes = [quakes sortedArrayUsingDescriptors:descriptors]; [descriptor release]; return sortedEarthquakes;}

Add A

nnotations

26Wednesday, September 30, 2009

Page 27: 360iDev MapKit Presentation - Denver 2009

Sort Earthquakes- (NSArray *)sortAndFilterEarthquakes { // find the visible earthquakes MKCoordinateRegion region = [self.mapView region]; LocationBoundingBox bbox = LocationBoundingBoxMake(region.center, region.span); NSPredicate *latPred = [NSPredicate predicateWithFormat: @"latitude BETWEEN {%@, %@}", [NSNumber numberWithFloat:bbox.min.latitude], [NSNumber numberWithFloat:bbox.max.latitude]]; NSPredicate *lonPred = [NSPredicate predicateWithFormat: @"longitude BETWEEN {%@, %@}", [NSNumber numberWithFloat:bbox.min.longitude], [NSNumber numberWithFloat:bbox.max.longitude]]; NSArray *predicates = [NSArray arrayWithObjects:latPred, lonPred, nil]; NSPredicate *locationPred = [NSCompoundPredicate andPredicateWithSubpredicates:predicates]; NSArray *quakes = [self.earthquakes filteredArrayUsingPredicate:locationPred]; NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"magnitude" ascending:YES]; NSArray *descriptors = [NSArray arrayWithObject:descriptor]; NSArray *sortedEarthquakes = [quakes sortedArrayUsingDescriptors:descriptors]; [descriptor release]; return sortedEarthquakes;}

Add A

nnotations

27Wednesday, September 30, 2009

Page 28: 360iDev MapKit Presentation - Denver 2009

MKMapViewDelegate(MapQuakesViewController)

EarthquakeParserDelegate(MapQuakesViewController)

addAnnotation:

viewForAnnotation:

delegate Provides Annotation View

Provide Annotation V

iews

28Wednesday, September 30, 2009

Page 29: 360iDev MapKit Presentation - Denver 2009

Return nil for user location

Provide Annotation V

iews

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation { MKAnnotationView *view = nil; if(annotation != mapView.userLocation) {!! EarthquakeAnnotation *eqAnn = (EarthquakeAnnotation*)annotation;!! view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:@"earthquakeLoc"];!! if(nil == view) { view = [[[EarthquakeAnnotationView alloc] initWithAnnotation:eqAnn reuseIdentifier:@"earthquakeLoc"] autorelease];!! } view.annotation = annotation; [view setCanShowCallout:YES]; UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; [view setRightCalloutAccessoryView:button]; } return view;}

29Wednesday, September 30, 2009

Page 30: 360iDev MapKit Presentation - Denver 2009

Reuse Annotation ViewsProvide A

nnotation View

s

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation { MKAnnotationView *view = nil; if(annotation != mapView.userLocation) {!! EarthquakeAnnotation *eqAnn = (EarthquakeAnnotation*)annotation;!! view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:@"earthquakeLoc"];!! if(nil == view) { view = [[[EarthquakeAnnotationView alloc] initWithAnnotation:eqAnn reuseIdentifier:@"earthquakeLoc"] autorelease];!! } view.annotation = annotation; [view setCanShowCallout:YES]; UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; [view setRightCalloutAccessoryView:button]; } return view;}

30Wednesday, September 30, 2009

Page 31: 360iDev MapKit Presentation - Denver 2009

Create if no re-useable views exist

Provide Annotation V

iews

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation { MKAnnotationView *view = nil; if(annotation != mapView.userLocation) {!! EarthquakeAnnotation *eqAnn = (EarthquakeAnnotation*)annotation;!! view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:@"earthquakeLoc"];!! if(nil == view) { view = [[[EarthquakeAnnotationView alloc] initWithAnnotation:eqAnn reuseIdentifier:@"earthquakeLoc"] autorelease];!! } view.annotation = annotation; [view setCanShowCallout:YES]; UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; [view setRightCalloutAccessoryView:button]; } return view;}

31Wednesday, September 30, 2009

Page 32: 360iDev MapKit Presentation - Denver 2009

Configure ViewProvide A

nnotation View

s

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation { MKAnnotationView *view = nil; if(annotation != mapView.userLocation) {!! EarthquakeAnnotation *eqAnn = (EarthquakeAnnotation*)annotation;!! view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:@"earthquakeLoc"];!! if(nil == view) { view = [[[EarthquakeAnnotationView alloc] initWithAnnotation:eqAnn reuseIdentifier:@"earthquakeLoc"] autorelease];!! } view.annotation = annotation; [view setCanShowCallout:YES]; UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; [view setRightCalloutAccessoryView:button]; } return view;}

32Wednesday, September 30, 2009

Page 33: 360iDev MapKit Presentation - Denver 2009

Provide Annotation V

iews

Custom MKAnnotationViewSubclass

EarthquakeAnnotationView

33Wednesday, September 30, 2009

Page 34: 360iDev MapKit Presentation - Denver 2009

Configure the Annotation View

Provide Annotation V

iews

- (void)setAnnotation:annotation { [super setAnnotation:annotation]; [self.layer.sublayers makeObjectsPerformSelector: @selector(removeFromSuperlayer)]; self.frame = CGRectMake(0.0f, 0.0f, 48.0f, 48.0f); self.earthquake = [(EarthquakeAnnotation *)annotation earthquake]; [self addBreathingLayer]; [self addDarkCircleLayer]; }

34Wednesday, September 30, 2009

Page 35: 360iDev MapKit Presentation - Denver 2009

Shape LayerProvide A

nnotation View

s

- (void) addBreathingLayer { self.circleLayer = [CAShapeLayer layer]; CGColorRef color = [self newFillColor]; self.circleLayer.fillColor = color; CGColorRelease(color); color = [self newStrokeColor]; self.circleLayer.strokeColor = color; CGColorRelease(color); self.circleLayer.lineWidth = 1.0f; CGMutablePathRef path = CGPathCreateMutable(); CGRect square = CGRectMake(0.0f, 0.0f, 48.0f, 48.0f); CGPathAddEllipseInRect(path, NULL, square); self.circleLayer.path = path; CGPathRelease(path); self.circleLayer.frame = square; CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; pathAnimation.duration = 1.5f; pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; pathAnimation.repeatCount = 1E100f; pathAnimation.autoreverses = YES; self.circleLayer.actions = [NSDictionary dictionaryWithObject:pathAnimation forKey:@"path"]; [self.layer addSublayer:self.circleLayer];}

35Wednesday, September 30, 2009

Page 36: 360iDev MapKit Presentation - Denver 2009

Breathing AnimationProvide A

nnotation View

s

- (void) addBreathingLayer { self.circleLayer = [CAShapeLayer layer]; CGColorRef color = [self newFillColor]; self.circleLayer.fillColor = color; CGColorRelease(color); color = [self newStrokeColor]; self.circleLayer.strokeColor = color; CGColorRelease(color); self.circleLayer.lineWidth = 1.0f; CGMutablePathRef path = CGPathCreateMutable(); CGRect square = CGRectMake(0.0f, 0.0f, 48.0f, 48.0f); CGPathAddEllipseInRect(path, NULL, square); self.circleLayer.path = path; CGPathRelease(path); self.circleLayer.frame = square; CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; pathAnimation.duration = 1.5f; pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; pathAnimation.repeatCount = 1E100f; pathAnimation.autoreverses = YES; self.circleLayer.actions = [NSDictionary dictionaryWithObject:pathAnimation forKey:@"path"]; [self.layer addSublayer:self.circleLayer];}

36Wednesday, September 30, 2009

Page 37: 360iDev MapKit Presentation - Denver 2009

Selection Starts Animation

Provide Annotation V

iews

- (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; if(YES == selected) { // animate the bottom shape's path CGRect square = CGRectMake(16.0f, 16.0f, 16.0f, 16.0f); CGMutablePathRef path = CGPathCreateMutable(); CGPathAddEllipseInRect(path, NULL, square); self.circleLayer.path = path; CGPathRelease(path); EarthquakeParser *parser = [EarthquakeParser earthquakeParser]; parser.delegate = self; [parser getShakeMapForEarthquake:self.earthquake]; } else { [CATransaction begin]; [CATransaction setDisableActions:YES]; CGMutablePathRef path = CGPathCreateMutable(); CGRect square = CGRectMake(0.0f, 0.0f, 48.0f, 48.0f); CGPathAddEllipseInRect(path, NULL, square); self.circleLayer.path = path; CGPathRelease(path); [CATransaction commit]; }}

37Wednesday, September 30, 2009

Page 38: 360iDev MapKit Presentation - Denver 2009

De-Selection Stops Animation

Provide Annotation V

iews

- (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; if(YES == selected) { // animate the bottom shape's path CGRect square = CGRectMake(16.0f, 16.0f, 16.0f, 16.0f); CGMutablePathRef path = CGPathCreateMutable(); CGPathAddEllipseInRect(path, NULL, square); self.circleLayer.path = path; CGPathRelease(path); EarthquakeParser *parser = [EarthquakeParser earthquakeParser]; parser.delegate = self; [parser getShakeMapForEarthquake:self.earthquake]; } else { [CATransaction begin]; [CATransaction setDisableActions:YES]; CGMutablePathRef path = CGPathCreateMutable(); CGRect square = CGRectMake(0.0f, 0.0f, 48.0f, 48.0f); CGPathAddEllipseInRect(path, NULL, square); self.circleLayer.path = path; CGPathRelease(path); [CATransaction commit]; }}

38Wednesday, September 30, 2009

Page 39: 360iDev MapKit Presentation - Denver 2009

Selected AnimationProvide Annotation V

iews

39Wednesday, September 30, 2009

Page 40: 360iDev MapKit Presentation - Denver 2009

Respond To Events

‘Right’ Detail

‘Left’ Shake Map

40Wednesday, September 30, 2009

Page 41: 360iDev MapKit Presentation - Denver 2009

Open The Web PageR

espond To Events

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control { NSURL *url = nil; EarthquakeAnnotation *eqAnn = (EarthquakeAnnotation *)[view annotation]; if(view.rightCalloutAccessoryView == control) { NSString *urlString = [eqAnn.earthquake.detailsURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; url = [NSURL URLWithString:urlString]; } else { NSString *urlString = [eqAnn.earthquake.shakeMapURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; url = [NSURL URLWithString:urlString]; } [[UIApplication sharedApplication] openURL:url];}

41Wednesday, September 30, 2009

Page 42: 360iDev MapKit Presentation - Denver 2009

Where To Now?

• Your data will likely have more interesting selection criteria, exploit it

• The ‘detail’ from right and left buttons can do lots more interesting stuff• USGS Provides KML files for shake

maps• Use Core Animation in the right or left

buttons

42Wednesday, September 30, 2009

Page 43: 360iDev MapKit Presentation - Denver 2009

Summary

• Filtering your Annotations is important if you have lots of data

• It’s easy to build your own custom annotation views and add Core Animation to them

43Wednesday, September 30, 2009