SlideShare ist ein Scribd-Unternehmen logo
1 von 44
Downloaden Sie, um offline zu lesen
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

Weitere ähnliche Inhalte

Kürzlich hochgeladen

The Singapore Teaching Practice document
The Singapore Teaching Practice documentThe Singapore Teaching Practice document
The Singapore Teaching Practice documentXsasf Sfdfasd
 
PISA-VET launch_El Iza Mohamedou_19 March 2024.pptx
PISA-VET launch_El Iza Mohamedou_19 March 2024.pptxPISA-VET launch_El Iza Mohamedou_19 March 2024.pptx
PISA-VET launch_El Iza Mohamedou_19 March 2024.pptxEduSkills OECD
 
Patient Counselling. Definition of patient counseling; steps involved in pati...
Patient Counselling. Definition of patient counseling; steps involved in pati...Patient Counselling. Definition of patient counseling; steps involved in pati...
Patient Counselling. Definition of patient counseling; steps involved in pati...raviapr7
 
How to Add Existing Field in One2Many Tree View in Odoo 17
How to Add Existing Field in One2Many Tree View in Odoo 17How to Add Existing Field in One2Many Tree View in Odoo 17
How to Add Existing Field in One2Many Tree View in Odoo 17Celine George
 
Maximizing Impact_ Nonprofit Website Planning, Budgeting, and Design.pdf
Maximizing Impact_ Nonprofit Website Planning, Budgeting, and Design.pdfMaximizing Impact_ Nonprofit Website Planning, Budgeting, and Design.pdf
Maximizing Impact_ Nonprofit Website Planning, Budgeting, and Design.pdfTechSoup
 
CHUYÊN ĐỀ DẠY THÊM TIẾNG ANH LỚP 11 - GLOBAL SUCCESS - NĂM HỌC 2023-2024 - HK...
CHUYÊN ĐỀ DẠY THÊM TIẾNG ANH LỚP 11 - GLOBAL SUCCESS - NĂM HỌC 2023-2024 - HK...CHUYÊN ĐỀ DẠY THÊM TIẾNG ANH LỚP 11 - GLOBAL SUCCESS - NĂM HỌC 2023-2024 - HK...
CHUYÊN ĐỀ DẠY THÊM TIẾNG ANH LỚP 11 - GLOBAL SUCCESS - NĂM HỌC 2023-2024 - HK...Nguyen Thanh Tu Collection
 
How to Add a New Field in Existing Kanban View in Odoo 17
How to Add a New Field in Existing Kanban View in Odoo 17How to Add a New Field in Existing Kanban View in Odoo 17
How to Add a New Field in Existing Kanban View in Odoo 17Celine George
 
5 charts on South Africa as a source country for international student recrui...
5 charts on South Africa as a source country for international student recrui...5 charts on South Africa as a source country for international student recrui...
5 charts on South Africa as a source country for international student recrui...CaraSkikne1
 
DUST OF SNOW_BY ROBERT FROST_EDITED BY_ TANMOY MISHRA
DUST OF SNOW_BY ROBERT FROST_EDITED BY_ TANMOY MISHRADUST OF SNOW_BY ROBERT FROST_EDITED BY_ TANMOY MISHRA
DUST OF SNOW_BY ROBERT FROST_EDITED BY_ TANMOY MISHRATanmoy Mishra
 
The basics of sentences session 10pptx.pptx
The basics of sentences session 10pptx.pptxThe basics of sentences session 10pptx.pptx
The basics of sentences session 10pptx.pptxheathfieldcps1
 
General views of Histopathology and step
General views of Histopathology and stepGeneral views of Histopathology and step
General views of Histopathology and stepobaje godwin sunday
 
Practical Research 1: Lesson 8 Writing the Thesis Statement.pptx
Practical Research 1: Lesson 8 Writing the Thesis Statement.pptxPractical Research 1: Lesson 8 Writing the Thesis Statement.pptx
Practical Research 1: Lesson 8 Writing the Thesis Statement.pptxKatherine Villaluna
 
HED Office Sohayok Exam Question Solution 2023.pdf
HED Office Sohayok Exam Question Solution 2023.pdfHED Office Sohayok Exam Question Solution 2023.pdf
HED Office Sohayok Exam Question Solution 2023.pdfMohonDas
 
Patterns of Written Texts Across Disciplines.pptx
Patterns of Written Texts Across Disciplines.pptxPatterns of Written Texts Across Disciplines.pptx
Patterns of Written Texts Across Disciplines.pptxMYDA ANGELICA SUAN
 
How to Make a Field read-only in Odoo 17
How to Make a Field read-only in Odoo 17How to Make a Field read-only in Odoo 17
How to Make a Field read-only in Odoo 17Celine George
 
Human-AI Co-Creation of Worked Examples for Programming Classes
Human-AI Co-Creation of Worked Examples for Programming ClassesHuman-AI Co-Creation of Worked Examples for Programming Classes
Human-AI Co-Creation of Worked Examples for Programming ClassesMohammad Hassany
 
UKCGE Parental Leave Discussion March 2024
UKCGE Parental Leave Discussion March 2024UKCGE Parental Leave Discussion March 2024
UKCGE Parental Leave Discussion March 2024UKCGE
 
How to Manage Cross-Selling in Odoo 17 Sales
How to Manage Cross-Selling in Odoo 17 SalesHow to Manage Cross-Selling in Odoo 17 Sales
How to Manage Cross-Selling in Odoo 17 SalesCeline George
 
How to Solve Singleton Error in the Odoo 17
How to Solve Singleton Error in the  Odoo 17How to Solve Singleton Error in the  Odoo 17
How to Solve Singleton Error in the Odoo 17Celine George
 
M-2- General Reactions of amino acids.pptx
M-2- General Reactions of amino acids.pptxM-2- General Reactions of amino acids.pptx
M-2- General Reactions of amino acids.pptxDr. Santhosh Kumar. N
 

Kürzlich hochgeladen (20)

The Singapore Teaching Practice document
The Singapore Teaching Practice documentThe Singapore Teaching Practice document
The Singapore Teaching Practice document
 
PISA-VET launch_El Iza Mohamedou_19 March 2024.pptx
PISA-VET launch_El Iza Mohamedou_19 March 2024.pptxPISA-VET launch_El Iza Mohamedou_19 March 2024.pptx
PISA-VET launch_El Iza Mohamedou_19 March 2024.pptx
 
Patient Counselling. Definition of patient counseling; steps involved in pati...
Patient Counselling. Definition of patient counseling; steps involved in pati...Patient Counselling. Definition of patient counseling; steps involved in pati...
Patient Counselling. Definition of patient counseling; steps involved in pati...
 
How to Add Existing Field in One2Many Tree View in Odoo 17
How to Add Existing Field in One2Many Tree View in Odoo 17How to Add Existing Field in One2Many Tree View in Odoo 17
How to Add Existing Field in One2Many Tree View in Odoo 17
 
Maximizing Impact_ Nonprofit Website Planning, Budgeting, and Design.pdf
Maximizing Impact_ Nonprofit Website Planning, Budgeting, and Design.pdfMaximizing Impact_ Nonprofit Website Planning, Budgeting, and Design.pdf
Maximizing Impact_ Nonprofit Website Planning, Budgeting, and Design.pdf
 
CHUYÊN ĐỀ DẠY THÊM TIẾNG ANH LỚP 11 - GLOBAL SUCCESS - NĂM HỌC 2023-2024 - HK...
CHUYÊN ĐỀ DẠY THÊM TIẾNG ANH LỚP 11 - GLOBAL SUCCESS - NĂM HỌC 2023-2024 - HK...CHUYÊN ĐỀ DẠY THÊM TIẾNG ANH LỚP 11 - GLOBAL SUCCESS - NĂM HỌC 2023-2024 - HK...
CHUYÊN ĐỀ DẠY THÊM TIẾNG ANH LỚP 11 - GLOBAL SUCCESS - NĂM HỌC 2023-2024 - HK...
 
How to Add a New Field in Existing Kanban View in Odoo 17
How to Add a New Field in Existing Kanban View in Odoo 17How to Add a New Field in Existing Kanban View in Odoo 17
How to Add a New Field in Existing Kanban View in Odoo 17
 
5 charts on South Africa as a source country for international student recrui...
5 charts on South Africa as a source country for international student recrui...5 charts on South Africa as a source country for international student recrui...
5 charts on South Africa as a source country for international student recrui...
 
DUST OF SNOW_BY ROBERT FROST_EDITED BY_ TANMOY MISHRA
DUST OF SNOW_BY ROBERT FROST_EDITED BY_ TANMOY MISHRADUST OF SNOW_BY ROBERT FROST_EDITED BY_ TANMOY MISHRA
DUST OF SNOW_BY ROBERT FROST_EDITED BY_ TANMOY MISHRA
 
The basics of sentences session 10pptx.pptx
The basics of sentences session 10pptx.pptxThe basics of sentences session 10pptx.pptx
The basics of sentences session 10pptx.pptx
 
General views of Histopathology and step
General views of Histopathology and stepGeneral views of Histopathology and step
General views of Histopathology and step
 
Practical Research 1: Lesson 8 Writing the Thesis Statement.pptx
Practical Research 1: Lesson 8 Writing the Thesis Statement.pptxPractical Research 1: Lesson 8 Writing the Thesis Statement.pptx
Practical Research 1: Lesson 8 Writing the Thesis Statement.pptx
 
HED Office Sohayok Exam Question Solution 2023.pdf
HED Office Sohayok Exam Question Solution 2023.pdfHED Office Sohayok Exam Question Solution 2023.pdf
HED Office Sohayok Exam Question Solution 2023.pdf
 
Patterns of Written Texts Across Disciplines.pptx
Patterns of Written Texts Across Disciplines.pptxPatterns of Written Texts Across Disciplines.pptx
Patterns of Written Texts Across Disciplines.pptx
 
How to Make a Field read-only in Odoo 17
How to Make a Field read-only in Odoo 17How to Make a Field read-only in Odoo 17
How to Make a Field read-only in Odoo 17
 
Human-AI Co-Creation of Worked Examples for Programming Classes
Human-AI Co-Creation of Worked Examples for Programming ClassesHuman-AI Co-Creation of Worked Examples for Programming Classes
Human-AI Co-Creation of Worked Examples for Programming Classes
 
UKCGE Parental Leave Discussion March 2024
UKCGE Parental Leave Discussion March 2024UKCGE Parental Leave Discussion March 2024
UKCGE Parental Leave Discussion March 2024
 
How to Manage Cross-Selling in Odoo 17 Sales
How to Manage Cross-Selling in Odoo 17 SalesHow to Manage Cross-Selling in Odoo 17 Sales
How to Manage Cross-Selling in Odoo 17 Sales
 
How to Solve Singleton Error in the Odoo 17
How to Solve Singleton Error in the  Odoo 17How to Solve Singleton Error in the  Odoo 17
How to Solve Singleton Error in the Odoo 17
 
M-2- General Reactions of amino acids.pptx
M-2- General Reactions of amino acids.pptxM-2- General Reactions of amino acids.pptx
M-2- General Reactions of amino acids.pptx
 

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