Merge pull request #497 from danjordan/Cordova-MapKitPlug

Upgraded MapKit to Cordova 1.6.0+ - README still needs updating
This commit is contained in:
tommy-carlos williams
2012-06-09 16:58:31 -07:00
10 changed files with 638 additions and 0 deletions

27
iOS/MapKit/AsyncImageView.h Executable file
View File

@@ -0,0 +1,27 @@
//
// AsyncImage.h
// SabreHotels
//
// Created by Brett Rudd on 19/03/2010.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface AsyncImageView : UIView {
//could instead be a subclass of UIImageView instead of UIView, depending on what other features you want to
// to build into this class?
NSURLConnection* connection; //keep a reference to the connection so we can cancel download in dealloc
NSMutableData* data; //keep reference to the data so we can collect it as it downloads
//but where is the UIImage reference? We keep it in self.subviews - no need to re-code what we have in the parent class
}
- (void)loadImageFromURL:(NSURL*)url;
- (void)loadDefaultImage;
- (UIImage*) image;
@end

94
iOS/MapKit/AsyncImageView.m Executable file
View File

@@ -0,0 +1,94 @@
//
// AsyncImageView.m
// Postcard
//
// Created by markj on 2/18/09.
// Copyright 2009 Mark Johnson. You have permission to copy parts of this code into your own projects for any use.
// www.markj.net
//
#import "AsyncImageView.h"
// This class demonstrates how the URL loading system can be used to make a UIView subclass
// that can download and display an image asynchronously so that the app doesn't block or freeze
// while the image is downloading. It works fine in a UITableView or other cases where there
// are multiple images being downloaded and displayed all at the same time.
@implementation AsyncImageView
- (void)dealloc {
[connection cancel]; //in case the URL is still downloading
[connection release];
[data release];
[super dealloc];
}
- (void)loadImageFromURL:(NSURL*)url {
if (connection!=nil) { [connection release]; } //in case we are downloading a 2nd image
if (data!=nil) { [data release]; }
NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; //notice how delegate set to self object
//TODO error handling, what if connection is nil?
}
//the URL connection calls this repeatedly as data arrives
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData {
if (data==nil) { data = [[NSMutableData alloc] initWithCapacity:2048]; }
[data appendData:incrementalData];
}
//the URL connection calls this once all the data has downloaded
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
//so self data now has the complete image
[connection release];
connection=nil;
if ([[self subviews] count]>0) {
//then this must be another image, the old one is still in subviews
[[[self subviews] objectAtIndex:0] removeFromSuperview]; //so remove it (releases it also)
}
//make an image view for the image
UIImageView* imageView = [[[UIImageView alloc] initWithImage:[UIImage imageWithData:data]] autorelease];
//make sizing choices based on your needs, experiment with these. maybe not all the calls below are needed.
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask = ( UIViewAutoresizingFlexibleWidth || UIViewAutoresizingFlexibleHeight );
[self addSubview:imageView];
imageView.frame = self.bounds;
[imageView setNeedsLayout];
[self setNeedsLayout];
[data release]; //don't need this any more, its in the UIImageView now
data=nil;
}
//in case we want a local image
- (void)loadDefaultImage {
if ([[self subviews] count]>0) {
//then this must be another image, the old one is still in subviews
[[[self subviews] objectAtIndex:0] removeFromSuperview]; //so remove it (releases it also)
}
//make an image view for the image
UIImageView* imageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon.png"]] autorelease];
//make sizing choices based on your needs, experiment with these. maybe not all the calls below are needed.
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask = ( UIViewAutoresizingFlexibleWidth || UIViewAutoresizingFlexibleHeight );
[self addSubview:imageView];
imageView.frame = self.bounds;
[imageView setNeedsLayout];
[self setNeedsLayout];
}
//just in case you want to get the image directly, here it is in subviews
- (UIImage*) image {
UIImageView* iv = [[self subviews] objectAtIndex:0];
return [iv image];
}
@end

38
iOS/MapKit/CDVAnnotation.h Executable file
View File

@@ -0,0 +1,38 @@
//
// CDVAnnotation.h
// Cordova
//
// Created by Brett Rudd on 17/03/2010.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@interface CDVAnnotation : NSObject <MKAnnotation> {
@private
CLLocationCoordinate2D _coordinate;
NSString *_title;
NSString *_subTitle;
NSString *_imageURL;
NSInteger _index;
MKPlacemark *_placemark;
NSString *pinColor;
BOOL selected;
}
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subTitle;
@property (nonatomic, copy) NSString *imageURL;
@property (nonatomic, assign) NSInteger index;
@property (nonatomic, retain) MKPlacemark *placemark;
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *pinColor;
@property (nonatomic, assign) BOOL selected;
- (void)notifyCalloutInfo:(MKPlacemark *)placemark;
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate index:(NSInteger)index title:(NSString*)title subTitle:(NSString*)subTitle imageURL:(NSString*)imageURL;
@end

