Tested with Google Maps SDK for iOS ver 1.6.1 and Titanium SDK version 3.1.3.GA, newer of versions of each SDK may work but are unsupported. if you are having problems with the latest SDK version, please download a supported version from: https://developers.google.com/maps/documentation/ios/releases
Please follow Google's terms of service and "Attribution Requirements" as detailed in https://developers.google.com/maps/documentation/ios/intro?hl=en
You will need to go through a one-time setup process and get Google Maps SDK for iOS.
Download the Google Maps SDK to your mac Visit this link: https://developers.google.com/maps/documentation/ios/start under "Getting the Google Maps SDK for iOS"
open the ZIP file somewhere, we will use it in the next step
to use Google maps you will need to obtain a license key, instructions are here: https://developers.google.com/maps/documentation/ios/start under "Obtaining an API Key"
Make sure you activate the correct service "Google Maps SDK for iOS" and not "Google Maps API v3" which is for Android.
Make sure you enter your app-id (e.g. com.moshemarciano.maptest) and associate it with your license key, or maps will not load!
Also make sure your key is active (status: Inactive will not work), view this for more details: https://developers.google.com/console/help/?csw=1#activatingapis
it might take sometime before your license or app-id association will become active.
These steps are only needed if you are upgrading from version 1.0 of the module. New users can skip to the next section.
Usually you will have your modules under this folder
~/Library/Application Support/Titanium/modules/iphone
or
/Library/Application\ Support/Titanium/modules/iphone/
for example, once you open it it should look like this
/Library/Application Support/Titanium/modules/iphone/com.moshemarciano.googlemaps/1.5
on your newly created module folder find the module.xcconfig file and edit it
on line #13 you will find a directive that begins with:
OTHER_LDFLAGS=$(inherited) -F YOUR-GOOGLE-FRAMEWORK-FOLDER -framework GoogleMaps
replace the YOUR-GOOGLE-FRAMEWORK-FOLDER with a full (preferably absolute) path of the folder where you opened your Google Maps SDK. It should contain the GoogleMaps.framework folder (it is a big one)
example #1:
OTHER_LDFLAGS=$(inherited) -F"/Users/moshem/Documents/src/ti_modules/ios/googlemaps" -framework GoogleMaps -framework AVFoundation -framework CoreData -framework CoreLocation -framework CoreText -framework GLKit -framework ImageIO -framework OpenGLES -framework QuartzCore -framework SystemConfiguration -weak_library /usr/lib/libc++.dylib
example #2
OTHER_LDFLAGS=$(inherited) -F /Users/someguy/bin/GoogleMaps/GoogleMaps-iOS-1.3.1/ -framework GoogleMaps -framework AVFoundation -framework CoreData -framework CoreLocation -framework CoreText -framework GLKit -framework ImageIO -framework OpenGLES -framework QuartzCore -framework SystemConfiguration -weak_library /usr/lib/libc++.dylib
in your TiApp.xml you will need to add some lines at the bottom (or use Ti Studio GUI to add the module)
after the SDK version line add the module declaration
<sdk-version>3.1.0.GA</sdk-version>
<modules>
<module platform="iphone" version="1.5">com.moshemarciano.googlemaps</module>
</modules>
Final one-time step would be to copy the Google SDK bundle file to your project Resources folder. (Alloy users see next section)
go into the "GoogleMaps.framework" folder and into it's "Resources" sub folder and copy the "GoogleMaps.bundle" into your Project Resources folder
your Titanium project Resources folder should look like
GoogleMaps.bundle/
KS_nav_ui.png
KS_nav_views.png
app.js
iphone/
ui/
Final one-time step would be to copy the Google SDK bundle file to your project app assets folder.
go into the "GoogleMaps.framework" folder and into it's "Resources" sub folder and copy the "GoogleMaps.bundle" into your Project Resources folder
your Titanium project folder structure should look like
alloy.js
app/
controllers/
models/
styles/
views/
assets/
iphone/
GoogleMaps.bundle/
The suggested way is to start with the module provided app.js file to learn the basics of using the module and the Google Maps API
Place any graphics, including custom markers in the Resources/iphone folder
If you are having trouble see the bottom of the file for the troubleshooting section
var googlemaps = require('com.moshemarciano.googleMaps');
googlemaps.licenseKey("BRzljeflsen_TuhKhkKqqa4YTbz398jd2kaA2w");
var map = googlemaps.createGoogleMap({
height:500,
width:300,
top:50
});
// set camera option #1
map.setCamera ({
latitude:32.066158,
longitude:34.77781900000002,
zoom:6});
// set camera option #2
map.setCamera ({
latitude:32.066158,
longitude:34.77781900000002,
zoom:6,
bearing:80,
viewingAngle:70});
index.xml
=========
<Window title="Window 1">
<Module id="map" module="com.moshemarciano.googleMaps" method="createGoogleMap"
width="300" height="300" top="50"/>
</Window>
alloy.js
========
var googlemaps = require('com.moshemarciano.googleMaps');
googlemaps.licenseKey("BRzljeflsen_TuhKhkKqqa4YTbz398jd2kaA2w");
index.js
========
$.map.setCamera ({
latitude:51.43580627441406,
longitude:-0.14256912469863892,
zoom:15,
bearing:0,
viewingAngle:0
});
// Alloy users should use $.map. convention
map.mapType = "normal"; // normal, hybrid, satellite, terrain
map.traffic = true;
map.zoomGestures = true;
map.scrollGestures = true;
map.tiltGestures = true;
map.rotateGestures = true;
// triggers the iOS "allow location services" alert
map.myLocation = true;
// show my location button control
map.myLocationButton = true;
// show compass button, only visible when camera bearing != 0
map.compassButton = true;
// overrides newer Google SDK behavior of panning map
// upon marker tap (unofficial workaround)
map.cameraMoveOnMarkerTap = true;
// customInfoWindow: if set, will not show infowindow for marker tap, you need to do so on your side
// use the mapX and mapY parameters of the various events to figure out
// where to display your custom window. May be mutual exclusive with cameraMoveOnMarkerTap
//
// second option : let the module take care of infoWindow management, you only provide the view
// see the custom info window section for more details
map.customInfoWindow = false;
/* If you use the Google Maps SDK for iOS in your application, you must include the attribution text as part of a legal notices section in your application. Including legal notices as an independent menu item, or as part of an "About" menu item, is recommended. */
alert (googlemaps.openSourceLicenseInfo);
// current map zoom
alert(map.zoom);
// current map region
alert(map.currentRegion);
// current map type (normal, satellite, terrain, hybrid, none)
alert(map.mapType);
// current selected Marker
alert(map.selectedMarker);
map.animateToViewingAngle(45);
map.animateToZoom(15);
map.animateToBearing(30);
map.animateToLocation({latitude:31.066158,
longitude:34.87781});
map.animateToCameraPosition ({
latitude:25.066158,
longitude:31.77781900000002,
zoom:6,
bearing:80,
viewingAngle:70});
// zooms in, in 1 increments
map.zoomIn();
// zooms out, in 1 increments
map.zoomOut();
// zoomTo X zoom level
map.zoomTo(3);
// set the camera to match location, animation is disabled
map.setTarget({
latitude:52.444366455078125,
longitude:-0.40848709344863892,
zoom:14}); // zoom is optional
// fit map to show Paris : NE = North East corner, SW = South West corner
map.fitBounds({
NElatitude:48.898581,
NElongitude:2.2649,
SWlatitude:48.815907,
SWlongitude:2.416306,
padding:0});
// Shifts the center of the view by the specified number
// of points in the x and y directions.
// X grows to the right, Y grows down.
map.scrollBy(50,300);
// create new markers
var marker = googlemaps.createMarker({
title: "MyMarker",
snippet: "My snippet",
tintColor: "blue", // mutually exclusive with icon property
userData: 123,
location: {latitude:35.9348534, longitude:38.9823748923}
});
var london = googlemaps.createMarker({
title: "London",
snippet: "My snippet",
animated: true,
tappable: true, // redundant, defaults to true
userData: btn, // pass anthing, including a Ti object
location: {latitude:35.9348534, longitude:38.9823748923},
icon: "icon.png" // loaded from your project resources folder
});
var london2 = googlemaps.createMarker({
title: "Custom",
snippet: "Icon",
animated: true,
iconView: customIcon, // pass your own custom view as Marker icon
location: {latitude:51.69614700317383, longitude:-0.2597615075111389},
zIndex: 1,
rotation: 90,
draggable: true,
flat: true,
groundAnchor: {x:0.4, y:0.2}
});
// access marker specific properties
alert (marker.userData);
// get all marker properties
alert(marker.data);
// add marker to map
map.addMarker(marker);
// select a marker programmatically (simulate tap)
map.selectMarker(marker);
// unselect marker
map.selectMarker(null);
// remove one marker
map.removeMarker(marker);
// clear map from all markers
map.clear();
// path data, each two numbers are X,Y coordinate (each number pair)
// make sure you enter an even number of items
var pathData = [51.14366455078125, -0.20148709344863892, 30.33434, 21.4095095];
// first method
var polyline = googlemaps.createPolyline({
path:pathData,
color:"yellow",
width:10
});
// second method - inline path data
var polyline = googlemaps.createPolyline({
path:[51.14366455078125, -0.20148709344863892, 30.33434, 21.4095095],
color:"yellow",
width:10
});
// add to map
map.addPolyline(polyline);
// remove from map
map.removePolyline(polyline);
var circle = googlemaps.createCircle({
radius:10000,
location: {latitude:51.43580627441406, longitude:-0.14256912469863892},
color:"black",
fillColor:"yellow",
width:2
});
// add to map
map.addCircle(circle);
// remove from map
map.removeCircle(circle);
// path data, each two numbers are X,Y coordinate (each number pair)
// make sure you enter an even number of items
var pathData = [51.14366455078125, -0.20148709344863892, 30.33434, 21.4095095];
// first method
var polygon = googlemaps.createPolygon({
path: newYorkPath,
title:"New York State",
color:"black",
fillColor:"blue",
width:2,
tappable:true,
});
// add to map
map.addPolygon(polygon);
// remove from map
map.removePolygon(polygon);
/**
* Called after a tap gesture at a particular coordinate, but only if a marker
* was not tapped. This is called before deselecting any currently selected
* marker (the implicit action for tapping on the map).
*/
map.addEventListener('tapAtCoordinate',function(e){
alert(e);
});
/**
* Called after the camera position has changed. During an animation, this
* delegate might not be notified of intermediate camera positions. However, it
* will always be called eventually with the final position of an the animation.
*/
map.addEventListener('changeCameraPosition',function(e){
Ti.API.info("map event : changeCameraPosition =>" + JSON.stringify(e));
});
/**
* Called after a long-press gesture at a particular coordinate.
*/
map.addEventListener('longPressAtCoordinate',function(e){
Ti.API.info("map event : longPressAtCoordinate =>" + JSON.stringify(e));
});
/**
* Called after a marker has been tapped.
*
*/
map.addEventListener('tapMarker',function(e){
Ti.API.info("map event : tapMarker =>" + JSON.stringify(e));
// check if this is a tap on a specific marker
if (e.marker == london)
alert ("London was tapped");
});
/**
* Called after a marker's info window has been tapped.
*/
map.addEventListener('tapInfoWindowOfMarker',function(e){
Ti.API.info("map event : tapInfoWindowOfMarker =>" + JSON.stringify(e));
});
/**
* Called repeatedly when the marker is actively being dragged
*/
map.addEventListener('draggingMarker',function(e){
Ti.API.info("map event : draggingMarker =>" + JSON.stringify(e));
});
/**
* Called once when the marker dragging starts
*/
map.addEventListener('dragMarkerBegin',function(e){
Ti.API.info("map event : dragMarkerBegin =>" + JSON.stringify(e));
});
/**
* Called once when the marker dragging ends
*/
map.addEventListener('dragMarkerEnd',function(e){
Ti.API.info("map event : dragMarkerEnd =>" + JSON.stringify(e));
});
/**
* Called after an overlay has been tapped.
* This method is not called for taps on markers.
*/
map.addEventListener('tapOverlay',function(e){
Ti.API.info("map event : tapOverlay =>" + JSON.stringify(e));
if ( (e.overlayType == "polygon") && (e.title == "New York State") )
Ti.API.info("Polygon tapped : " + e.title);
});
// we still want the module to handle it
map.customInfoWindow = false;
var infoWindow;
var infoWindowLabel = null;
// pre-create the view to hold the
// custom info window
infoWindow = createInfoWindow();
function createInfoWindow() {
infoWindow = null;
infoWindow = Ti.UI.createView({
width: 120,
height: 80,
backgroundColor:"red",
});
infoWindowLabel = Ti.UI.createLabel({
color: "white",
text: "Custom",
width: 100,
height: 80,
top:0,
font:{fontSize:20, fontWeight:'bold'},
textAlign:'center'
});
infoWindow.add(infoWindowLabel);
return infoWindow;
}
// add your own callback to the module
// the callback will be called every time
// the user taps on a marker, if this callback
// is not defined, the default infoWindow will
// be shown
// this is EXPERIMENTAL and thus has limitations:
//
// you can't create views or do any fancy UI
// stuff inside the callback other than update
// your pre-created view. this is due to some
// weird inter-thread issues between Titanium
// and iOS UI. if you do, your app will crash
//
// also, after the callback a 'tapMarker' event
// will fire, if you need to handle it as well
// make sure not to do any blocking UI stuff, such
// as alert('hi') in the event handler or it might
// make the map unresponsive.
map.InfoWindowCallback = function (e) {
if (e.marker == marker) {
infoWindow.backgroundColor = "red";
infoWindowLabel.text = "Marker 1";
} else if (e.marker == marker2) {
infoWindow.backgroundColor = "magenta";
infoWindowLabel.text = "Marker 2";
} else if (e.marker == marker3) {
infoWindow.backgroundColor = "blue";
infoWindowLabel.text = "Marker 3";
}
return infoWindow;
};
license is required per seat, licenses are for current and all future updates of the same version (e.g. 1.1, 1.2, 1.3), next versions of the module (e.g. 2.0,3.0) might be sold seperatly. see LICENSE file in this module for exact details
if your output looks like this:
[ERROR] : ** BUILD FAILED **
[ERROR] : The following build commands failed:
[ERROR] : Ld build/Debug-iphonesimulator/users\ myApp.app/one\ Two normal i386 [ERROR] : (1 failure
this means that the module is not setup correctly in your environment, review the above instructions, specifically the module.xcconfig issue and try again. for more detailed info, open your project build folder and find the .xcode project file, open it in xcode and try to run it, see the log which usually will be detailed and state "Framework not found"
View this PDF for more clarification : https://dl.dropboxusercontent.com/u/172026/GoogleMapsSDKNotFound.pdf
if your output looks like this:
[INFO] : [GoogleMapsModule] Google Maps => New map instance
[ERROR] : The application has crashed with an uncaught exception 'NSInvalidArgumentException'.
[ERROR] : Reason: [ERROR] : *** -[NSBundle initWithURL:]: nil URL argument
[ERROR] : Stack trace: [ERROR] : 0 CoreFoundation 0x04fa15c8 __exceptionPreprocess 152
[ERROR] : 1 libobjc.A.dylib 0x04b428b6 objc_exception_throw 44
[ERROR] : 2 CoreFoundation 0x04fa13bb
[NSException raise:format:] 139
[ERROR] : 3 Foundation 0x023885ce -[NSBundle initWithURL:] 95
[ERROR] : 4 Foundation 0x02388557 [NSBundle bundleWithURL:] 67
One of Google SDK's cryptic error messages. This means that the GoogleFramework.bundle file is not found by the app at runtime, or it means that the version of the GoogleFramework.bundle file was not updated to the latest version when you updated Google SDK to the latest version, these two should always be of the same version.
[INFO] : 2013-10-09 16:01:19.633 Test test[18227:a0b] ClientParametersRequest failed, 0 attempts remaining (0 vs 5). Error Domain=com.google.HTTPStatus Code=400 "The operation couldnt be completed. (com.google.HTTPStatus error 400.)" UserInfo=0xdd650c0 {data=<CFData 0xdd65ad0 [0x50ecec8]>{length = 145, capacity = 256, bytes = 0x3c48544d4c3e0a3c484541443e0a3c54 ... 3c2f48544d4c3e0a}}
[INFO] : 2013-10-09 16:01:19.634 Test test[18227:a0b] Google Maps SDK for iOS cannot connect or validate APIKey: Error Domain=com.google.HTTPStatus Code=400 "The operation couldnt be completed. (com.google.HTTPStatus error 400.)" UserInfo=0xdd650c0 {data=<CFData 0xdd65ad0 [0x50ecec8]>{length = 145, capacity = 256, bytes = 0x3c48544d4c3e0a3c484541443e0a3c54 ... 3c2f48544d4c3e0a}}
[INFO] : 2013-10-09 16:01:19.634 Test test[18227:a0b]
Your key may be invalid for your bundle ID: my.test.app Your Google API key is invalid, or is not correctly paired with your app bundle ID or is not yet activated on Google servers (sometimes takes a few hours, maybe more)
<Error>: CGContextDrawImage: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update.
Your GoogleMaps.framework and GoogleMaps.bundle are not of the same version, you need to update both at the same time you download a new version of the Google Maps SDK for iOS
please send your questions and requests to canufind1@gmail.com
Please don't post technical issues on the Module page @ marketplace.appcelerator.com
if you feel you found a possible bug in the module please include with your bug report a sample project that illustrates the problem, a screenshot of the bug is always helpful.
Support for Google SDK v 1.8.1
Support for Titanium SDK 3.3.0.GA
Support for Google SDK v 1.6.1
Added myLocationButton, compassButton settings
Added map.zoom, map.currentRegion, map.mapType, map.selectedMarker getters
Experimental support for built-in custom info windows
Added marker property: flat (skews with map perspective)
Added marker property: rotation (beta note: google seems to have a tap event bug)
Added marker property: draggable
Added events: draggingMarker, dragMarkerBegin, dragMarkerEnd
Support for Google SDK v 1.5.0
Support for iOS7
Support for Titanium SDK 3.1.3.GA
Added support for Titanium Alloy projects
Added userData to the tapInfoWindowOfMarker event
Added Alloy example app in the more_examples folder
Added Marker properties: zIndex, groundAnchor
Bug fixes
Support for Google SDK v 1.3.1
Custom view markers are now supported via the iconView property
Added animated, tintColor, tappable options to markers
Added tappable support and title property to circle overlays
Support for Camera actions including fitBounds
Added cameraMoveOnMarkerTap setting to override map pan on marker tap
3D Flyby demo added to the sample app.js
Better memory management
Added another example app (window-scenarios.js) to show memory best practices
Support for Google SDK v 1.2.2.3031
Added Polygon,Circle support
Added tapOverlay event
Added selectMarker method
Support for Google SDK v 1.1.2.2533
Added Polyline support
Added visibleRegion reporting on the "changeCameraPosition" event
Initial release