Maps on the
                                  iPhone
                                Using MapKit for Fun and Profit




Wednesday, September 30, 2009                                     1
Add A MKMapView




Wednesday, September 30, 2009                     2
Tweak Parameters


                                Show the blue breathing dot




                                            Then Build and Go...


Wednesday, September 30, 2009                                      3
And Bada-Boom
                                  A Map App




Wednesday, September 30, 2009                   4
But we want more,
                                like the breathing
                                     Blue Dot




Wednesday, September 30, 2009                        5
Where we are
                                       Headed
                                ‘Right’ Action - Details



                                ‘Left’ Action - Shake Map



                                Animated ‘Breathing’




Wednesday, September 30, 2009                              6
Stuff To Do
                  • View Based Project
                  • Parse XML from USGS
                  • Create Earthquakes
                  • Store Earthquakes
                  • Add Annotations
                  • Provide Annotation Views
                  • Respond to Events

Wednesday, September 30, 2009                  7
View Based Project
              Connect the
            MapView to our VC
Wednesday, September 30, 2009                   8
Parse XML
              NSOperationQueue


             operation
                                NSOperation
              thread




                                                        parseForData:




                                addOperation:




                                                                                                                                         Parse XML
                                                                 EarthquakeParser


                         initWithContentsOfURL:

                                 parse
                                                                              invokeOnMainThread:
                                                       didStartElement:
                                                  foundCharcters:
                                           didEndElement:
                NSXMLParser
                                                                          event
                                                                          loop




                                                            addEarthquake:
                                                            parserFinished




                                                                     EarthquakeParserDelegate
                                                                    (MapQuakesViewController)




                                                                                                    Don’t block the main event thread!


Wednesday, September 30, 2009                                                                                                                    9
NSOperationQueue


                operation
                                  NSOperation
                 thread




                                                                                                       Parse XML
                                                parseForData:




                                addOperation:      EarthquakeParser




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



Wednesday, September 30, 2009                                                                                 10
EarthquakeParser


                           initWithContentsOfURL:




                                                                                                        Parse XML
                                   parse




                   NSXMLParser




         Parse XML on the                                        Each XML Event comes to the parser’s
                                                                 delegate on the alternate thread
         alternate thread
Wednesday, September 30, 2009                                                                                 11
NSXMLParser Kick-off
  - (BOOL)parseForData {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    NSURL *url = [NSURL URLWithString:feedURLString];




                                                                               Parse XML
    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;
  }




Wednesday, September 30, 2009                                                        12
Create Earthquakes
                                                    EarthquakeParser




                                          didStartElement:
                                     foundCharcters:
                                didEndElement:
                NSXMLParser




         Parse XML on the                                       Each XML Event comes to the parser’s
                                                                delegate on the alternate thread
         alternate thread
Wednesday, September 30, 2009                                                                                       13
Entry Element starts
                          and Earthquake
 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
   namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName




                                                                                    Create Earthquakes
     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;
     }
 }


Wednesday, September 30, 2009                                                                    14
EarthquakeParser                     Back to the Main
                                                                         Thread




                                                                                                          Store Earthquakes
                                           invokeOnMainThread:




                                       event
                                       loop




                      addEarthquake:




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




Wednesday, September 30, 2009                                                                                          15
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];
    }
  }




Wednesday, September 30, 2009                                                                  16
MapQuakesVC holds the
                     Earthquakes




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




Wednesday, September 30, 2009                                         17
EarthquakeParser                        Back to the Main
                                                                       Thread




                                                                                                         Add Annotations
                                         invokeOnMainThread:




                                     event
                                     loop




                   parserFinished




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




Wednesday, September 30, 2009                                                                                       18
Back to the Main Thread
                          Again




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




Wednesday, September 30, 2009                                                               19
Display Earthquakes as
                        Annotations




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




Wednesday, September 30, 2009                          20
Remove Old Annotations
- (void)displayEarthquakes {




                                                                    Add Annotations
  [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];
    }
}


Wednesday, September 30, 2009                                                  21
Find Visible Quakes
                          Sorted by Magnitude
- (void)displayEarthquakes {




                                                                    Add Annotations
  [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];
    }
}


Wednesday, September 30, 2009                                                  22
Limit Earthquakes to
                                  100
- (void)displayEarthquakes {




                                                                    Add Annotations
  [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];
    }
}


Wednesday, September 30, 2009                                                  23
Find and Display
                                  Earthquakes
- (void)displayEarthquakes {




                                                                    Add Annotations
  [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];
    }
}


Wednesday, September 30, 2009                                                  24
Remove old Earthquake
                       Annotations




                                                                                 Add 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];
 }




Wednesday, September 30, 2009                                                               25
Filter Visible
                                Earthquakes
  - (NSArray *)sortAndFilterEarthquakes {




                                                                                      Add Annotations
    // 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;
  }