50
iOS/MapKit/CDVAnnotation.m Executable file
View File

@@ -0,0 +1,50 @@
//
// PHAnnotation.m
// PhoneGapLib
//
// Created by Brett Rudd on 17/03/2010.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "CDVAnnotation.h"
@implementation CDVAnnotation
@synthesize title = _title;
@synthesize subTitle = _subTitle;
@synthesize index = _index;
@synthesize placemark = _placemark;
@synthesize imageURL = _imageURL;
@synthesize coordinate = _coordinate;
@synthesize pinColor;
@synthesize selected;
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate index:(NSInteger)index title:(NSString*)title subTitle:(NSString*)subTitle imageURL:(NSString*)imageURL {
if ((self = [super init])) {
_coordinate=coordinate;
_title = [title retain];
_subTitle = [subTitle retain];
_index=index;
_imageURL=[imageURL retain];
}
return self;
}
- (NSString *)title {
return _title;
}
- (NSString *)subtitle {
return _subTitle;
}
- (void)notifyCalloutInfo:(MKPlacemark *)newPlacemark {
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:@"MKAnnotationCalloutInfoDidChangeNotification" object:self]];
}
- (void)dealloc {
[_title release], _title = nil;
[super dealloc];
}
@end

42
iOS/MapKit/MapKit.h Normal file
View File

@@ -0,0 +1,42 @@
//
// UIControls.h
// Cordova
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#ifdef CORDOVA_FRAMEWORK
#import <Cordova/CDVPlugin.h>
#else
#import "CDVPlugin.h"
#endif
@interface MapKitView : CDVPlugin <MKMapViewDelegate>
{
}
@property (nonatomic, copy) NSString *buttonCallback;
@property (nonatomic, retain) UIView* childView;
@property (nonatomic, retain) MKMapView* mapView;
@property (nonatomic, retain) UIButton* imageButton;
- (void)createView;
- (void)showMap:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void)hideMap:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void)destroyMap:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void)clearMapPins:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void)addMapPins:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void)setMapData:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) closeButton:(id)button;
@end

73
iOS/MapKit/MapKit.js Normal file
View File

@@ -0,0 +1,73 @@
(function(window) {
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc., Brett Rudd, Jesse MacFadyen
*/
var cordovaRef = window.PhoneGap || window.Cordova || window.cordova;
var MapKit = function() {
this.options = {
buttonCallback: 'window.plugins.mapKit.onMapCallback',
height: 460,
diameter: 1000,
atBottom: true,
lat: 49.281468,
lon: -123.104446
};
}
MapKit.prototype = {
onMapCallback: function(pindex) {
alert('You selected pin : ' + pindex);
},
showMap: function() {
cordovaRef.exec('MapKitView.showMap');
},
setMapData: function(options) {
/*
buttonCallback: String, string callback function
height: Number, - pixels
diameter: Number, - meters
atBottom: Bool,
lat: Number,
lon: Number
*/
for (var v in options) {
if (options.hasOwnProperty(v)) {
this.options[v] = options[v];
}
}
cordovaRef.exec('MapKitView.setMapData', this.options);
},
addMapPins: function(pins) {
var pinStr = '[]';
if (pins) {
pinStr = JSON.stringify(pins);
}
cordovaRef.exec('MapKitView.addMapPins', pinStr);
},
clearMapPins: function() {
cordovaRef.exec('MapKitView.clearMapPins');
},
hideMap: function() {
cordovaRef.exec('MapKitView.hideMap', {});
}
};
cordovaRef.addConstructor(function() {
window.plugins = window.plugins || {};
window.plugins.mapKit = new MapKit();
});
}(window));

303
iOS/MapKit/MapKit.m Normal file
View File