Wednesday, September 30, 2009                                                                    26
Sort Earthquakes
  - (NSArray *)sortAndFilterEarthquakes {




                                                                                      Add Annotations
    // 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;
  }

Wednesday, September 30, 2009                                                                    27
Provide Annotation Views
                                       EarthquakeParserDelegate
                                      (MapQuakesViewController)




                                   addAnnotation:




                                viewForAnnotation:




                                     MKMapViewDelegate
                                  (MapQuakesViewController)




         delegate Provides
         Annotation View
Wednesday, September 30, 2009                                                       28
Return nil for user




                                                                                  Provide Annotation Views
                                location
 - (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;
 }




Wednesday, September 30, 2009                                                                       29
Provide Annotation Views
                 Reuse Annotation Views
 - (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;
 }




Wednesday, September 30, 2009                                                                       30
Create if no re-useable




                                                                                  Provide Annotation Views
                        views exist
 - (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;
 }




Wednesday, September 30, 2009                                                                       31
Provide Annotation Views
                                Configure View
 - (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;
 }




Wednesday, September 30, 2009                                                                      32
Custom




                                                     Provide Annotation Views
                                   MKAnnotationView
                                   Subclass




                                EarthquakeAnnotationView


Wednesday, September 30, 2009                                          33
Configure the




                                                                         Provide Annotation Views
                                Annotation View

- (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];

}




Wednesday, September 30, 2009                                                              34
Provide Annotation Views
                                Shape Layer
  - (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];
  }


Wednesday, September 30, 2009                                                                             35
Provide Annotation Views
                          Breathing Animation
  - (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];
  }


Wednesday, September 30, 2009                                                                             36
Selection Starts




                                                                         Provide Annotation Views
                                   Animation
   - (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];
     }
   }




Wednesday, September 30, 2009                                                              37
De-Selection Stops




                                                                         Provide Annotation Views
                                    Animation
   - (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];
     }
   }




Wednesday, September 30, 2009                                                              38
Selected Animation




                                                Provide Annotation Views
Wednesday, September 30, 2009                                     39
‘Right’ Detail




                                                   Respond To Events
                                ‘Left’ Shake Map




Wednesday, September 30, 2009                                   40
Open The Web Page




                                                                                        Respond 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];
 }




Wednesday, September 30, 2009                                                                        41
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

Wednesday, September 30, 2009                                     42
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



Wednesday, September 30, 2009                                  43
Thanks!
                                Pragmatic iPhone Studio




Wednesday, September 30, 2009                             44

More Related Content

PPTX
Intro to Reactive Thinking and RxJava 2
DOCX
Unidad 4 robotica.docx
PPT
Description of Open-E Snapshot
PDF
yt: An Analysis and Visualization System for Astrophysical Simulation Data
PDF
SAP REST Summit 2009 - Atom At Work
PPTX
RxJava 2 Reactive extensions for the JVM
PDF
Game Kit - iPhone
PDF
Core Animation
Intro to Reactive Thinking and RxJava 2
Unidad 4 robotica.docx
Description of Open-E Snapshot
yt: An Analysis and Visualization System for Astrophysical Simulation Data
SAP REST Summit 2009 - Atom At Work
RxJava 2 Reactive extensions for the JVM
Game Kit - iPhone
Core Animation

Similar to 360iDev MapKit Presentation - Denver 2009 (13)

KEY
Hdc09 I Phone Dev Connecting To Web
PPT
iOS Multithreading
PDF
iOS: Web Services and XML parsing
PDF
Slideshare presentation
PDF
XML-Free Programming : Java Server and Client Development without &lt;>
ODP
SCDJWS 6. REST JAX-P
PDF
IQPC Canada XML 2001: How to Use XML Parsing to Enhance Electronic Communication
PDF
Elements for an iOS Backend
PDF
Xml & Java
PDF
Cross-Platform Native Mobile Development with Eclipse
PDF
Native Phone Development 101
PPTX
eXtensible Markup Language (XML)
PDF
Dom API In Java Script
Hdc09 I Phone Dev Connecting To Web
iOS Multithreading
iOS: Web Services and XML parsing
Slideshare presentation
XML-Free Programming : Java Server and Client Development without &lt;>
SCDJWS 6. REST JAX-P
IQPC Canada XML 2001: How to Use XML Parsing to Enhance Electronic Communication
Elements for an iOS Backend
Xml & Java
Cross-Platform Native Mobile Development with Eclipse
Native Phone Development 101
eXtensible Markup Language (XML)
Dom API In Java Script
Ad

Recently uploaded (20)

DOCX
Cambridge-Practice-Tests-for-IELTS-12.docx
PDF
medical_surgical_nursing_10th_edition_ignatavicius_TEST_BANK_pdf.pdf
PDF
MBA _Common_ 2nd year Syllabus _2021-22_.pdf
PPTX
202450812 BayCHI UCSC-SV 20250812 v17.pptx
PPTX
History, Philosophy and sociology of education (1).pptx
PPTX
A powerpoint presentation on the Revised K-10 Science Shaping Paper
PDF
Hazard Identification & Risk Assessment .pdf
PDF
LDMMIA Reiki Yoga Finals Review Spring Summer
PDF
AI-driven educational solutions for real-life interventions in the Philippine...
PDF
Paper A Mock Exam 9_ Attempt review.pdf.
PDF
Trump Administration's workforce development strategy
PDF
Vision Prelims GS PYQ Analysis 2011-2022 www.upscpdf.com.pdf
PPTX
CHAPTER IV. MAN AND BIOSPHERE AND ITS TOTALITY.pptx
PDF
David L Page_DCI Research Study Journey_how Methodology can inform one's prac...
PPTX
Share_Module_2_Power_conflict_and_negotiation.pptx
PDF
What if we spent less time fighting change, and more time building what’s rig...
PDF
IGGE1 Understanding the Self1234567891011
PPTX
B.Sc. DS Unit 2 Software Engineering.pptx
PDF
My India Quiz Book_20210205121199924.pdf
PDF
Uderstanding digital marketing and marketing stratergie for engaging the digi...
Cambridge-Practice-Tests-for-IELTS-12.docx
medical_surgical_nursing_10th_edition_ignatavicius_TEST_BANK_pdf.pdf
MBA _Common_ 2nd year Syllabus _2021-22_.pdf
202450812 BayCHI UCSC-SV 20250812 v17.pptx
History, Philosophy and sociology of education (1).pptx
A powerpoint presentation on the Revised K-10 Science Shaping Paper
Hazard Identification & Risk Assessment .pdf
LDMMIA Reiki Yoga Finals Review Spring Summer
AI-driven educational solutions for real-life interventions in the Philippine...
Paper A Mock Exam 9_ Attempt review.pdf.
Trump Administration's workforce development strategy
Vision Prelims GS PYQ Analysis 2011-2022 www.upscpdf.com.pdf
CHAPTER IV. MAN AND BIOSPHERE AND ITS TOTALITY.pptx
David L Page_DCI Research Study Journey_how Methodology can inform one's prac...
Share_Module_2_Power_conflict_and_negotiation.pptx
What if we spent less time fighting change, and more time building what’s rig...
IGGE1 Understanding the Self1234567891011
B.Sc. DS Unit 2 Software Engineering.pptx
My India Quiz Book_20210205121199924.pdf
Uderstanding digital marketing and marketing stratergie for engaging the digi...
Ad

360iDev MapKit Presentation - Denver 2009

  • 1. Maps on the iPhone Using MapKit for Fun and Profit Wednesday, September 30, 2009 1
  • 2. Add A MKMapView Wednesday, September 30, 2009 2
  • 3. Tweak Parameters Show the blue breathing dot Then Build and Go... Wednesday, September 30, 2009 3
  • 4. And Bada-Boom A Map App Wednesday, September 30, 2009 4
  • 5. But we want more, like the breathing Blue Dot Wednesday, September 30, 2009 5
  • 6. Where we are Headed ‘Right’ Action - Details ‘Left’ Action - Shake Map Animated ‘Breathing’ Wednesday, September 30, 2009 6
  • 7. Stuff To Do • View Based Project • Parse XML from USGS • Create Earthquakes • Store Earthquakes • Add Annotations • Provide Annotation Views • Respond to Events Wednesday, September 30, 2009 7
  • 8. View Based Project Connect the MapView to our VC Wednesday, September 30, 2009 8
  • 9. Parse XML NSOperationQueue operation NSOperation thread parseForData: addOperation: Parse XML EarthquakeParser initWithContentsOfURL: parse invokeOnMainThread: didStartElement: foundCharcters: didEndElement: NSXMLParser event loop addEarthquake: parserFinished EarthquakeParserDelegate (MapQuakesViewController) Don’t block the main event thread! Wednesday, September 30, 2009 9
  • 10. NSOperationQueue operation NSOperation thread Parse XML parseForData: addOperation: EarthquakeParser Kick Off The Parse parseForData: is on an alternate thread, won’t block event thread while downloading the XML Wednesday, September 30, 2009 10
  • 11. EarthquakeParser initWithContentsOfURL: Parse XML parse NSXMLParser Parse XML on the Each XML Event comes to the parser’s delegate on the alternate thread alternate thread Wednesday, September 30, 2009 11
  • 12. NSXMLParser Kick-off - (BOOL)parseForData { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; NSURL *url = [NSURL URLWithString:feedURLString]; Parse XML 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; } Wednesday, September 30, 2009 12
  • 13. Create Earthquakes EarthquakeParser didStartElement: foundCharcters: didEndElement: NSXMLParser Parse XML on the Each XML Event comes to the parser’s delegate on the alternate thread alternate thread Wednesday, September 30, 2009 13
  • 14. Entry Element starts and Earthquake - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName Create Earthquakes 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; } } Wednesday, September 30, 2009 14
  • 15. EarthquakeParser Back to the Main Thread Store Earthquakes invokeOnMainThread: event loop addEarthquake: EarthquakeParserDelegate Let the main thread know an earthquake was (MapQuakesViewController) found by pushing the addEarthquake: and parserFinished methods onto the main thread Wednesday, September 30, 2009 15
  • 16. 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]; } } Wednesday, September 30, 2009 16
  • 17. MapQuakesVC holds the Earthquakes Store Earthquakes - (void)addEarthquake:(Earthquake *)earthquake { [self.earthquakes addObject:earthquake]; } Wednesday, September 30, 2009 17
  • 18. EarthquakeParser Back to the Main Thread Add Annotations invokeOnMainThread: event loop parserFinished EarthquakeParserDelegate Once all the XML is parsed we tell the delegate (MapQuakesViewController) which displays the earthquakes in the form of an annotation. Wednesday, September 30, 2009 18
  • 19. Back to the Main Thread Again Add Annotations - (void)parserDidEndDocument:(NSXMLParser *)parser { [(id)[self delegate] performSelectorOnMainThread:@selector(parserFinished) withObject:nil waitUntilDone:NO]; [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; [self autorelease]; } Wednesday, September 30, 2009 19
  • 20. Display Earthquakes as Annotations Add Annotations - (void)parserFinished { [self displayEarthquakes]; } Wednesday, September 30, 2009 20
  • 21. Remove Old Annotations - (void)displayEarthquakes { Add Annotations [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]; } } Wednesday, September 30, 2009 21
  • 22. Find Visible Quakes Sorted by Magnitude - (void)displayEarthquakes { Add Annotations [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]; } } Wednesday, September 30, 2009 22
  • 23. Limit Earthquakes to 100 - (void)displayEarthquakes { Add Annotations [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]; } } Wednesday, September 30, 2009 23
  • 24. Find and Display Earthquakes - (void)displayEarthquakes { Add Annotations [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]; } } Wednesday, September 30, 2009 24
  • 25. Remove old Earthquake Annotations Add 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]; } Wednesday, September 30, 2009 25
  • 26. Filter Visible Earthquakes - (NSArray *)sortAndFilterEarthquakes { Add Annotations // 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; } Wednesday, September 30, 2009 26
  • 27. Sort Earthquakes - (NSArray *)sortAndFilterEarthquakes { Add Annotations // 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; } Wednesday, September 30, 2009 27
  • 28. Provide Annotation Views EarthquakeParserDelegate (MapQuakesViewController) addAnnotation: viewForAnnotation: MKMapViewDelegate (MapQuakesViewController) delegate Provides Annotation View Wednesday, September 30, 2009 28
  • 29. Return nil for user Provide Annotation Views location - (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; } Wednesday, September 30, 2009 29
  • 30. Provide Annotation Views Reuse Annotation Views - (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; } Wednesday, September 30, 2009 30
  • 31. Create if no re-useable Provide Annotation Views views exist - (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; } Wednesday, September 30, 2009 31
  • 32. Provide Annotation Views Configure View - (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; } Wednesday, September 30, 2009 32
  • 33. Custom Provide Annotation Views MKAnnotationView Subclass EarthquakeAnnotationView Wednesday, September 30, 2009 33
  • 34. Configure the Provide Annotation Views Annotation View - (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]; } Wednesday, September 30, 2009 34
  • 35. Provide Annotation Views Shape Layer - (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]; } Wednesday, September 30, 2009 35
  • 36. Provide Annotation Views Breathing Animation - (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]; } Wednesday, September 30, 2009 36
  • 37. Selection Starts Provide Annotation Views Animation - (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]; } } Wednesday, September 30, 2009 37
  • 38. De-Selection Stops Provide Annotation Views Animation - (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]; } } Wednesday, September 30, 2009 38
  • 39. Selected Animation Provide Annotation Views Wednesday, September 30, 2009 39
  • 40. ‘Right’ Detail Respond To Events ‘Left’ Shake Map Wednesday, September 30, 2009 40
  • 41. Open The Web Page Respond 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]; } Wednesday, September 30, 2009 41
  • 42. 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 Wednesday, September 30, 2009 42
  • 43. 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 Wednesday, September 30, 2009 43
  • 44. Thanks! Pragmatic iPhone Studio Wednesday, September 30, 2009 44