@@ -0,0 +1,303 @@
//
// Cordova
//
//
#import "MapKit.h"
#import "CDVAnnotation.h"
#import "AsyncImageView.h"
#ifdef CORDOVA_FRAMEWORK
// PhoneGap >= 1.2.0
#import <Cordova/JSONKit.h>
#else
// https://github.com/johnezang/JSONKit
#import "JSONKit.h"
#endif
@implementation MapKitView
@synthesize buttonCallback;
@synthesize childView;
@synthesize mapView;
@synthesize imageButton;
-(CDVPlugin*) initWithWebView:(UIWebView*)theWebView
{
self = (MapKitView*)[super initWithWebView:theWebView];
return self;
}
/**
* Create a native map view
*/
- (void)createView
{
self.childView = [[UIView alloc] init];
self.mapView = [[MKMapView alloc] init];
[self.mapView sizeToFit];
self.mapView.delegate = self;
self.mapView.multipleTouchEnabled = YES;
self.mapView.autoresizesSubviews = YES;
self.mapView.userInteractionEnabled = YES;
self.mapView.showsUserLocation = YES;
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.childView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.imageButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.childView addSubview:self.mapView];
[self.childView addSubview:self.imageButton];
[ [ [ self viewController ] view ] addSubview:self.childView];
}
- (void)mapView:(MKMapView *)theMapView regionDidChangeAnimated: (BOOL)animated
{
float currentLat = theMapView.region.center.latitude;
float currentLon = theMapView.region.center.longitude;
float latitudeDelta = theMapView.region.span.latitudeDelta;
float longitudeDelta = theMapView.region.span.longitudeDelta;
NSString* jsString = nil;
jsString = [[NSString alloc] initWithFormat:@"geo.onMapMove(\'%f','%f','%f','%f\');", currentLat,currentLon,latitudeDelta,longitudeDelta];
[self.webView stringByEvaluatingJavaScriptFromString:jsString];
[jsString autorelease];
}
- (void)destroyMap:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
if (self.mapView)
{
[ self.mapView removeAnnotations:mapView.annotations];
[ self.mapView removeFromSuperview];
mapView = nil;
}
if(self.imageButton)
{
[ self.imageButton removeFromSuperview];
[ self.imageButton removeTarget:self action:@selector(closeButton:) forControlEvents:UIControlEventTouchUpInside];
self.imageButton = nil;
}
if(self.childView)
{
[ self.childView removeFromSuperview];
self.childView = nil;
}
self.buttonCallback = nil;
}
- (void)clearMapPins:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
{
[self.mapView removeAnnotations:self.mapView.annotations];
}
- (void)addMapPins:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
{
NSArray *pins = [[arguments objectAtIndex:0] objectFromJSONString];
for (int y = 0; y < pins.count; y++)
{
NSDictionary *pinData = [pins objectAtIndex:y];
CLLocationCoordinate2D pinCoord = { [[pinData objectForKey:@"lat"] floatValue] , [[pinData objectForKey:@"lon"] floatValue] };
NSString *title=[[pinData valueForKey:@"title"] description];
NSString *subTitle=[[pinData valueForKey:@"subTitle"] description];
NSString *imageURL=[[pinData valueForKey:@"imageURL"] description];
NSString *pinColor=[[pinData valueForKey:@"pinColor"] description];
NSInteger index=[[pinData valueForKey:@"index"] integerValue];
BOOL selected = [[pinData valueForKey:@"selected"] boolValue];
CDVAnnotation *annotation = [[CDVAnnotation alloc] initWithCoordinate:pinCoord index:index title:title subTitle:subTitle imageURL:imageURL];
annotation.pinColor=pinColor;
annotation.selected = selected;
[self.mapView addAnnotation:annotation];
[annotation release];
}
}
/**
* Set annotations and mapview settings
*/
- (void)setMapData:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;\
{
if (!self.mapView)
{
[self createView];
}
// defaults
CGFloat height = 480.0f;
CGFloat offsetTop = 0.0f;
if ([options objectForKey:@"height"])
{
height=[[options objectForKey:@"height"] floatValue];
}
if ([options objectForKey:@"offsetTop"])
{
offsetTop=[[options objectForKey:@"offsetTop"] floatValue];
}
if ([options objectForKey:@"buttonCallback"])
{
self.buttonCallback=[[options objectForKey:@"buttonCallback"] description];
}
CLLocationCoordinate2D centerCoord = { [[options objectForKey:@"lat"] floatValue] , [[options objectForKey:@"lon"] floatValue] };
CLLocationDistance diameter = [[options objectForKey:@"diameter"] floatValue];
CGRect webViewBounds = self.webView.bounds;
CGRect mapBounds;
mapBounds = CGRectMake(
webViewBounds.origin.x,
webViewBounds.origin.y + (offsetTop / 2),
webViewBounds.size.width,
webViewBounds.origin.y + height
);
[self.childView setFrame:mapBounds];
[self.mapView setFrame:mapBounds];
MKCoordinateRegion region=[ self.mapView regionThatFits: MKCoordinateRegionMakeWithDistance(centerCoord,
diameter*(height / webViewBounds.size.width),
diameter*(height / webViewBounds.size.width))];
[self.mapView setRegion:region animated:YES];
CGRect frame = CGRectMake(285.0,12.0, 29.0, 29.0);
[ self.imageButton setImage:[UIImage imageNamed:@"www/map-close-button.png"] forState:UIControlStateNormal];
[ self.imageButton setFrame:frame];
[ self.imageButton addTarget:self action:@selector(closeButton:) forControlEvents:UIControlEventTouchUpInside];
}
- (void) closeButton:(id)button
{
[ self hideMap:NULL withDict:NULL];
NSString* jsString = [NSString stringWithFormat:@"%@(\"%i\");", self.buttonCallback,-1];
[self.webView stringByEvaluatingJavaScriptFromString:jsString];
}
- (void)showMap:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
if (!self.mapView)
{
[self createView];
}
self.childView.hidden = NO;
self.mapView.showsUserLocation = YES;
}
- (void)hideMap:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
if (!self.mapView || self.childView.hidden==YES)
{
return;
}
// disable location services, if we no longer need it.
self.mapView.showsUserLocation = NO;
self.childView.hidden = YES;
}
- (MKAnnotationView *) mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>) annotation {
if ([annotation class] != CDVAnnotation.class) {
return nil;
}
CDVAnnotation *phAnnotation=(CDVAnnotation *) annotation;
NSString *identifier=[NSString stringWithFormat:@"INDEX[%i]", phAnnotation.index];
MKPinAnnotationView *annView = (MKPinAnnotationView *)[theMapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annView!=nil) return annView;
annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annView.animatesDrop=YES;
annView.canShowCallout = YES;
if ([phAnnotation.pinColor isEqualToString:@"green"])
annView.pinColor = MKPinAnnotationColorGreen;
else if ([phAnnotation.pinColor isEqualToString:@"purple"])
annView.pinColor = MKPinAnnotationColorPurple;
else
annView.pinColor = MKPinAnnotationColorRed;
AsyncImageView* asyncImage = [[[AsyncImageView alloc] initWithFrame:CGRectMake(0,0, 50, 32)] autorelease];
asyncImage.tag = 999;
if (phAnnotation.imageURL)
{
NSURL *url = [[NSURL alloc] initWithString:phAnnotation.imageURL];
[asyncImage loadImageFromURL:url];
[ url release ];
}
else
{
[asyncImage loadDefaultImage];
}
annView.leftCalloutAccessoryView = asyncImage;
if (self.buttonCallback && phAnnotation.index!=-1)
{
UIButton *myDetailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
myDetailButton.frame = CGRectMake(0, 0, 23, 23);
myDetailButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
myDetailButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
myDetailButton.tag=phAnnotation.index;
annView.rightCalloutAccessoryView = myDetailButton;
[ myDetailButton addTarget:self action:@selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
if(phAnnotation.selected)
{
[self performSelector:@selector(openAnnotation:) withObject:phAnnotation afterDelay:1.0];
}
return [annView autorelease];
}
-(void)openAnnotation:(id <MKAnnotation>) annotation
{
[ self.mapView selectAnnotation:annotation animated:YES];
}
- (void) checkButtonTapped:(id)button
{
UIButton *tmpButton = button;
NSString* jsString = [NSString stringWithFormat:@"%@(\"%i\");", self.buttonCallback, tmpButton.tag];
[self.webView stringByEvaluatingJavaScriptFromString:jsString];
}
- (void)dealloc
{
if (self.mapView)
{
[ self.mapView removeAnnotations:mapView.annotations];
[ self.mapView removeFromSuperview];
self.mapView = nil;
}
if(self.imageButton)
{
[ self.imageButton removeFromSuperview];
self.imageButton = nil;
}
if(childView)
{
[ self.childView removeFromSuperview];
self.childView = nil;
}
self.buttonCallback = nil;
[super dealloc];
}
@end

11
iOS/MapKit/README.md Normal file
View File

@@ -0,0 +1,11 @@
# PhoneGap iOS Map Plugin #
## Adding the Plugin to your project ##
Using this plugin requires [iOS PhoneGap](http://github.com/phonegap/phonegap-iphone) and the MapKit framework.
1. Add the "MapKit" framework to your Xcode project (different in Xcode 3 and 4, search for instructions)
2. Add the .h and .m files to your Plugins folder in your project
3. Add the .js files to your "www" folder on disk, and add reference(s) to the .js files as &lt;script&gt; tags in your html file(s)
4. In your app's [APPNAME]-Info.plist, expand "Plugins", and add a new string key and value under it. For the key, add "MapKitView" (left column) for the key, and add "MapKitView" for the value (right column).

BIN
iOS/MapKit/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB