├── .gitignore ├── CONTRIBUTING ├── Docs └── html │ ├── Classes │ ├── GoogleDirectionsDefinition.html │ ├── GoogleDirectionsWaypoint.html │ ├── GoogleMapDefinition.html │ ├── GoogleStreetViewDefinition.html │ └── OpenInGoogleMapsController.html │ ├── Constants │ ├── GoogleMapsFallback.html │ ├── GoogleMapsTravelMode.html │ └── GoogleMapsViewOptions.html │ ├── css │ ├── styles.css │ └── stylesPrint.css │ ├── hierarchy.html │ ├── img │ ├── button_bar_background.png │ ├── disclosure.png │ ├── disclosure_open.png │ ├── library_background.png │ └── title_background.png │ └── index.html ├── LICENSE ├── OpenInGoogleMaps.podspec ├── OpenInGoogleMapsController.h ├── OpenInGoogleMapsController.m ├── OpenInGoogleMapsSample ├── OpenInGoogleMapsSample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── OpenInGoogleMapsSample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── Main_iPhone.storyboard │ ├── Enums.h │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ ├── 2xLaunch.png │ │ │ ├── Contents.json │ │ │ └── Retina4Launch.png │ ├── MapRequestModel.h │ ├── MapRequestModel.m │ ├── OpenInGoogleMapsSample-Info.plist │ ├── OpenInGoogleMapsSample-Prefix.pch │ ├── PickLocationViewController.h │ ├── PickLocationViewController.m │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── OpenInGoogleMapsSampleTests │ ├── OpenInGoogleMapsSampleTests-Info.plist │ ├── OpenInGoogleMapsSampleTests.m │ └── en.lproj │ └── InfoPlist.strings └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | .idea/ 20 | 21 | # OSX 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) 6 | (CLA), which you can do online. The CLA is necessary mainly because you own the 7 | copyright to your changes, even after your contribution becomes part of our 8 | codebase, so we need your permission to use and distribute your code. We also 9 | need to be sure of various other things — for instance that you'll tell us if you 10 | know that your code infringes on other people's patents. You don't have to sign 11 | the CLA until after you've submitted your code for review and a member has 12 | approved it, but you must do it before we can put your code into our codebase. 13 | Before you start working on a larger contribution, you should get in touch with 14 | us first through the issue tracker with your idea so that we can help out and 15 | possibly guide you. Coordinating up front makes it much easier to avoid 16 | frustration later on. 17 | 18 | ### Code reviews 19 | All submissions, including submissions by project members, require review. We 20 | use Github pull requests for this purpose. 21 | 22 | ### The small print 23 | Contributions made by corporations are covered by a different agreement than 24 | the one above, the Software Grant and Corporate Contributor License Agreement. -------------------------------------------------------------------------------- /Docs/html/Classes/GoogleDirectionsDefinition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GoogleDirectionsDefinition Class Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

OpenInGoogleMaps

15 | Google 16 |
17 | 18 | 21 | 57 |
58 | 91 |
92 |
93 | 94 | 100 | 105 |
106 | 107 |
108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
Inherits fromNSObject
Declared inOpenInGoogleMapsController.h
116 | 117 | 118 | 119 | 120 |
121 | 122 |

Overview

123 |

Helper class used to define two waypoints from which to create a set of directions in Google 124 | Maps. Either the starting point or the destination must be non-nil in order to produce a 125 | valid request.

126 |
127 | 128 | 129 | 130 | 131 | 132 |
133 | 134 |

Tasks

135 | 136 | 137 | 138 | 139 | 140 | 161 | 162 |
163 | 164 | 165 | 166 | 167 | 168 |
169 | 170 |

Properties

171 | 172 |
173 | 174 |

destinationPoint

175 | 176 | 177 | 178 |
179 |

Destination. If this is set to nil, we will end at the user’s current location.

180 |
181 | 182 | 183 | 184 |
@property (nonatomic, strong) GoogleDirectionsWaypoint *destinationPoint
185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 |
199 |

See Also

200 | 205 |
206 | 207 | 208 | 209 |
210 |

Declared In

211 | OpenInGoogleMapsController.h
212 |
213 | 214 | 215 |
216 | 217 |
218 | 219 |

startingPoint

220 | 221 | 222 | 223 |
224 |

Starting point. If this is set to nil, we will start at the user’s current location.

225 |
226 | 227 | 228 | 229 |
@property (nonatomic, strong) GoogleDirectionsWaypoint *startingPoint
230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 |
244 |

See Also

245 | 250 |
251 | 252 | 253 | 254 |
255 |

Declared In

256 | OpenInGoogleMapsController.h
257 |
258 | 259 | 260 |
261 | 262 |
263 | 264 |

travelMode

265 | 266 | 267 | 268 |
269 |

Method of transportation for which to provide directions. If the application opened does not 270 | support this travel type, it will fall back to providing driving directions.

271 |
272 | 273 | 274 | 275 |
@property (nonatomic, assign) GoogleMapsTravelMode travelMode
276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 |
290 |

See Also

291 | 296 |
297 | 298 | 299 | 300 |
301 |

Declared In

302 | OpenInGoogleMapsController.h
303 |
304 | 305 | 306 |
307 | 308 |
309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 |
321 | 327 | 336 |
337 |
338 | 429 | 430 | -------------------------------------------------------------------------------- /Docs/html/Classes/GoogleDirectionsWaypoint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GoogleDirectionsWaypoint Class Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

OpenInGoogleMaps

15 | Google 16 |
17 | 18 | 21 | 62 |
63 | 102 |
103 |
104 | 105 | 111 | 116 |
117 | 118 |
119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 |
Inherits fromNSObject
Declared inOpenInGoogleMapsController.h
127 | 128 | 129 | 130 | 131 |
132 | 133 |

Overview

134 |

A class used to define a waypoint for directions.

135 |
136 | 137 | 138 | 139 | 140 | 141 |
142 | 143 |

Tasks

144 | 145 | 146 | 147 | 148 | 149 | 176 | 177 |
178 | 179 | 180 | 181 | 182 | 183 |
184 | 185 |

Properties

186 | 187 |
188 | 189 |

location

190 | 191 | 192 | 193 |
194 |

Lat/long of start or end location. If this is set and not equal to 195 | kCLLocationCoordinate2DInvalid, it takes precedence over queryString.

196 |
197 | 198 | 199 | 200 |
@property (nonatomic, assign) CLLocationCoordinate2D location
201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 |
217 |

Declared In

218 | OpenInGoogleMapsController.h
219 |
220 | 221 | 222 |
223 | 224 |
225 | 226 |

queryString

227 | 228 | 229 | 230 |
231 |

Query string to use to determine this waypoint. For best results, use unambiguous strings 232 | such as addresses (“355 Main Street, Cambridge, MA”) or business search queries with unique 233 | results (“The Exploratorium, San Francisco CA”).

234 |
235 | 236 | 237 | 238 |
@property (nonatomic, copy) NSString *queryString
239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 |
255 |

Declared In

256 | OpenInGoogleMapsController.h
257 |
258 | 259 | 260 |
261 | 262 |
263 | 264 | 265 | 266 |
267 | 268 |

Class Methods

269 | 270 |
271 | 272 |

waypointWithLocation:

273 | 274 | 275 | 276 |
277 |

Class helper method to create a waypoint with a coordinate location.

278 |
279 | 280 | 281 | 282 |
+ (instancetype)waypointWithLocation:(CLLocationCoordinate2D)location
283 | 284 | 285 | 286 |
287 |

Parameters

288 | 289 |
290 |
location
291 |

A coodinate in lat/long for the waypoint.

292 |
293 | 294 |
295 | 296 | 297 | 298 |
299 |

Return Value

300 |

A waypoint to be used in a GoogleDirectionsDefinition object.

301 |
302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 |
314 |

Declared In

315 | OpenInGoogleMapsController.h
316 |
317 | 318 | 319 |
320 | 321 |
322 | 323 |

waypointWithQuery:

324 | 325 | 326 | 327 |
328 |

Class helper method to create a waypoint with a search query.

329 |
330 | 331 | 332 | 333 |
+ (instancetype)waypointWithQuery:(NSString *)queryString
334 | 335 | 336 | 337 |
338 |

Parameters

339 | 340 |
341 |
queryString
342 |

Query string for the waypoint.

343 |
344 | 345 |
346 | 347 | 348 | 349 |
350 |

Return Value

351 |

A waypoint to be used in a GoogleDirectionsDefinition object.

352 |
353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 |
365 |

Declared In

366 | OpenInGoogleMapsController.h
367 |
368 | 369 | 370 |
371 | 372 |
373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 |
383 | 389 | 398 |
399 |
400 | 491 | 492 | -------------------------------------------------------------------------------- /Docs/html/Classes/GoogleMapDefinition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GoogleMapDefinition Class Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

OpenInGoogleMaps

15 | Google 16 |
17 | 18 | 21 | 59 |
60 | 95 |
96 |
97 | 98 | 104 | 109 |
110 | 111 |
112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
Inherits fromNSObject
Declared inOpenInGoogleMapsController.h
120 | 121 | 122 | 123 | 124 |
125 | 126 |

Overview

127 |

Helper class used to define a map to be opened in Google Maps. Note that there’s a good chance 128 | some of these properties will be nil, but either the queryString or the center property 129 | should be set.

130 |
131 | 132 | 133 | 134 | 135 | 136 |
137 | 138 |

Tasks

139 | 140 | 141 | 142 | 143 | 144 | 171 | 172 |
173 | 174 | 175 | 176 | 177 | 178 |
179 | 180 |

Properties

181 | 182 |
183 | 184 |

center

185 | 186 | 187 | 188 |
189 |

Location in lat/long. If both this and the queryString are specified, this will be used as a 190 | center for the search. 191 | To clear this value, set it to kCLLocationCoordinate2DInvalid.

192 |
193 | 194 | 195 | 196 |
@property (nonatomic, assign) CLLocationCoordinate2D center
197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 |
213 |

Declared In

214 | OpenInGoogleMapsController.h
215 |
216 | 217 | 218 |
219 | 220 |
221 | 222 |

queryString

223 | 224 | 225 | 226 |
227 |

A query string which, if set, will be used to search for a place by name.

228 |
229 | 230 | 231 | 232 |
@property (nonatomic, copy) NSString *queryString
233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 |
249 |

Declared In

250 | OpenInGoogleMapsController.h
251 |
252 | 253 | 254 |
255 | 256 |
257 | 258 |

viewOptions

259 | 260 | 261 | 262 |
263 |

Bitwise ORs of different viewing options to display in the Google Maps application.

264 |
265 | 266 | 267 | 268 |
@property (nonatomic, assign) GoogleMapsViewOptions viewOptions
269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 |
283 |

See Also

284 | 289 |
290 | 291 | 292 | 293 |
294 |

Declared In

295 | OpenInGoogleMapsController.h
296 |
297 | 298 | 299 |
300 | 301 |
302 | 303 |

zoomLevel

304 | 305 | 306 | 307 |
308 |

Zoom level. Currently, Google Maps clamps this value from 0.0 (to show the whole Earth) to 21.0.

309 |
310 | 311 | 312 | 313 |
@property (nonatomic, assign) float zoomLevel
314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 |
330 |

Declared In

331 | OpenInGoogleMapsController.h
332 |
333 | 334 | 335 |
336 | 337 |
338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 |
350 | 356 | 365 |
366 |
367 | 458 | 459 | -------------------------------------------------------------------------------- /Docs/html/Classes/GoogleStreetViewDefinition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GoogleStreetViewDefinition Class Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

OpenInGoogleMaps

15 | Google 16 |
17 | 18 | 21 | 53 |
54 | 83 |
84 |
85 | 86 | 92 | 97 |
98 | 99 |
100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
Inherits fromNSObject
Declared inOpenInGoogleMapsController.h
108 | 109 | 110 | 111 | 112 |
113 | 114 |

Overview

115 |

Helper class used to define a Street View location to be opened in Google Maps, 116 | Currently, this class is a subset of GoogleMapDefinition, but we’re keeping these two classes 117 | separate in case they diverge in the future.

118 |
119 | 120 | 121 | 122 | 123 | 124 |
125 | 126 |

Tasks

127 | 128 | 129 | 130 | 131 | 132 |
    133 |
  • 134 | 135 |   center 136 | 137 | property 138 | 139 |
  • 140 |
141 | 142 |
143 | 144 | 145 | 146 | 147 | 148 |
149 | 150 |

Properties

151 | 152 |
153 | 154 |

center

155 | 156 | 157 | 158 |
159 |

Location in lat/long. This is currently the only way you can define a Street View location.

160 |
161 | 162 | 163 | 164 |
@property (nonatomic, assign) CLLocationCoordinate2D center
165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 |
181 |

Declared In

182 | OpenInGoogleMapsController.h
183 |
184 | 185 | 186 |
187 | 188 |
189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 |
201 | 207 | 216 |
217 |
218 | 309 | 310 | -------------------------------------------------------------------------------- /Docs/html/Constants/GoogleMapsFallback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GoogleMapsFallback Constants Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

OpenInGoogleMaps

15 | Google 16 |
17 | 18 | 21 | 36 |
37 | 46 |
47 |
48 | 49 | 55 | 60 |
61 | 62 |
63 | 64 | 65 | 66 | 67 |
Declared inOpenInGoogleMapsController.h
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |

GoogleMapsFallback

78 | 79 | 80 |
81 |

Fallback strategies for what to show if the user doesn’t have Google Maps installed.

82 |
83 | 84 | 85 |
86 | 87 | 88 |

Definition

89 | typedef NS_ENUM(NSInteger, GoogleMapsFallback ) {
90 | 91 |    kGoogleMapsFallbackNone,
92 | 93 |    kGoogleMapsFallbackAppleMaps,
94 | 95 |    kGoogleMapsFallbackChromeThenSafari,
96 | 97 |    kGoogleMapsFallbackChromeThenAppleMaps,
98 | 99 |    kGoogleMapsFallbackSafari,
100 | 101 | };
102 | 103 |
104 | 105 |
106 |

Constants

107 |
108 | 109 |
kGoogleMapsFallbackNone
110 |
111 | 112 | 113 |

Do nothing else, and return NO. This is the default option.

114 | 115 | 116 | 117 | 118 | 119 | 120 |

121 | Declared In OpenInGoogleMapsController.h. 122 |

123 | 124 |
125 | 126 |
kGoogleMapsFallbackAppleMaps
127 |
128 | 129 | 130 |

Show the map in Apple’s Maps app instead. Choose this option if you’d prefer to use a native 131 | app at all times.

132 | 133 | 134 | 135 | 136 | 137 | 138 |

139 | Declared In OpenInGoogleMapsController.h. 140 |

141 | 142 |
143 | 144 |
kGoogleMapsFallbackChromeThenSafari
145 |
146 | 147 | 148 |

Show the map in Chrome, if available. Otherwise display the map in Google Maps within Safari. 149 | Choose this option if it’s important that the map you view is based on Google’s map data.

150 | 151 | 152 | 153 | 154 | 155 | 156 |

157 | Declared In OpenInGoogleMapsController.h. 158 |

159 | 160 |
161 | 162 |
kGoogleMapsFallbackChromeThenAppleMaps
163 |
164 | 165 | 166 |

Show the map in Chrome, if available. Otherwise display the map in Apple’s Maps app. Choose 167 | this option if you’d prefer apps that can point back to your application using the 168 | x-callback-url standard.

169 | 170 | 171 | 172 | 173 | 174 | 175 |

176 | Declared In OpenInGoogleMapsController.h. 177 |

178 | 179 |
180 | 181 |
kGoogleMapsFallbackSafari
182 |
183 | 184 | 185 |

Show the map in Google Maps in the Safari browser.

186 | 187 | 188 | 189 | 190 | 191 | 192 |

193 | Declared In OpenInGoogleMapsController.h. 194 |

195 | 196 |
197 | 198 |
199 |
200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 |
209 |

Declared In

210 | OpenInGoogleMapsController.h
211 |
212 | 213 | 214 | 215 | 216 | 217 | 218 |
219 | 225 | 234 |
235 |
236 | 327 | 328 | -------------------------------------------------------------------------------- /Docs/html/Constants/GoogleMapsTravelMode.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GoogleMapsTravelMode Constants Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

OpenInGoogleMaps

15 | Google 16 |
17 | 18 | 21 | 36 |
37 | 46 |
47 |
48 | 49 | 55 | 60 |
61 | 62 |
63 | 64 | 65 | 66 | 67 |
Declared inOpenInGoogleMapsController.h
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |

GoogleMapsTravelMode

78 | 79 | 80 |
81 |

Defines the method by which the user would like to travel when creating directions.

82 |
83 | 84 | 85 |
86 | 87 | 88 |

Definition

89 | typedef NS_ENUM(NSInteger, GoogleMapsTravelMode ) {
90 | 91 |    kGoogleMapsTravelModeDriving = 1,
92 | 93 |    kGoogleMapsTravelModeTransit,
94 | 95 |    kGoogleMapsTravelModeBiking,
96 | 97 |    kGoogleMapsTravelModeWalking,
98 | 99 | };
100 | 101 |
102 | 103 |
104 |

Constants

105 |
106 | 107 |
kGoogleMapsTravelModeDriving
108 |
109 | 110 | 111 |

Driving

112 | 113 | 114 | 115 | 116 | 117 | 118 |

119 | Declared In OpenInGoogleMapsController.h. 120 |

121 | 122 |
123 | 124 |
kGoogleMapsTravelModeTransit
125 |
126 | 127 | 128 |

Public transit

129 | 130 | 131 | 132 | 133 | 134 | 135 |

136 | Declared In OpenInGoogleMapsController.h. 137 |

138 | 139 |
140 | 141 |
kGoogleMapsTravelModeBiking
142 |
143 | 144 | 145 |

Biking

146 | 147 | 148 | 149 | 150 | 151 | 152 |

153 | Declared In OpenInGoogleMapsController.h. 154 |

155 | 156 |
157 | 158 |
kGoogleMapsTravelModeWalking
159 |
160 | 161 | 162 |

Walking

163 | 164 | 165 | 166 | 167 | 168 | 169 |

170 | Declared In OpenInGoogleMapsController.h. 171 |

172 | 173 |
174 | 175 |
176 |
177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 |
186 |

Declared In

187 | OpenInGoogleMapsController.h
188 |
189 | 190 | 191 | 192 | 193 | 194 | 195 |
196 | 202 | 211 |
212 |
213 | 304 | 305 | -------------------------------------------------------------------------------- /Docs/html/Constants/GoogleMapsViewOptions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GoogleMapsViewOptions Constants Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

OpenInGoogleMaps

15 | Google 16 |
17 | 18 | 21 | 36 |
37 | 46 |
47 |
48 | 49 | 55 | 60 |
61 | 62 |
63 | 64 | 65 | 66 | 67 |
Declared inOpenInGoogleMapsController.h
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |

GoogleMapsViewOptions

78 | 79 | 80 |
81 |

Options for some of the views you can toggle in Google Maps.

82 |
83 | 84 | 85 |
86 | 87 | 88 |

Definition

89 | typedef NS_OPTIONS(NSInteger, GoogleMapsViewOptions ) {
90 | 91 |    kGoogleMapsViewOptionSatellite = 1 < < 0,
92 | 93 |    kGoogleMapsViewOptionTraffic = 1 < < 1,
94 | 95 |    kGoogleMapsViewOptionTransit = 1 < < 2,
96 | 97 | };
98 | 99 |
100 | 101 |
102 |

Constants

103 |
104 | 105 |
kGoogleMapsViewOptionSatellite
106 |
107 | 108 | 109 |

Satellite view.

110 | 111 | 112 | 113 | 114 | 115 | 116 |

117 | Declared In OpenInGoogleMapsController.h. 118 |

119 | 120 |
121 | 122 |
kGoogleMapsViewOptionTraffic
123 |
124 | 125 | 126 |

Show traffic information.

127 | 128 | 129 | 130 | 131 | 132 | 133 |

134 | Declared In OpenInGoogleMapsController.h. 135 |

136 | 137 |
138 | 139 |
kGoogleMapsViewOptionTransit
140 |
141 | 142 | 143 |

Show public transit routes.

144 | 145 | 146 | 147 | 148 | 149 | 150 |

151 | Declared In OpenInGoogleMapsController.h. 152 |

153 | 154 |
155 | 156 |
157 |
158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 |
167 |

Declared In

168 | OpenInGoogleMapsController.h
169 |
170 | 171 | 172 | 173 | 174 | 175 | 176 |
177 | 183 | 192 |
193 |
194 | 285 | 286 | -------------------------------------------------------------------------------- /Docs/html/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 3 | font-size: 13px; 4 | } 5 | 6 | code { 7 | font-family: Courier, Consolas, monospace; 8 | font-size: 13px; 9 | color: #666; 10 | } 11 | 12 | pre { 13 | font-family: Courier, Consolas, monospace; 14 | font-size: 13px; 15 | line-height: 18px; 16 | tab-interval: 0.5em; 17 | border: 1px solid #C7CFD5; 18 | background-color: #F1F5F9; 19 | color: #666; 20 | padding: 0.3em 1em; 21 | } 22 | 23 | ul { 24 | list-style-type: square; 25 | } 26 | 27 | li { 28 | margin-bottom: 10px; 29 | } 30 | 31 | a, a code { 32 | text-decoration: none; 33 | color: #36C; 34 | } 35 | 36 | a:hover, a:hover code { 37 | text-decoration: underline; 38 | color: #36C; 39 | } 40 | 41 | h2 { 42 | border-bottom: 1px solid #8391A8; 43 | color: #3C4C6C; 44 | font-size: 187%; 45 | font-weight: normal; 46 | margin-top: 1.75em; 47 | padding-bottom: 2px; 48 | } 49 | 50 | table { 51 | margin-bottom: 4em; 52 | border-collapse:collapse; 53 | vertical-align: middle; 54 | } 55 | 56 | td { 57 | border: 1px solid #9BB3CD; 58 | padding: .667em; 59 | font-size: 100%; 60 | } 61 | 62 | th { 63 | border: 1px solid #9BB3CD; 64 | padding: .3em .667em .3em .667em; 65 | background: #93A5BB; 66 | font-size: 103%; 67 | font-weight: bold; 68 | color: white; 69 | text-align: left; 70 | } 71 | 72 | /* @group Common page elements */ 73 | 74 | #top_header { 75 | height: 91px; 76 | left: 0; 77 | min-width: 598px; 78 | position: absolute; 79 | right: 0; 80 | top: 0; 81 | z-index: 900; 82 | } 83 | 84 | #footer { 85 | clear: both; 86 | padding-top: 20px; 87 | text-align: center; 88 | } 89 | 90 | #contents, #overview_contents { 91 | -webkit-overflow-scrolling: touch; 92 | border-top: 1px solid #A9A9A9; 93 | position: absolute; 94 | top: 90px; 95 | left: 0; 96 | right: 0; 97 | bottom: 0; 98 | overflow-x: hidden; 99 | overflow-y: auto; 100 | padding-left: 2em; 101 | padding-right: 2em; 102 | padding-top: 1em; 103 | min-width: 550px; 104 | } 105 | 106 | #contents.isShowingTOC { 107 | left: 230px; 108 | min-width: 320px; 109 | } 110 | 111 | .copyright { 112 | font-size: 12px; 113 | } 114 | 115 | .generator { 116 | font-size: 11px; 117 | } 118 | 119 | .main-navigation ul li { 120 | display: inline; 121 | margin-left: 15px; 122 | list-style: none; 123 | } 124 | 125 | .navigation-top { 126 | clear: both; 127 | float: right; 128 | } 129 | 130 | .navigation-bottom { 131 | clear: both; 132 | float: right; 133 | margin-top: 20px; 134 | margin-bottom: -10px; 135 | } 136 | 137 | .open > .disclosure { 138 | background-image: url("../img/disclosure_open.png"); 139 | } 140 | 141 | .disclosure { 142 | background: url("../img/disclosure.png") no-repeat scroll 0 0; 143 | } 144 | 145 | .disclosure, .nodisclosure { 146 | display: inline-block; 147 | height: 8px; 148 | margin-right: 5px; 149 | position: relative; 150 | width: 9px; 151 | } 152 | 153 | /* @end */ 154 | 155 | /* @group Header */ 156 | 157 | #top_header #library { 158 | background: url("../img/library_background.png") repeat-x 0 0 #485E78; 159 | background-color: #ccc; 160 | height: 35px; 161 | font-size: 115%; 162 | } 163 | 164 | #top_header #library #libraryTitle { 165 | color: #FFFFFF; 166 | margin-left: 15px; 167 | text-shadow: 0 -1px 0 #485E78; 168 | top: 8px; 169 | position: absolute; 170 | } 171 | 172 | #libraryTitle { 173 | left: 0; 174 | } 175 | 176 | #top_header #library #developerHome { 177 | color: #92979E; 178 | right: 15px; 179 | top: 8px; 180 | position: absolute; 181 | } 182 | 183 | #top_header #library a:hover { 184 | text-decoration: none; 185 | } 186 | 187 | #top_header #title { 188 | background: url("../img/title_background.png") repeat-x 0 0 #8A98A9; 189 | border-bottom: 1px solid #757575; 190 | height: 25px; 191 | overflow: hidden; 192 | } 193 | 194 | #top_header h1 { 195 | font-size: 105%; 196 | font-weight: normal; 197 | margin: 0; 198 | padding: 3px 0 2px; 199 | text-align: center; 200 | /*text-shadow: 0 1px 0 #D5D5D5;*/ 201 | white-space: nowrap; 202 | } 203 | 204 | #headerButtons { 205 | background-color: #D8D8D8; 206 | background-image: url("../img/button_bar_background.png"); 207 | border-bottom: 0px solid #EDEDED; 208 | border-top: 0px solid #a8a8a8; 209 | font-size: 8pt; 210 | height: 28px; 211 | left: 0; 212 | list-style: none outside none; 213 | margin: 0; 214 | overflow: hidden; 215 | padding: 0; 216 | position: absolute; 217 | right: 0; 218 | top: 61px; 219 | } 220 | 221 | #headerButtons li { 222 | background-repeat: no-repeat; 223 | display: inline; 224 | margin-top: 0; 225 | margin-bottom: 0; 226 | padding: 0; 227 | } 228 | 229 | #toc_button button { 230 | background-color: #EBEEF1; 231 | border-color: #ACACAC; 232 | border-style: none solid none none; 233 | border-width: 0 1px 0 0; 234 | height: 28px; 235 | margin: 0; 236 | padding-left: 30px; 237 | text-align: left; 238 | width: 230px; 239 | } 240 | 241 | li#jumpto_button { 242 | left: 230px; 243 | margin-left: 0; 244 | position: absolute; 245 | } 246 | 247 | li#jumpto_button select { 248 | height: 22px; 249 | margin: 5px 2px 0 10px; 250 | max-width: 300px; 251 | } 252 | 253 | /* @end */ 254 | 255 | /* @group Table of contents */ 256 | 257 | #tocContainer.isShowingTOC { 258 | border-right: 1px solid #ACACAC; 259 | display: block; 260 | overflow-x: hidden; 261 | overflow-y: auto; 262 | padding: 0; 263 | } 264 | 265 | #tocContainer { 266 | background-color: #EBEEF1; 267 | border-top: 1px solid #ACACAC; 268 | bottom: 0; 269 | display: none; 270 | left: 0; 271 | overflow: hidden; 272 | position: absolute; 273 | top: 90px; 274 | width: 229px; 275 | } 276 | 277 | #tocContainer > ul#toc { 278 | font-size: 11px; 279 | margin: 0; 280 | padding: 12px 0 18px; 281 | width: 209px; 282 | -moz-user-select: none; 283 | -webkit-user-select: none; 284 | user-select: none; 285 | } 286 | 287 | #tocContainer > ul#toc > li { 288 | margin: 0; 289 | padding: 0 0 7px 30px; 290 | text-indent: -15px; 291 | } 292 | 293 | #tocContainer > ul#toc > li > .sectionName a { 294 | color: #000000; 295 | font-weight: bold; 296 | } 297 | 298 | #tocContainer > ul#toc > li > .sectionName a:hover { 299 | text-decoration: none; 300 | } 301 | 302 | #tocContainer > ul#toc li.children > ul { 303 | display: none; 304 | height: 0; 305 | } 306 | 307 | #tocContainer > ul#toc > li > ul { 308 | margin: 0; 309 | padding: 0; 310 | } 311 | 312 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 313 | margin-left: 0; 314 | margin-bottom: 0; 315 | padding-left: 15px; 316 | } 317 | 318 | #tocContainer > ul#toc > li ul { 319 | list-style: none; 320 | margin-right: 0; 321 | padding-right: 0; 322 | } 323 | 324 | #tocContainer > ul#toc li.children.open > ul { 325 | display: block; 326 | height: auto; 327 | margin-left: -15px; 328 | padding-left: 0; 329 | } 330 | 331 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 332 | margin-left: 0; 333 | padding-left: 15px; 334 | } 335 | 336 | #tocContainer li ul li { 337 | margin-top: 0.583em; 338 | overflow: hidden; 339 | text-overflow: ellipsis; 340 | white-space: nowrap; 341 | } 342 | 343 | #tocContainer li ul li span.sectionName { 344 | white-space: normal; 345 | } 346 | 347 | #tocContainer > ul#toc > li > ul > li > .sectionName a { 348 | font-weight: bold; 349 | } 350 | 351 | #tocContainer > ul#toc > li > ul a { 352 | color: #4F4F4F; 353 | } 354 | 355 | /* @end */ 356 | 357 | /* @group Index formatting */ 358 | 359 | .index-title { 360 | font-size: 13px; 361 | font-weight: normal; 362 | } 363 | 364 | .index-column { 365 | float: left; 366 | width: 30%; 367 | min-width: 200px; 368 | font-size: 11px; 369 | } 370 | 371 | .index-column ul { 372 | margin: 8px 0 0 0; 373 | padding: 0; 374 | list-style: none; 375 | } 376 | 377 | .index-column ul li { 378 | margin: 0 0 3px 0; 379 | padding: 0; 380 | } 381 | 382 | .hierarchy-column { 383 | min-width: 400px; 384 | } 385 | 386 | .hierarchy-column ul { 387 | margin: 3px 0 0 15px; 388 | } 389 | 390 | .hierarchy-column ul li { 391 | list-style-type: square; 392 | } 393 | 394 | /* @end */ 395 | 396 | /* @group Common formatting elements */ 397 | 398 | .title { 399 | font-weight: normal; 400 | font-size: 215%; 401 | margin-top:0; 402 | } 403 | 404 | .subtitle { 405 | font-weight: normal; 406 | font-size: 180%; 407 | color: #3C4C6C; 408 | border-bottom: 1px solid #5088C5; 409 | } 410 | 411 | .subsubtitle { 412 | font-weight: normal; 413 | font-size: 145%; 414 | height: 0.7em; 415 | } 416 | 417 | .note { 418 | border: 1px solid #5088C5; 419 | background-color: white; 420 | margin: 1.667em 0 1.75em 0; 421 | padding: 0 .667em .083em .750em; 422 | } 423 | 424 | .warning { 425 | border: 1px solid #5088C5; 426 | background-color: #F0F3F7; 427 | margin-bottom: 0.5em; 428 | padding: 0.3em 0.8em; 429 | } 430 | 431 | .bug { 432 | border: 1px solid #000; 433 | background-color: #ffffcc; 434 | margin-bottom: 0.5em; 435 | padding: 0.3em 0.8em; 436 | } 437 | 438 | .deprecated { 439 | color: #F60425; 440 | } 441 | 442 | /* @end */ 443 | 444 | /* @group Common layout */ 445 | 446 | .section { 447 | margin-top: 3em; 448 | } 449 | 450 | /* @end */ 451 | 452 | /* @group Object specification section */ 453 | 454 | .section-specification { 455 | margin-left: 2.5em; 456 | margin-right: 2.5em; 457 | font-size: 12px; 458 | } 459 | 460 | .section-specification table { 461 | margin-bottom: 0em; 462 | border-top: 1px solid #d6e0e5; 463 | } 464 | 465 | .section-specification td { 466 | vertical-align: top; 467 | border-bottom: 1px solid #d6e0e5; 468 | border-left-width: 0px; 469 | border-right-width: 0px; 470 | border-top-width: 0px; 471 | padding: .6em; 472 | } 473 | 474 | .section-specification .specification-title { 475 | font-weight: bold; 476 | } 477 | 478 | /* @end */ 479 | 480 | /* @group Tasks section */ 481 | 482 | .task-list { 483 | list-style-type: none; 484 | padding-left: 0px; 485 | } 486 | 487 | .task-list li { 488 | margin-bottom: 3px; 489 | } 490 | 491 | .task-item-suffix { 492 | color: #996; 493 | font-size: 12px; 494 | font-style: italic; 495 | margin-left: 0.5em; 496 | } 497 | 498 | span.tooltip span.tooltip { 499 | font-size: 1.0em; 500 | display: none; 501 | padding: 0.3em; 502 | border: 1px solid #aaa; 503 | background-color: #fdfec8; 504 | color: #000; 505 | text-align: left; 506 | } 507 | 508 | span.tooltip:hover span.tooltip { 509 | display: block; 510 | position: absolute; 511 | margin-left: 2em; 512 | } 513 | 514 | /* @end */ 515 | 516 | /* @group Method section */ 517 | 518 | .section-method { 519 | margin-top: 2.3em; 520 | } 521 | 522 | .method-title { 523 | margin-bottom: 1.5em; 524 | } 525 | 526 | .method-subtitle { 527 | margin-top: 0.7em; 528 | margin-bottom: 0.2em; 529 | } 530 | 531 | .method-subsection p { 532 | margin-top: 0.4em; 533 | margin-bottom: 0.8em; 534 | } 535 | 536 | .method-declaration { 537 | margin-top:1.182em; 538 | margin-bottom:.909em; 539 | } 540 | 541 | .method-declaration code { 542 | font:14px Courier, Consolas, monospace; 543 | color:#000; 544 | } 545 | 546 | .declaration { 547 | color: #000; 548 | } 549 | 550 | .termdef { 551 | margin-bottom: 10px; 552 | margin-left: 0px; 553 | margin-right: 0px; 554 | margin-top: 0px; 555 | } 556 | 557 | .termdef dt { 558 | margin: 0; 559 | padding: 0; 560 | } 561 | 562 | .termdef dd { 563 | margin-bottom: 6px; 564 | margin-left: 16px; 565 | margin-right: 0px; 566 | margin-top: 1px; 567 | } 568 | 569 | .termdef dd p { 570 | margin-bottom: 6px; 571 | margin-left: 0px; 572 | margin-right: 0px; 573 | margin-top: -1px; 574 | } 575 | 576 | .argument-def { 577 | margin-top: 0.3em; 578 | margin-bottom: 0.3em; 579 | } 580 | 581 | .argument-def dd { 582 | margin-left: 1.25em; 583 | } 584 | 585 | .see-also-section ul { 586 | list-style-type: none; 587 | padding-left: 0px; 588 | margin-top: 0; 589 | } 590 | 591 | .see-also-section li { 592 | margin-bottom: 3px; 593 | } 594 | 595 | .declared-in-ref { 596 | color: #666; 597 | } 598 | 599 | #tocContainer.hideInXcode { 600 | display: none; 601 | border: 0px solid black; 602 | } 603 | 604 | #top_header.hideInXcode { 605 | display: none; 606 | } 607 | 608 | #contents.hideInXcode { 609 | border: 0px solid black; 610 | top: 0px; 611 | left: 0px; 612 | } 613 | 614 | /* @end */ 615 | 616 | -------------------------------------------------------------------------------- /Docs/html/css/stylesPrint.css: -------------------------------------------------------------------------------- 1 | 2 | header { 3 | display: none; 4 | } 5 | 6 | div.main-navigation, div.navigation-top { 7 | display: none; 8 | } 9 | 10 | div#overview_contents, div#contents.isShowingTOC, div#contents { 11 | overflow: visible; 12 | position: relative; 13 | top: 0px; 14 | border: none; 15 | left: 0; 16 | } 17 | #tocContainer.isShowingTOC { 18 | display: none; 19 | } 20 | nav { 21 | display: none; 22 | } -------------------------------------------------------------------------------- /Docs/html/hierarchy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OpenInGoogleMaps Hierarchy 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

OpenInGoogleMaps

14 | Google 15 |
16 | 17 | 20 | 21 |
22 |
23 |
24 | 27 | 32 |
33 | 34 |
35 |

Class Hierarchy

36 | 37 | 56 | 57 |
58 | 59 | 60 | 61 |
62 | 63 | 64 |

Constant References

65 | 74 | 75 | 76 | 77 |
78 | 79 |
80 | 83 | 93 |
94 |
95 | 96 | -------------------------------------------------------------------------------- /Docs/html/img/button_bar_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/OpenInGoogleMaps-iOS/89de89de8ad5b8512e6cdcf09ab7b8f11e2f91f9/Docs/html/img/button_bar_background.png -------------------------------------------------------------------------------- /Docs/html/img/disclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/OpenInGoogleMaps-iOS/89de89de8ad5b8512e6cdcf09ab7b8f11e2f91f9/Docs/html/img/disclosure.png -------------------------------------------------------------------------------- /Docs/html/img/disclosure_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/OpenInGoogleMaps-iOS/89de89de8ad5b8512e6cdcf09ab7b8f11e2f91f9/Docs/html/img/disclosure_open.png -------------------------------------------------------------------------------- /Docs/html/img/library_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/OpenInGoogleMaps-iOS/89de89de8ad5b8512e6cdcf09ab7b8f11e2f91f9/Docs/html/img/library_background.png -------------------------------------------------------------------------------- /Docs/html/img/title_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/OpenInGoogleMaps-iOS/89de89de8ad5b8512e6cdcf09ab7b8f11e2f91f9/Docs/html/img/title_background.png -------------------------------------------------------------------------------- /Docs/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OpenInGoogleMaps Reference 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

OpenInGoogleMaps

14 | Google 15 |
16 | 17 | 20 | 21 |
22 |
23 |
24 | 27 | 32 |
33 | 34 |
35 | 36 | 37 |

OpenInGoogleMapsController

38 | 39 |

The OpenInGoogleMapsController class is designed to make it easy for an iOS developer to open a map, show a Street View location, or show a set of directions directly in Google Maps. The class supports using the x-callback-URL standard so that you can add a “Back to my app” button directly within Google Maps, and supports a number of fallback strategies, so that you can automatically open the map in another application if the user does not have Google Maps installed.

40 | 41 |

About the Google Maps URL Scheme

42 | 43 |

The OpenInGoogleMapsController class makes use of the Google Maps URL Scheme. If you want to understand how the class works under the hood, we highly recommend reading the URL Scheme documentation first.

44 | 45 |

Installing OpenInGoogleMapsController

46 | 47 |

You can download the OpenInGoogleMapsController class, along with a sample app demonstrating its use, from the OpenInGoogleMaps Github page.

48 | 49 |

To add the class to your Xcode project, simply drag the OpenInGoogleMapsController .m and .h files into Xcode. Make sure you call #import OpenInGoogleMapsController.h where necessary.

50 | 51 |

Running the Sample Application

52 | 53 |

If you would like to try out the sample application, open OpenInGoogleMapsSample.xcodeproj in Xcode. You will probably want to run this on a real device, as the simulator does not have Google Maps installed.

54 | 55 |

Using OpenInGoogleMapsController

56 | 57 |

OpenInGoogleMapsController is a singleton class, which you can reference by calling the sharedInstance class method.

58 | 59 |
[[OpenInGoogleMapsController sharedInstance] <make calls here>]
 60 | 
61 | 62 |

Adding a Callback URL

63 | 64 |

Google Maps and Google Chrome both support the x-callback-URL specification, which allows you to easily add a “Back to my app” button in Google Maps. To add a callback url:

65 | 66 |
    67 |
  1. Within your Xcode project, select your target, and then select Info -> URL Types
  2. 68 |
  3. Add a URLType for your application. This string should be unique for your app. Many developers choose to use their bundle identifier, without the periods (comgooglemyapp, for instance).
  4. 69 |
  5. Set the callbackURL property in your OpenInGoogleMapsController class.

    70 | 71 |
     NSString myURLScheme = @"comexamplemyapp://";
     72 |  NSURL myCallbackURL = [NSURL URLWithString:myURLScheme];
     73 |  [OpenInGoogleMapsController sharedInstance].callbackURL = myCallbackURL;
     74 | 
  6. 75 |
76 | 77 | 78 |

When you open your maps now in Google Maps (or Google Chrome), you should see a button that redirects users back to your app when they’re done viewing the map.

79 | 80 |

You only need to set the callback URL once during the lifetime of your application and it will be used in all future OpenInGoogleMapsController requests.

81 | 82 |

Fallback Strategies

83 | 84 |

If the user does not have Google Maps installed, you can specify a number of fallback strategies for OpenInGoogleMapsController to try by setting the fallbackStrategy property.

85 | 86 |
[OpenInGoogleMapsController sharedInstance].fallbackStrategy =
 87 |     kGoogleMapsFallbackChromeThenAppleMaps;
 88 | 
89 | 90 |

The fallback strategies you can try are as follows:

91 | 92 |
    93 |
  • kGoogleMapsFallbackNone = Do nothing and return NO if the user does not have Google Maps installed. This is the default.
  • 94 |
  • kGoogleMapsFallbackAppleMaps = Open the map with Apple’s Maps app instead.
  • 95 |
  • kGoogleMapsFallbackChromeThenSafari = Open the map with Google Chrome if installed, otherwise 96 | open the map using Google Maps in Safari.
  • 97 |
  • kGoogleMapsFallbackChromeThenAppleMaps = Open the map with Google Chrome if installed, otherwise 98 | use Apple’s Maps app.
  • 99 |
  • kGoogleMapsFallbackSafari = Open the map with Google Maps in Safari instead.
  • 100 |
101 | 102 | 103 |

If you have specified a callback URL, it will also be passed to Google Chrome.

104 | 105 |

You only need to set the fallback strategy once during the lifetime of your application and it will be used in all future OpenInGoogleMapsController requests.

106 | 107 |

Detecting if Google Maps is installed

108 | 109 |

If you want to manually detect if Google Maps is installed, you can use the isGoogleMapsInstalled property.

110 | 111 |
BOOL isGoogleMapsInstalled = [OpenInGoogleMapsController sharedInstance].isGoogleMapsInstalled;
112 | 
113 | 114 |

Opening a map

115 | 116 |

Opening a map requires first creating a GoogleMapDefinition object to define the map you want opened. You can then pass the definition object to the openMap method. This method will return YES if it was able to open the map in some application, and NO if it was unable to open a map, either because you didn’t define anything to search for, or your user does not have Google Maps installed and you did not specify a fallback strategy.

117 | 118 |
GoogleMapDefinition *definition = [[GoogleMapDefinition alloc] init];
119 | // Steps to define the definition.
120 | [[OpenInGoogleMapsController sharedInstance] openMap:definition];
121 | 
122 | 123 |

GoogleMapDefinition

124 | 125 |

The GoogleMapDefinition class includes several properties, some of which may be set to nil:

126 | 127 |
    128 |
  • (NSString *)queryString: A query string which, if set, will be used to search for a place by name.
  • 129 |
  • (CLLocationCoordinate2D) center: Defines the center of the map, in lat/long. If both this and the query string are specified, this will be used as a center for the search. To clear this value, set it to kCLLocationCoordinate2DInvalid. (Setting it to nil will specify a center of 0,0.)
  • 130 |
  • (GoogleMapsViewOptions) viewOptions: A set of bitwise-ORed options that can be set on your map: 131 | 132 |
      133 |
    • kGoogleMapsViewOptionSatellite: Shows a satellite view.
    • 134 |
    • kGoogleMapsViewOptionTraffic: Shows traffic information.
    • 135 |
    • kGoogleMapsViewOptionTransit: Shows transit information.
    • 136 |
    137 |
  • 138 |
  • float zoomLevel: Defines the zoom level of the map. This can currently be any value from 0 to 21.0.
  • 139 |
140 | 141 | 142 |

Here’s an example that opens up a map for “123 Main Street, Anytown, CA” with the “traffic” and “satellite” map layers turned on.

143 | 144 |
GoogleMapDefinition *definition = [[GoogleMapDefinition alloc] init];
145 | definition.queryString = @"123 Main Street, Anytown, CA";
146 | definition.viewOptions = kGoogleMapsViewOptionSatellite | kGoogleMapsViewOptionTraffic;
147 | [[OpenInGoogleMapsController sharedInstance] openMap:definition];
148 | 
149 | 150 |

Opening a street view location

151 | 152 |

Opening a Street View location requires creating a GoogleStreetViewDefinition class to define the location you want to open. You can then pass this definition to the openStreetView method. This method will return YES if it was able to open the Street View request in some application, and NO if it was not, either because you didn’t define a set of coordinates, or your user does not have Google Maps installed and you did not specify a fallback strategy.

153 | 154 |

Note that a YES value does not actually guarantee the coordinates you specified were a valid Street View location.

155 | 156 |

If your fallback strategy involves an app that does not support Street View, the OpenInGoogleMapsController class will open a zoomed-in satellite view on a map instead.

157 | 158 |

Here’s an example that opens up a Street View location near the Taj Mahal.

159 | 160 |
GoogleStreetViewDefinition *definition = [[GoogleStreetViewDefinition alloc] init];
161 | definition.center = CLLocationCoordinate2DMake(27.1724439,78.0420174);
162 | [[OpenInGoogleMapsController sharedInstance] openStreetView:definition];
163 | 
164 | 165 |

GoogleStreetViewDefinition

166 | 167 |

The GoogleStreetViewDefinition class includes one property:

168 | 169 |
    170 |
  • (CLLocationCoordinate2D) center: Defines the Street View location, in lat/long. To clear this value, set it to kCLLocationCoordinate2DInvalid. (Setting it to nil will specify a center of 0,0.)
  • 171 |
172 | 173 | 174 |

Opening directions

175 | 176 |

Opening a set of directions in Google Maps requires creating a GoogleDirectionsDefinition class to define the set of points you want to travel between. You can then pass this definition to the openStreetView method. This method will return YES if it was able to open the set of directions in some application, and NO if it was unable to, either because both your start and end point were empty, or your user does not have Google Maps installed and you did not specify a fallback strategy.

177 | 178 |

Note that a YES value does not guarantee that Google Maps (or the fallback application) was able to find a set of directions between these two points:

179 | 180 |

GoogleDirectionsWaypoint

181 | 182 |

The GoogleDirectionsDefinition class uses the GoogleDirectionsWaypoint class to define its start and end points for a direction request. This class includes these properties:

183 | 184 |
    185 |
  • CLLocationCoordinate2D location: Defines the location as a set of coordinates
  • 186 |
  • NSString *queryString: Defines the location as a query string (such as an address)
  • 187 |
188 | 189 | 190 |

If both of these values are set, the location takes precedence over the query string.

191 | 192 |

The GoogleDirectionsWaypoint class also has two class helper methods: + waypointWithQuery:(NSString *)queryString and + waypointWithLocation:(CLLocationCoordinate2D)location to easily construct waypoints.

193 | 194 |

GoogleDirectionsDefinition

195 | 196 |

The GoogleDirectionsDefinition class includes these properties:

197 | 198 |
    199 |
  • GoogleDirectionsWaypoint *startingPoint: Defines the starting point. If this is set to nil, the directions will start at the user’s current location.
  • 200 |
  • GoogleDirectionsWaypoint *destinationPoint: Defines the destination. If this is set to nil, the directions will end at the user’s current location.
  • 201 |
  • GoogleMapsTravelMode travelMode: Defines how the user will get from the startingPoint to the destinationPoint. Current options are: 202 | 203 |
      204 |
    • kGoogleMapsTravelModeDriving for driving.
    • 205 |
    • kGoogleMapsTravelModeTransit for taking public transportation.
    • 206 |
    • kGoogleMapsTravelModeBiking for biking.
    • 207 |
    • kGoogleMapsTravelModeWalking for walking.
    • 208 |
    209 |
  • 210 |
211 | 212 | 213 |

The following example will help you plan your next burrito-centric road trip:

214 | 215 |
GoogleDirectionsDefinition *definition = [[GoogleDirectionsDefinition alloc] init];
216 | definition.startingPoint = [GoogleDirectionsWaypoint
217 |     waypointWithQuery:@"La Taqueria, 2889 Mission St San Francisco, CA 94110"];
218 | definition.destinationPoint = [GoogleDirectionsWaypoint
219 |     waypointWithQuery:@"Delicious Mexican Eatery, 3314 Fort Blvd, El Paso, TX 79930"];
220 | definition.travelMode = kGoogleMapsTravelModeDriving;
221 | [[OpenInGoogleMapsController sharedInstance] openDirections:definition];
222 | 
223 | 224 |

The following example will give you biking directions from MI6 headquarters to Sherlock Holmes' address:

225 | 226 |
GoogleDirectionsDefinition *definition = [[GoogleDirectionsDefinition alloc] init];
227 | definition.startingPoint = [GoogleDirectionsWaypoint waypointWithLocation:CLLocationCoordinate2DMake(51.487242,-0.124402)];
228 | definition.destinationPoint = [GoogleDirectionsWaypoint
229 |                                waypointWithQuery:@"221B Baker Street, London"];
230 | definition.travelMode = kGoogleMapsTravelModeBiking;
231 | [[OpenInGoogleMapsController sharedInstance] openDirections:definition];
232 | 
233 | 234 |

The following example will give you walking directions from your current location to the North American International Auto Show:

235 | 236 |
GoogleDirectionsDefinition *definition = [[GoogleDirectionsDefinition alloc] init];
237 | definition.startingPoint = nil;
238 | GoogleDirectionsWaypoint *destination = [[GoogleDirectionsWaypoint alloc] init];
239 | destination.queryString = @"1 Washington Blvd, Detroit, MI 48226";
240 | definition.destinationPoint = destination;
241 | definition.travelMode = kGoogleMapsTravelModeWalking;
242 | [[OpenInGoogleMapsController sharedInstance] openDirections:definition];
243 | 
244 | 245 |

Special Thanks

246 | 247 |

Special thanks go out to Ian Barber, Leo Hourvitz, and Sam Thorogood, for thoroughly reviewing this code. Any remaining mistakes are the author’s.

248 | 249 |

Safari is a registered trademark of Apple Inc.

250 | 251 | 252 |
253 | 254 | 255 | 256 | 257 | 258 |
259 |

Class References

260 | 273 |
274 | 275 | 276 | 277 |
278 | 279 | 280 |

Constant References

281 | 290 | 291 | 292 | 293 |
294 | 295 |
296 | 299 | 309 |
310 |
311 | 312 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /OpenInGoogleMaps.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint OpenInGoogleMaps.podspec' to ensure this is a 3 | # valid spec and remove all comments before submitting the spec. 4 | # 5 | # Any lines starting with a # are optional, but encouraged 6 | # 7 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 8 | # 9 | 10 | Pod::Spec.new do |s| 11 | s.name = "OpenInGoogleMaps" 12 | s.version = "0.1.0" 13 | s.summary = "A helper class to simplify the task of opening a map directly in Google Maps on iOS" 14 | s.description = <<-DESC 15 | The `OpenInGoogleMapsController` class is designed to make it easy for an iOS 16 | developer to open a map, show a Street View location, or show a set of directions directly in Google 17 | Maps. The class supports using the `x-callback-URL` standard so that you can add a "Back to my app" 18 | button directly within Google Maps, and supports a number of fallback strategies, so that you can 19 | automatically open the map in another application if the user does not have Google Maps installed. 20 | DESC 21 | s.homepage = "https://github.com/googlemaps/OpenInGoogleMaps-iOS" 22 | # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 23 | s.license = { :type => 'Apache 2.0', :file => 'LICENSE' } 24 | s.author = { "Todd Kerpelman" => "kerp@google.com" } 25 | s.source = { :git => "https://github.com/googlemaps/OpenInGoogleMaps-iOS.git", :tag => s.version.to_s } 26 | s.platform = :ios, '7.0' 27 | s.requires_arc = true 28 | s.source_files = 'OpenInGoogleMapsController.{h,m}' 29 | s.frameworks = 'CoreLocation' 30 | end 31 | -------------------------------------------------------------------------------- /OpenInGoogleMapsController.h: -------------------------------------------------------------------------------- 1 | // 2 | // OpenInGoogleMapsController.h 3 | // 4 | // Copyright 2014 Google Inc. All rights reserved. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // For more information on using the OpenInGoogleMapsController, please refer to the README.md 19 | // file included with this project, or to the Google Maps URL Scheme documentation at 20 | // https://developers.google.com/maps/documentation/ios/urlscheme 21 | // 22 | 23 | #import 24 | #import 25 | 26 | /** 27 | * Options for some of the views you can toggle in Google Maps. 28 | */ 29 | typedef NS_OPTIONS(NSInteger, GoogleMapsViewOptions){ 30 | /** 31 | * Satellite view. 32 | */ 33 | kGoogleMapsViewOptionSatellite = 1 << 0, 34 | /** 35 | * Show traffic information. 36 | */ 37 | kGoogleMapsViewOptionTraffic = 1 << 1, 38 | /** 39 | * Show public transit routes. 40 | */ 41 | kGoogleMapsViewOptionTransit = 1 << 2 42 | }; 43 | 44 | /** 45 | * Defines the method by which the user would like to travel when creating directions. 46 | */ 47 | typedef NS_ENUM(NSInteger, GoogleMapsTravelMode){ 48 | /** 49 | * Driving 50 | */ 51 | kGoogleMapsTravelModeDriving = 1, 52 | /** 53 | * Public transit 54 | */ 55 | kGoogleMapsTravelModeTransit, 56 | /** 57 | * Biking 58 | */ 59 | kGoogleMapsTravelModeBiking, 60 | /** 61 | * Walking 62 | */ 63 | kGoogleMapsTravelModeWalking 64 | }; 65 | 66 | /** 67 | * Fallback strategies for what to show if the user doesn't have Google Maps installed. 68 | */ 69 | typedef NS_ENUM(NSInteger, GoogleMapsFallback){ 70 | /** 71 | * Do nothing else, and return NO. This is the default option. 72 | */ 73 | kGoogleMapsFallbackNone, 74 | /** 75 | * Show the map in Apple's Maps app instead. Choose this option if you'd prefer to use a native 76 | * app at all times. 77 | */ 78 | kGoogleMapsFallbackAppleMaps, 79 | /** 80 | * Show the map in Chrome, if available. Otherwise display the map in Google Maps within Safari. 81 | * Choose this option if it's important that the map you view is based on Google's map data. 82 | */ 83 | kGoogleMapsFallbackChromeThenSafari, 84 | /** 85 | * Show the map in Chrome, if available. Otherwise display the map in Apple's Maps app. Choose 86 | * this option if you'd prefer apps that can point back to your application using the 87 | * `x-callback-url` standard. 88 | */ 89 | kGoogleMapsFallbackChromeThenAppleMaps, 90 | /** 91 | * Show the map in Google Maps in the Safari browser. 92 | */ 93 | kGoogleMapsFallbackSafari 94 | }; 95 | 96 | /** 97 | * Helper class used to define a map to be opened in Google Maps. Note that there's a good chance 98 | * some of these properties will be nil, but either the `queryString` or the `center` property 99 | * should be set. 100 | */ 101 | @interface GoogleMapDefinition : NSObject 102 | 103 | /** 104 | * A query string which, if set, will be used to search for a place by name. 105 | */ 106 | @property(nonatomic, copy) NSString *queryString; 107 | 108 | /** 109 | * Location in lat/long. If both this and the `queryString` are specified, this will be used as a 110 | * center for the search. 111 | * To clear this value, set it to `kCLLocationCoordinate2DInvalid`. 112 | */ 113 | @property(nonatomic, assign) CLLocationCoordinate2D center; 114 | 115 | /** 116 | * Bitwise ORs of different viewing options to display in the Google Maps application. 117 | * 118 | * @see GoogleMapsViewOptions 119 | */ 120 | @property(nonatomic, assign) GoogleMapsViewOptions viewOptions; 121 | 122 | /** 123 | * Zoom level. Currently, Google Maps clamps this value from 0.0 (to show the whole Earth) to 21.0. 124 | */ 125 | @property(nonatomic, assign) float zoomLevel; 126 | 127 | @end 128 | 129 | /** 130 | * Helper class used to define a Street View location to be opened in Google Maps, 131 | * Currently, this class is a subset of GoogleMapDefinition, but we're keeping these two classes 132 | * separate in case they diverge in the future. 133 | */ 134 | @interface GoogleStreetViewDefinition : NSObject 135 | 136 | /** 137 | * Location in lat/long. This is currently the only way you can define a Street View location. 138 | */ 139 | @property(nonatomic, assign) CLLocationCoordinate2D center; 140 | 141 | @end 142 | 143 | 144 | /** 145 | * A class used to define a waypoint for directions. 146 | */ 147 | @interface GoogleDirectionsWaypoint: NSObject 148 | 149 | /** 150 | * Class helper method to create a waypoint with a search query. 151 | * 152 | * @param queryString Query string for the waypoint. 153 | * 154 | * @return A waypoint to be used in a GoogleDirectionsDefinition object. 155 | */ 156 | + (instancetype)waypointWithQuery:(NSString *)queryString; 157 | 158 | /** 159 | * Class helper method to create a waypoint with a coordinate location. 160 | * 161 | * @param location A coodinate in lat/long for the waypoint. 162 | * 163 | * @return A waypoint to be used in a GoogleDirectionsDefinition object. 164 | */ 165 | + (instancetype)waypointWithLocation:(CLLocationCoordinate2D)location; 166 | 167 | /** 168 | * Lat/long of start or end location. If this is set and not equal to 169 | * `kCLLocationCoordinate2DInvalid`, it takes precedence over `queryString`. 170 | */ 171 | @property(nonatomic, assign) CLLocationCoordinate2D location; 172 | 173 | /** 174 | * Query string to use to determine this waypoint. For best results, use unambiguous strings 175 | * such as addresses ("355 Main Street, Cambridge, MA") or business search queries with unique 176 | * results ("The Exploratorium, San Francisco CA"). 177 | */ 178 | @property(nonatomic, copy) NSString *queryString; 179 | 180 | @end 181 | 182 | 183 | /** 184 | * Helper class used to define two waypoints from which to create a set of directions in Google 185 | * Maps. Either the starting point or the destination must be non-nil in order to produce a 186 | * valid request. 187 | */ 188 | @interface GoogleDirectionsDefinition : NSObject 189 | 190 | /** 191 | * Starting point. If this is set to `nil`, we will start at the user's current location. 192 | * 193 | * @see GoogleDirectionsWaypoint 194 | */ 195 | @property(nonatomic, strong) GoogleDirectionsWaypoint *startingPoint; 196 | 197 | /** 198 | * Destination. If this is set to `nil`, we will end at the user's current location. 199 | * 200 | * @see GoogleDirectionsWaypoint 201 | */ 202 | @property(nonatomic, strong) GoogleDirectionsWaypoint *destinationPoint; 203 | 204 | /** 205 | * Method of transportation for which to provide directions. If the application opened does not 206 | * support this travel type, it will fall back to providing driving directions. 207 | * 208 | * @see GoogleMapsTravelMode 209 | */ 210 | @property(nonatomic, assign) GoogleMapsTravelMode travelMode; 211 | 212 | @end 213 | 214 | /** 215 | 216 | The `OpenInGoogleMapsController` class is designed to make it easy for an iOS developer to open a 217 | map, show a Street View location, or show a set of directions directly in Google Maps. The class 218 | supports using the `x-callback-URL` standard so that you can add a "Back to _my app_" button 219 | directly within Google Maps, and supports a number of fallback strategies, so that you can 220 | automatically open the map in another application if the user does not have Google Maps installed. 221 | */ 222 | @interface OpenInGoogleMapsController : NSObject 223 | 224 | /** 225 | * Singleton method. Use this for making any calls against the `OpenInGoogleMapsController` class. 226 | * 227 | * @return Returns the shared singleton instance. 228 | */ 229 | + (OpenInGoogleMapsController *)sharedInstance; 230 | 231 | /** 232 | * The callback URL that you want Google maps (or Google Chrome) to use to redirect back to your 233 | * application. If `x-callback-url` is not supported, this will not be used and Google Maps will 234 | * be opened with a simple `comgooglemaps:` call instead. 235 | * See the Google Maps URL scheme documentation for more information about `x-callback-url` 236 | * support. 237 | */ 238 | @property(nonatomic, strong) NSURL *callbackURL; 239 | 240 | /** 241 | * Determines what to do if the user does not have Google Maps. You only need to set the 242 | * fallback strategy once during the lifetime of your application and it will be used in all future 243 | * `OpenInGoogleMapsController` requests. 244 | * @see GoogleMapsFallback 245 | */ 246 | @property(nonatomic, assign) GoogleMapsFallback fallbackStrategy; 247 | 248 | /** 249 | * Evaluates to `YES` if Google Mpas is installed, `NO` otherwise. 250 | */ 251 | @property(nonatomic, readonly, getter=isGoogleMapsInstalled) BOOL googleMapsInstalled; 252 | 253 | /** 254 | * Opens a map with the characteristics specified in the `GoogleMapDefinition `in Google Maps. 255 | * 256 | * @param definition A `GoogleMapDefinition` for the map you would like to open. 257 | * 258 | * @return Returns `YES` if it was succesfully able to open the map in Google Maps or one of the 259 | * fallback options as specified in `fallbackStrategy`. Returns `NO` otherwise. 260 | */ 261 | - (BOOL)openMap:(GoogleMapDefinition *)definition; 262 | 263 | /** 264 | * Shows Street View location in Google maps. 265 | * 266 | * @param definition A GoogleStreetViewDefinition for the location you would like to view. 267 | * 268 | * @return Returns `YES` if it was successfully able to open the Google Maps app or one of the 269 | * fallback options as specified in `fallbackStrategy`. Does _not_ guarantee that the address 270 | * entered is a valid street view location. 271 | */ 272 | - (BOOL)openStreetView:(GoogleStreetViewDefinition *)definition; 273 | 274 | /** 275 | * Show point-to-point directions in Google Maps. 276 | * 277 | * @param definition A GoogleDirectionsDefinition for the location you would like to view. 278 | * 279 | * @return Returns `YES` if it was successfully able to open the Google Maps app or one of the 280 | * fallback options as specified in `fallbackStrategy`. Does _not_ guarantee that the directions 281 | * request is a valid one, or that directions were found. 282 | */ 283 | - (BOOL)openDirections:(GoogleDirectionsDefinition *)definition; 284 | 285 | 286 | @end 287 | 288 | -------------------------------------------------------------------------------- /OpenInGoogleMapsController.m: -------------------------------------------------------------------------------- 1 | // 2 | // OpenInGoogleMapsController.m 3 | // 4 | // Copyright 2014 Google Inc. All rights reserved. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #import 19 | #import "OpenInGoogleMapsController.h" 20 | 21 | // Constants for URL schemes and arguments defined by Google Maps and Chrome. 22 | static NSString * const kGoogleMapsScheme = @"comgooglemaps://"; 23 | static NSString * const kGoogleMapsCallbackScheme = @"comgooglemaps-x-callback://"; 24 | static NSString* const kGoogleChromeOpenLink = 25 | @"googlechrome-x-callback://x-callback-url/open/?url="; 26 | 27 | static NSString * const kGoogleMapsStringTraffic = @"traffic"; 28 | static NSString * const kGoogleMapsStringTransit = @"transit"; 29 | static NSString * const kGoogleMapsStringSatellite = @"satellite"; 30 | 31 | /* 32 | * Helper method that percent-escapes a string so it can safely be used in a URL. 33 | */ 34 | static NSString *encodeByAddingPercentEscapes(NSString *input) { 35 | NSString *encodedValue = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes( 36 | kCFAllocatorDefault, (CFStringRef) input, NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", 37 | kCFStringEncodingUTF8)); 38 | return encodedValue; 39 | } 40 | 41 | /* 42 | * The GoogleMapsURLSchemable protocol states that any definition can be exported into an 43 | * array of URL arguments that can then be used to open Google Maps, Apple Maps, or a web page. 44 | * It's through these methods that the definitions do most of the "heavy lifting" required to 45 | * convert themselves into a URL that can be opened in the appropriate app. 46 | * 47 | * The first three methods return arrays of strings that represent URL arguments in the form of 48 | * "foo=bar". These can then by joined by ampersands (and prepended by a question mark) to create 49 | * a full URL. 50 | */ 51 | @protocol GoogleMapsURLSchemable 52 | @required 53 | - (NSArray *)URLArgumentsForGoogleMaps; 54 | - (NSArray *)URLArgumentsForAppleMaps; 55 | - (NSArray *)URLArgumentsForWeb; 56 | - (BOOL)anythingToSearchFor; 57 | @end 58 | 59 | /* 60 | * GoogleMapDefinition - A definition for opening up a location in a map. 61 | */ 62 | @interface GoogleMapDefinition() 63 | @end 64 | 65 | 66 | @implementation GoogleMapDefinition 67 | - (instancetype)init { 68 | self = [super init]; 69 | if (self) { 70 | _center = kCLLocationCoordinate2DInvalid; 71 | } 72 | return self; 73 | } 74 | 75 | 76 | - (BOOL)anythingToSearchFor { 77 | return (CLLocationCoordinate2DIsValid(self.center) || self.queryString); 78 | } 79 | 80 | 81 | - (NSArray *)URLArgumentsForGoogleMaps { 82 | NSMutableArray *urlArguments = [NSMutableArray array]; 83 | if (self.queryString) { 84 | [urlArguments addObject: 85 | [NSString stringWithFormat:@"q=%@", encodeByAddingPercentEscapes(self.queryString)]]; 86 | } 87 | 88 | if (CLLocationCoordinate2DIsValid(self.center)) { 89 | [urlArguments addObject:[NSString stringWithFormat:@"center=%f,%f", 90 | self.center.latitude, self.center.longitude]]; 91 | } 92 | if (self.zoomLevel > 0) { 93 | [urlArguments addObject:[NSString stringWithFormat:@"zoom=%f", self.zoomLevel]]; 94 | } 95 | if (self.viewOptions) { 96 | NSMutableArray *viewsToShow = [NSMutableArray arrayWithCapacity:3]; 97 | if (self.viewOptions & kGoogleMapsViewOptionSatellite) { 98 | [viewsToShow addObject:kGoogleMapsStringSatellite]; 99 | } 100 | if (self.viewOptions & kGoogleMapsViewOptionTraffic) { 101 | [viewsToShow addObject:kGoogleMapsStringTraffic]; 102 | } 103 | if (self.viewOptions & kGoogleMapsViewOptionTransit) { 104 | [viewsToShow addObject:kGoogleMapsStringTransit]; 105 | } 106 | [urlArguments addObject: 107 | [NSString stringWithFormat:@"views=%@", [viewsToShow componentsJoinedByString:@","]]]; 108 | } 109 | return urlArguments; 110 | } 111 | 112 | - (NSArray *)URLArgumentsForAppleMaps { 113 | NSMutableArray *urlArguments = [NSMutableArray array]; 114 | if (self.queryString) { 115 | [urlArguments addObject: 116 | [NSString stringWithFormat:@"q=%@", encodeByAddingPercentEscapes(self.queryString)]]; 117 | } 118 | if (CLLocationCoordinate2DIsValid(self.center)) { 119 | [urlArguments addObject:[NSString stringWithFormat:@"ll=%f,%f", 120 | self.center.latitude, self.center.longitude]]; 121 | } 122 | if (self.zoomLevel > 0) { 123 | [urlArguments addObject:[NSString stringWithFormat:@"z=%d", (int)self.zoomLevel]]; 124 | } 125 | // Apple Map's "Hybrid" view is closest to what Google's "Satellite" view looks like 126 | if (self.viewOptions & kGoogleMapsViewOptionSatellite) { 127 | [urlArguments addObject:@"t=h"]; 128 | } 129 | // TODO: Figure out what URL scheme argument enables traffic information. 130 | 131 | return urlArguments; 132 | } 133 | 134 | - (NSArray *)URLArgumentsForWeb { 135 | NSMutableArray *urlArguments = [NSMutableArray array]; 136 | if (self.queryString) { 137 | [urlArguments addObject: 138 | [NSString stringWithFormat:@"q=%@", encodeByAddingPercentEscapes(self.queryString)]]; 139 | } 140 | if (CLLocationCoordinate2DIsValid(self.center)) { 141 | [urlArguments addObject:[NSString stringWithFormat:@"ll=%f,%f", 142 | self.center.latitude, self.center.longitude]]; 143 | } 144 | if (self.zoomLevel > 0) { 145 | [urlArguments addObject:[NSString stringWithFormat:@"z=%d", (int)self.zoomLevel]]; 146 | } 147 | if (self.viewOptions & kGoogleMapsViewOptionSatellite) { 148 | [urlArguments addObject:@"t=h"]; 149 | } 150 | if (self.viewOptions & kGoogleMapsViewOptionTraffic) { 151 | [urlArguments addObject:@"layer=t"]; 152 | } 153 | 154 | if (self.viewOptions & kGoogleMapsViewOptionTransit) { 155 | [urlArguments addObject:@"lci=transit_comp"]; 156 | } 157 | 158 | return urlArguments; 159 | } 160 | 161 | @end 162 | 163 | /* 164 | * GoogleStreetViewDefinition - A definition for opening up a location in Street View. 165 | */ 166 | 167 | @interface GoogleStreetViewDefinition() 168 | @end 169 | 170 | @implementation GoogleStreetViewDefinition 171 | - (instancetype)init { 172 | self = [super init]; 173 | if (self) { 174 | _center = kCLLocationCoordinate2DInvalid; 175 | } 176 | return self; 177 | } 178 | 179 | - (BOOL)anythingToSearchFor { 180 | return CLLocationCoordinate2DIsValid(self.center); 181 | } 182 | 183 | - (NSArray *)URLArgumentsForGoogleMaps { 184 | NSMutableArray *urlArguments = [NSMutableArray array]; 185 | [urlArguments addObject: 186 | [NSString stringWithFormat:@"center=%f,%f", self.center.latitude, self.center.longitude]]; 187 | [urlArguments addObject:@"mapmode=streetview"]; 188 | return urlArguments; 189 | } 190 | 191 | /* 192 | * Apple Maps doesn't support Street View, but we can zoom in to the general location with 193 | * satellite view. That's pretty close. 194 | */ 195 | - (NSArray *)URLArgumentsForAppleMaps { 196 | NSMutableArray *urlArguments = [NSMutableArray array]; 197 | 198 | 199 | [urlArguments addObject: 200 | [NSString stringWithFormat:@"ll=%f,%f", self.center.latitude, self.center.longitude]]; 201 | [urlArguments addObject:@"z=19"]; 202 | [urlArguments addObject:@"t=k"]; 203 | return urlArguments; 204 | } 205 | 206 | /* 207 | * Currently, we are unable to open a link to Street View in our mobile web browser. But we 208 | * can zoom in with satellite view just like we do in Apple Maps 209 | */ 210 | - (NSArray *)URLArgumentsForWeb { 211 | return [self URLArgumentsForAppleMaps]; 212 | } 213 | 214 | 215 | @end 216 | 217 | /* 218 | * GoogleDirectionsWaypoint - A point defined by either a set of coordinates or a search string. 219 | * Used by the GoogleDirectionsDefinition classs. 220 | */ 221 | @interface GoogleDirectionsWaypoint() 222 | @end 223 | 224 | @implementation GoogleDirectionsWaypoint 225 | - (instancetype)init { 226 | self = [super init]; 227 | if (self) { 228 | _location = kCLLocationCoordinate2DInvalid; 229 | } 230 | return self; 231 | } 232 | 233 | /* 234 | * Since waypoints should contain a location or a query string (but not both), 235 | * these static helper methods can be quite handy. 236 | */ 237 | + (instancetype)waypointWithLocation:(CLLocationCoordinate2D)location { 238 | GoogleDirectionsWaypoint *waypoint = [[GoogleDirectionsWaypoint alloc] init]; 239 | waypoint.location = location; 240 | return waypoint; 241 | } 242 | 243 | + (instancetype)waypointWithQuery:(NSString *)queryString { 244 | GoogleDirectionsWaypoint *waypoint = [[GoogleDirectionsWaypoint alloc] init]; 245 | waypoint.queryString = queryString; 246 | return waypoint; 247 | } 248 | 249 | - (BOOL)anythingToSearchFor { 250 | return (CLLocationCoordinate2DIsValid(self.location) || self.queryString); 251 | } 252 | 253 | /* 254 | * Since a waypoint could be the start address ('saddr' in the URL) or the end address ('daddr'), 255 | * we need to pass in the proper key when retrieving the URL argument for this waypoint. 256 | */ 257 | - (NSString *)URLArgumentUsingKey:(NSString *)key { 258 | if (CLLocationCoordinate2DIsValid(self.location)) { 259 | return [NSString stringWithFormat:@"%@=%f,%f", 260 | key, self.location.latitude, self.location.longitude]; 261 | } else if (self.queryString) { 262 | return [NSString stringWithFormat:@"%@=%@", 263 | key, encodeByAddingPercentEscapes(self.queryString)]; 264 | } else { 265 | return @""; 266 | } 267 | } 268 | 269 | @end 270 | 271 | /* 272 | * GoogleDirectionsWaypoint - A point defined by either a set of coordinates or a search string. 273 | * Used by the GoogleDirectionsDefinition classs. 274 | */ 275 | @interface GoogleDirectionsDefinition() 276 | @end 277 | 278 | @implementation GoogleDirectionsDefinition 279 | 280 | - (BOOL)anythingToSearchFor { 281 | return ([self.startingPoint anythingToSearchFor] || [self.destinationPoint anythingToSearchFor]); 282 | } 283 | 284 | /* 285 | * Retrieving the "travel mode" argument in Google Maps. 286 | */ 287 | - (NSString *)urlArgumentValueForTravelMode { 288 | switch (self.travelMode) { 289 | case kGoogleMapsTravelModeBiking: 290 | return @"bicycling"; 291 | case kGoogleMapsTravelModeDriving: 292 | return @"driving"; 293 | case kGoogleMapsTravelModeTransit: 294 | return @"transit"; 295 | case kGoogleMapsTravelModeWalking: 296 | return @"walking"; 297 | } 298 | return nil; 299 | } 300 | 301 | /* 302 | * Retrieving the "travel mode" argument for the web. 303 | */ 304 | - (NSString *)urlArgumentValueForTravelModeWeb { 305 | switch (self.travelMode) { 306 | case kGoogleMapsTravelModeBiking: 307 | return @"b"; 308 | case kGoogleMapsTravelModeDriving: 309 | return @"c"; 310 | case kGoogleMapsTravelModeTransit: 311 | return @"r"; 312 | case kGoogleMapsTravelModeWalking: 313 | return @"w"; 314 | } 315 | return nil; 316 | } 317 | 318 | /* 319 | * Retrieving the start and end waypoint arguments is the same in Google Maps, Apple Maps, and 320 | * the web. 321 | */ 322 | - (NSMutableArray *)waypointArguments { 323 | NSMutableArray *waypointArguments = [NSMutableArray array]; 324 | if ([self.startingPoint anythingToSearchFor]) { 325 | [waypointArguments addObject:[self.startingPoint URLArgumentUsingKey:@"saddr"]]; 326 | } 327 | if ([self.destinationPoint anythingToSearchFor]) { 328 | [waypointArguments addObject:[self.destinationPoint URLArgumentUsingKey:@"daddr"]]; 329 | } 330 | return waypointArguments; 331 | } 332 | 333 | - (NSArray *)URLArgumentsForGoogleMaps { 334 | NSMutableArray *urlArguments = [self waypointArguments]; 335 | 336 | NSString *travelMode = [self urlArgumentValueForTravelMode]; 337 | if (travelMode) { 338 | [urlArguments addObject:[NSString stringWithFormat:@"directionsmode=%@", travelMode]]; 339 | } 340 | return urlArguments; 341 | } 342 | 343 | - (NSArray *)URLArgumentsForAppleMaps { 344 | NSMutableArray *urlArguments = [self waypointArguments]; 345 | 346 | if (self.travelMode == kGoogleMapsTravelModeDriving) { 347 | [urlArguments addObject:@"dirflg=d"]; 348 | } else if (self.travelMode == kGoogleMapsTravelModeWalking) { 349 | [urlArguments addObject:@"dirflg=w"]; 350 | } 351 | return urlArguments; 352 | } 353 | 354 | - (NSArray *)URLArgumentsForWeb { 355 | NSMutableArray *urlArguments = [self waypointArguments]; 356 | 357 | NSString *travelMode = [self urlArgumentValueForTravelModeWeb]; 358 | if (travelMode) { 359 | [urlArguments addObject:[NSString stringWithFormat:@"dirflg=%@", travelMode]]; 360 | } 361 | return urlArguments; 362 | } 363 | 364 | 365 | @end 366 | 367 | /* 368 | * OpenInGoogleMapsController - The main class that creates and opens a URL to display a particular 369 | * map in Google Maps (or an alternative application is a fallback strategy is specified). 370 | * 371 | * Most of the work required to take a definition and open it up in the proper application 372 | * is done by the definitions themselves. This class creates the "outer bits" of the URL (like 373 | * the base URL and any x-callback-url data at the end), and takes care of the logic for performing 374 | * the various fallback strategies. 375 | */ 376 | 377 | @implementation OpenInGoogleMapsController { 378 | UIApplication *_sharedApplication; 379 | } 380 | 381 | + (OpenInGoogleMapsController *)sharedInstance { 382 | static OpenInGoogleMapsController *_sharedInstance; 383 | static dispatch_once_t oncePredicate; 384 | dispatch_once(&oncePredicate, ^{ 385 | _sharedInstance = [[self alloc] init]; 386 | }); 387 | return _sharedInstance; 388 | } 389 | 390 | - (instancetype)init { 391 | self = [super init]; 392 | if (self) { 393 | _sharedApplication = [UIApplication sharedApplication]; 394 | } 395 | return self; 396 | } 397 | 398 | 399 | - (BOOL)isGoogleMapsInstalled { 400 | NSURL *simpleURL = [NSURL URLWithString:kGoogleMapsScheme]; 401 | NSURL *callbackURL = [NSURL URLWithString:kGoogleMapsCallbackScheme]; 402 | return ([_sharedApplication canOpenURL:simpleURL] || 403 | [_sharedApplication canOpenURL:callbackURL]); 404 | } 405 | 406 | 407 | 408 | - (BOOL)fallBackToAppleMapsWithDefinition:(id)definition { 409 | 410 | NSMutableString *mapURL = [@"https://maps.apple.com/" mutableCopy]; 411 | [mapURL appendString:[NSString stringWithFormat:@"?%@", 412 | [[definition URLArgumentsForAppleMaps] componentsJoinedByString:@"&"]]]; 413 | #if DEBUG 414 | NSLog(@"Opening up URL: %@", mapURL); 415 | #endif 416 | NSURL *URLToOpen = [NSURL URLWithString:mapURL]; 417 | 418 | return [_sharedApplication openURL:URLToOpen]; 419 | } 420 | 421 | 422 | - (BOOL)fallbackToChromeFirstWithDefinition:(id)definition { 423 | NSMutableString *mapURL = [kGoogleChromeOpenLink mutableCopy]; 424 | 425 | NSString *embedURL = @"https://maps.google.com/maps/"; 426 | NSString *urlArgumentsAsString = [NSString stringWithFormat:@"?%@", 427 | [[definition URLArgumentsForWeb] componentsJoinedByString:@"&"]]; 428 | 429 | NSString *fullEmbedURL = [embedURL stringByAppendingString:urlArgumentsAsString]; 430 | [mapURL appendString:encodeByAddingPercentEscapes(fullEmbedURL)]; 431 | #if DEBUG 432 | NSLog(@"Opening up URL: %@", mapURL); 433 | NSLog(@"Embedded URL of: %@", fullEmbedURL); 434 | #endif 435 | [self appendMapURLString:mapURL withCallbackArgumentsFromURL:self.callbackURL]; 436 | 437 | NSURL *URLToOpen = [NSURL URLWithString:mapURL]; 438 | if ([_sharedApplication openURL:URLToOpen]) { 439 | return YES; 440 | } else if (self.fallbackStrategy == kGoogleMapsFallbackChromeThenAppleMaps) { 441 | return [self fallBackToAppleMapsWithDefinition:definition]; 442 | } else if (self.fallbackStrategy == kGoogleMapsFallbackChromeThenSafari) { 443 | return [self fallbackToSafariWithDefinition:definition]; 444 | } 445 | return NO; 446 | } 447 | 448 | 449 | - (BOOL)fallbackToSafariWithDefinition:(id)definition { 450 | NSMutableString *mapURL = [@"https://maps.google.com/maps" mutableCopy]; 451 | 452 | [mapURL appendString:[NSString stringWithFormat:@"?%@", 453 | [[definition URLArgumentsForWeb] componentsJoinedByString:@"&"]]]; 454 | #if DEBUG 455 | NSLog(@"Opening up URL: %@", mapURL); 456 | #endif 457 | NSURL *URLToOpen = [NSURL URLWithString:mapURL]; 458 | return [_sharedApplication openURL:URLToOpen]; 459 | } 460 | 461 | 462 | /* 463 | * Since the definitions themselves do most of the work required to generate the URL arguments 464 | * appropriate for their type of map request, the same method can be used to open up the correct 465 | * URL, no matter what definition gets passed in. We chose not to make this method public, 466 | * simply because the more explicit methods below eliminate potential confusion. 467 | */ 468 | - (BOOL)openInGoogleMapsWithDefinition:(id)definition { 469 | // Did we define anything to search for in our map? 470 | if (![definition anythingToSearchFor]) { 471 | return NO; 472 | } 473 | 474 | // Can we open this in google maps? 475 | if (![self isGoogleMapsInstalled]) { 476 | switch (self.fallbackStrategy) { 477 | case kGoogleMapsFallbackNone: 478 | return NO; 479 | case kGoogleMapsFallbackAppleMaps: 480 | return [self fallBackToAppleMapsWithDefinition:definition]; 481 | case kGoogleMapsFallbackChromeThenSafari: 482 | case kGoogleMapsFallbackChromeThenAppleMaps: 483 | return [self fallbackToChromeFirstWithDefinition:definition]; 484 | case kGoogleMapsFallbackSafari: 485 | return [self fallbackToSafariWithDefinition:definition]; 486 | } 487 | } 488 | 489 | NSMutableString *mapURL = [[self baseURLStringUsingCallback:self.callbackURL] mutableCopy]; 490 | 491 | [mapURL appendString:[NSString stringWithFormat:@"?%@", 492 | [[definition URLArgumentsForGoogleMaps] componentsJoinedByString:@"&"]]]; 493 | [self appendMapURLString:mapURL withCallbackArgumentsFromURL:self.callbackURL]; 494 | #if DEBUG 495 | NSLog(@"Opening up URL: %@", mapURL); 496 | #endif 497 | NSURL *URLToOpen = [NSURL URLWithString:mapURL]; 498 | 499 | return [_sharedApplication openURL:URLToOpen]; 500 | } 501 | 502 | 503 | - (BOOL)openMap:(GoogleMapDefinition *)definition { 504 | return [self openInGoogleMapsWithDefinition:definition]; 505 | } 506 | 507 | - (BOOL)openStreetView:(GoogleStreetViewDefinition *)definition { 508 | return [self openInGoogleMapsWithDefinition:definition]; 509 | } 510 | 511 | - (BOOL)openDirections:(GoogleDirectionsDefinition *)definition { 512 | return [self openInGoogleMapsWithDefinition:definition]; 513 | } 514 | 515 | 516 | # pragma mark - Map URL fragment methods 517 | 518 | /* 519 | * Returns the correct URL scheme (comgooglemaps vs comgooglemaps-x-callback), 520 | * depending on whether or not the callback URL can be opened. 521 | */ 522 | - (NSString *)baseURLStringUsingCallback:(NSURL *)callbackURL { 523 | BOOL usingCallback = callbackURL && [_sharedApplication canOpenURL:callbackURL]; 524 | return (usingCallback) ? kGoogleMapsCallbackScheme : kGoogleMapsScheme; 525 | } 526 | 527 | /* 528 | * Add the x-success and x-source arguments to the end of an URL string, if the callback URL 529 | * exists and is supported by the target app. 530 | */ 531 | - (void)appendMapURLString:(NSMutableString *)mapURL 532 | withCallbackArgumentsFromURL:(NSURL *)callbackURL { 533 | BOOL usingCallback = callbackURL && [_sharedApplication canOpenURL:callbackURL]; 534 | if (usingCallback) { 535 | [mapURL appendFormat:@"&x-success=%@", 536 | encodeByAddingPercentEscapes([callbackURL absoluteString])]; 537 | [mapURL appendFormat:@"&x-source=%@", 538 | encodeByAddingPercentEscapes( 539 | [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"])]; 540 | } 541 | } 542 | 543 | 544 | @end 545 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // OpenInGoogleMapsSample 4 | // 5 | // Copyright 2014 Google Inc. All rights reserved. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import 20 | 21 | @interface AppDelegate : UIResponder 22 | 23 | @property(strong, nonatomic) UIWindow *window; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // OpenInGoogleMapsSample 4 | // 5 | // Copyright 2014 Google Inc. All rights reserved. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import "AppDelegate.h" 20 | 21 | @implementation AppDelegate 22 | 23 | - (BOOL)application:(UIApplication *)application 24 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 25 | // Override point for customization after application launch. 26 | return YES; 27 | } 28 | 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/Enums.h: -------------------------------------------------------------------------------- 1 | // 2 | // Enums.h 3 | // OpenInGoogleMapsSample 4 | // 5 | // Copyright 2014 Google Inc. All rights reserved. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import 20 | 21 | // An enum to specify whether or not the user is searching for a start location or an 22 | // end location. (The latter is used only when searching for directions.) 23 | typedef NS_ENUM(NSInteger, LocationGroup) { 24 | kLocationGroupStart, 25 | kLocationGroupEnd 26 | }; 27 | 28 | // An enum to specify the travel mode for which the user wants directions. 29 | typedef NS_ENUM(NSInteger, TravelMode) { 30 | kTravelModeNotSpecified, 31 | kTravelModeDriving, 32 | kTravelModePublicTransit, 33 | kTravelModeBicycling, 34 | kTravelModeWalking 35 | }; 36 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/Images.xcassets/LaunchImage.launchimage/2xLaunch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/OpenInGoogleMaps-iOS/89de89de8ad5b8512e6cdcf09ab7b8f11e2f91f9/OpenInGoogleMapsSample/OpenInGoogleMapsSample/Images.xcassets/LaunchImage.launchimage/2xLaunch.png -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "filename" : "2xLaunch.png", 7 | "extent" : "full-screen", 8 | "minimum-system-version" : "7.0", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "Retina4Launch.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "ipad", 23 | "extent" : "full-screen", 24 | "minimum-system-version" : "7.0", 25 | "scale" : "1x" 26 | }, 27 | { 28 | "orientation" : "landscape", 29 | "idiom" : "ipad", 30 | "extent" : "full-screen", 31 | "minimum-system-version" : "7.0", 32 | "scale" : "1x" 33 | }, 34 | { 35 | "orientation" : "portrait", 36 | "idiom" : "ipad", 37 | "extent" : "full-screen", 38 | "minimum-system-version" : "7.0", 39 | "scale" : "2x" 40 | }, 41 | { 42 | "orientation" : "landscape", 43 | "idiom" : "ipad", 44 | "extent" : "full-screen", 45 | "minimum-system-version" : "7.0", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/Images.xcassets/LaunchImage.launchimage/Retina4Launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/OpenInGoogleMaps-iOS/89de89de8ad5b8512e6cdcf09ab7b8f11e2f91f9/OpenInGoogleMapsSample/OpenInGoogleMapsSample/Images.xcassets/LaunchImage.launchimage/Retina4Launch.png -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/MapRequestModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // MapRequestModel.h 3 | // OpenInGoogleMapsSample 4 | // 5 | // Copyright 2014 Google Inc. All rights reserved. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import "Enums.h" 20 | #import 21 | #import 22 | 23 | // This is an object that represents our request for a map, directions, or Street View location. We 24 | // intentionally chose not to re-use the definitions or enums in the OpenInGoogleMapsController to 25 | // keep things not quite as tighly coupled. 26 | @interface MapRequestModel : NSObject 27 | @property(nonatomic, copy, readonly) NSString *startQueryString; 28 | @property(nonatomic, assign, readonly) CLLocationCoordinate2D startLocation; 29 | // Whether to use the "user's current location" as the start location. 30 | @property(nonatomic, assign, readonly, getter=isStartCurrentLocation) BOOL startCurrentLocation; 31 | @property(nonatomic, copy, readonly) NSString *destinationQueryString; 32 | @property(nonatomic, assign, readonly) CLLocationCoordinate2D desstinationLocation; 33 | @property(nonatomic, assign, readonly, getter=isDestinationCurrentLocation) 34 | BOOL destinationCurrentLocation; 35 | @property(nonatomic, assign) TravelMode travelMode; 36 | 37 | // Set only the text search for a beginning or end location group. For example, "4 Main Street, 38 | // Anytown USA". 39 | // |group| in these methods is a LocationGroup enum to specify whether we're setting our start 40 | // location or destination. The destination group is only used when getting directions. 41 | - (void)setQueryString:(NSString *)query forGroup:(LocationGroup)group; 42 | 43 | // Set a text search for a location group along with a latitude and longitude. Text group 44 | // can be nil (when specifying a specific coordinate, useful for Street View) or they can be 45 | // combined, as in "Ice cream near 37.7579691, -122.3880665" 46 | - (void)setQueryString:(NSString *)query 47 | center:(CLLocationCoordinate2D)center 48 | forGroup:(LocationGroup)group; 49 | 50 | // Set the beginning or end location to be "The user's current location". Used only in directions. 51 | - (void)useCurrentLocationForGroup:(LocationGroup)group; 52 | 53 | // Retrieves a text description of the search currently set for the location group. 54 | - (NSString *)descriptionForGroup:(LocationGroup)group; 55 | 56 | // Get string descriptions of the different travel modes, sorted by the enum values they 57 | // represent. 58 | - (NSArray *)sortedTravelModeDescriptions; 59 | 60 | // Get a text version of the "travelMode" property. 61 | - (NSString *)travelModeDescription; 62 | @end 63 | 64 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/MapRequestModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // MapRequestModel.m 3 | // OpenInGoogleMapsSample 4 | // 5 | // Copyright 2014 Google Inc. All rights reserved. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import "MapRequestModel.h" 20 | 21 | @implementation MapRequestModel { 22 | NSDictionary *_travelModeDescriptions; 23 | } 24 | 25 | - (instancetype)init { 26 | if (self = [super init]) { 27 | _travelModeDescriptions = @{ 28 | @(kTravelModeNotSpecified): @"(Unspecified)", 29 | @(kTravelModeDriving): @"Driving", 30 | @(kTravelModePublicTransit): @"Transit", 31 | @(kTravelModeBicycling): @"Biking", 32 | @(kTravelModeWalking): @"Walking" 33 | }; 34 | _travelMode = kTravelModeNotSpecified; 35 | _startLocation = kCLLocationCoordinate2DInvalid; 36 | _desstinationLocation = kCLLocationCoordinate2DInvalid; 37 | } 38 | return self; 39 | } 40 | 41 | - (void)setQueryString:(NSString *)query 42 | center:(CLLocationCoordinate2D)center 43 | forGroup:(LocationGroup)group { 44 | if (group == kLocationGroupStart) { 45 | _startCurrentLocation = NO; 46 | _startQueryString = [query copy]; 47 | _startLocation = center; 48 | } else if (group == kLocationGroupEnd) { 49 | _destinationCurrentLocation = NO; 50 | _destinationQueryString = [query copy]; 51 | _desstinationLocation = center; 52 | } 53 | } 54 | 55 | - (void)setQueryString:(NSString *)query forGroup:(LocationGroup)group { 56 | [self setQueryString:query center:kCLLocationCoordinate2DInvalid forGroup:group]; 57 | } 58 | 59 | 60 | - (void)useCurrentLocationForGroup:(LocationGroup)group { 61 | // Nil out everything else and just use our current location 62 | if (group == kLocationGroupStart) { 63 | _startQueryString = nil; 64 | _startLocation = kCLLocationCoordinate2DInvalid; 65 | _startCurrentLocation = YES; 66 | } else if (group == kLocationGroupEnd) { 67 | _destinationQueryString = nil; 68 | _desstinationLocation = kCLLocationCoordinate2DInvalid; 69 | _destinationCurrentLocation = YES; 70 | } 71 | } 72 | 73 | - (NSString *)descriptionForGroup:(LocationGroup)group { 74 | if (group == kLocationGroupStart) { 75 | return [self makeDescriptionForSearch:_startQueryString 76 | withLocation:_startLocation 77 | usingCurrentLocation:_startCurrentLocation]; 78 | } else { 79 | return [self makeDescriptionForSearch:_destinationQueryString 80 | withLocation:_desstinationLocation 81 | usingCurrentLocation:_destinationCurrentLocation]; 82 | } 83 | } 84 | 85 | - (NSArray *)sortedTravelModeDescriptions { 86 | NSArray *sortedKeys = 87 | [[_travelModeDescriptions allKeys] sortedArrayUsingSelector:@selector(compare:)]; 88 | NSArray *valuesFromSortedKeys = 89 | [_travelModeDescriptions objectsForKeys:sortedKeys notFoundMarker:[NSNull null]]; 90 | return valuesFromSortedKeys; 91 | } 92 | 93 | - (NSString *)makeDescriptionForSearch:(NSString *)searchString 94 | withLocation:(CLLocationCoordinate2D)location 95 | usingCurrentLocation:(BOOL)currentLocation { 96 | BOOL isLocationValid = CLLocationCoordinate2DIsValid(location); 97 | if (searchString == nil && !isLocationValid && currentLocation == NO) { 98 | return @"-- Location not set --"; 99 | } else if (currentLocation) { 100 | return @"(Current Location)"; 101 | } else if (searchString != nil && !isLocationValid) { 102 | return searchString; 103 | } else if (searchString == nil && isLocationValid) { 104 | return [NSString stringWithFormat:@"Lat: %.4f, Long: %.4f", 105 | location.latitude, location.longitude]; 106 | } else { 107 | return [NSString stringWithFormat:@"%@ near (%.2f, %.2f)", 108 | searchString, location.latitude, location.longitude]; 109 | } 110 | } 111 | 112 | - (NSString *)travelModeDescription { 113 | return [_travelModeDescriptions objectForKey:@(self.travelMode)]; 114 | } 115 | 116 | 117 | @end 118 | 119 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/OpenInGoogleMapsSample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleTypeRole 27 | Editor 28 | CFBundleURLName 29 | OpenInGoogleMapsSample 30 | CFBundleURLSchemes 31 | 32 | OpenInGoogleMapsSample 33 | 34 | 35 | 36 | CFBundleVersion 37 | 1.0 38 | LSRequiresIPhoneOS 39 | 40 | UIMainStoryboardFile 41 | Main_iPhone 42 | UIMainStoryboardFile~ipad 43 | Main_iPad 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | 52 | LSApplicationQueriesSchemes 53 | 54 | comgooglemaps 55 | comgooglemaps-x-callback 56 | OpenInGoogleMapsSample 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/OpenInGoogleMapsSample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/PickLocationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PickLocationViewController.h 3 | // OpenInGoogleMapsSample 4 | // 5 | // Copyright 2014 Google Inc. All rights reserved. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import "Enums.h" 20 | #import 21 | #import 22 | 23 | 24 | @class PickLocationViewController; 25 | 26 | // The PickLocationDelegate is used to tell the ViewController what location the user ended 27 | // up searching for. 28 | @protocol PickLocationDelegate 29 | // The user specified only a search string. (e.g. "425 Main Street") 30 | - (void)pickLocationController:(PickLocationViewController *)controller 31 | pickedQueryString:(NSString *)query 32 | forGroup:(LocationGroup)group; 33 | // The user specified only a set of coordinates. 34 | - (void)pickLocationController:(PickLocationViewController *)controller 35 | pickedLocation:(CLLocationCoordinate2D)location 36 | forGroup:(LocationGroup)group; 37 | // The user specified a search string and set of coordinates. (e.g. "Ice cream near 38 | // 37.7579691, -122.3880665") 39 | - (void)pickLocationController:(PickLocationViewController *)controller 40 | pickedQueryString:(NSString *)query 41 | location:(CLLocationCoordinate2D)location 42 | forGroup:(LocationGroup)group; 43 | // The user turned on the "Use current location" switch. 44 | - (void)pickLocationController:(PickLocationViewController *)controller 45 | pickedCurrentLocationForGroup:(LocationGroup)group; 46 | // The user didn't pick anything, so leave the current values unchanged. 47 | - (void)noLocationPickedByPickLocationController:(PickLocationViewController *)controller; 48 | @end 49 | 50 | @interface PickLocationViewController : UIViewController 51 | // The location group we are specifying a location for. 52 | @property(nonatomic, assign) LocationGroup group; 53 | @property(nonatomic, weak) id delegate; 54 | // Should we allow the "use current location" switch? Currently used only when searching for 55 | // directions. 56 | @property(nonatomic, assign) BOOL allowCurrentLocation; 57 | @end 58 | 59 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/PickLocationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PickLocationViewController.m 3 | // OpenInGoogleMapsSample 4 | // 5 | // Copyright 2014 Google Inc. All rights reserved. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import "PickLocationViewController.h" 20 | 21 | @interface PickLocationViewController () 22 | @property(weak, nonatomic) IBOutlet UITextField *searchStringTextField; 23 | @property(weak, nonatomic) IBOutlet UITextField *latTextField; 24 | @property(weak, nonatomic) IBOutlet UITextField *longTextField; 25 | @property(weak, nonatomic) IBOutlet UILabel *instructionLabel; 26 | @property(weak, nonatomic) IBOutlet UIPickerView *locationPicker; 27 | @property(weak, nonatomic) IBOutlet UISwitch *currentLocationSwitch; 28 | @property(weak, nonatomic) IBOutlet UILabel *currentLocationLabel; 29 | @end 30 | 31 | 32 | 33 | @implementation PickLocationViewController { 34 | NSArray *_someNiceDefaults; 35 | } 36 | 37 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 38 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 39 | if (self) { 40 | // Custom initialization 41 | } 42 | return self; 43 | } 44 | 45 | - (IBAction)doneButtonPressed:(id)sender { 46 | // Let's pass the information back to our delegate. 47 | NSString *searchStringText = self.searchStringTextField.text; 48 | float latValue = [self.latTextField.text floatValue]; 49 | float longValue = [self.longTextField.text floatValue]; 50 | if (latValue < -90 || latValue > 90) 51 | latValue = 0; 52 | if (longValue < -180 || longValue > 180) 53 | longValue = 0; 54 | 55 | // TODO: Explicitly writing a lat,long of 0,0 (or 0.0, 0.0) will 56 | // count as not setting it at all. Let's fix that 57 | CLLocationCoordinate2D location; 58 | if (latValue == 0 && longValue == 0) { 59 | location = kCLLocationCoordinate2DInvalid; 60 | } else { 61 | location = CLLocationCoordinate2DMake(latValue, longValue); 62 | } 63 | BOOL validLocaton = CLLocationCoordinate2DIsValid(location); 64 | 65 | if (self.currentLocationSwitch.isOn) { 66 | NSLog(@"Current location is on!!"); 67 | [self.delegate pickLocationController:self pickedCurrentLocationForGroup:self.group]; 68 | } else if ([searchStringText isEqualToString:@""]) { 69 | if (validLocaton) { 70 | [self.delegate pickLocationController:self 71 | pickedLocation:location 72 | forGroup:self.group]; 73 | } else { 74 | [self.delegate noLocationPickedByPickLocationController:self]; 75 | } 76 | } else { 77 | if (validLocaton) { 78 | [self.delegate pickLocationController:self 79 | pickedQueryString:searchStringText 80 | location:location 81 | forGroup:self.group]; 82 | } else { 83 | [self.delegate pickLocationController:self 84 | pickedQueryString:searchStringText 85 | forGroup:self.group]; 86 | } 87 | } 88 | [self dismissViewControllerAnimated:YES completion:nil]; 89 | 90 | } 91 | - (IBAction)useCurrentLocationSwitchChanged:(UISwitch *)sender { 92 | self.searchStringTextField.enabled = !sender.isOn; 93 | self.latTextField.enabled = !sender.isOn; 94 | self.longTextField.enabled = !sender.isOn; 95 | self.locationPicker.hidden = sender.isOn; 96 | } 97 | 98 | // Hide the keyboard when touching outside the textfields 99 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 100 | [[self view] endEditing:YES]; 101 | } 102 | 103 | 104 | #pragma mark - Picker View DataSource methods 105 | 106 | - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { 107 | return 1; 108 | } 109 | 110 | - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { 111 | return _someNiceDefaults.count; 112 | } 113 | 114 | #pragma mark - Picker View Delegate methods 115 | 116 | - (NSString *)pickerView:(UIPickerView *)pickerView 117 | titleForRow:(NSInteger)row 118 | forComponent:(NSInteger)component { 119 | NSDictionary *entry = _someNiceDefaults[row]; 120 | return entry[@"description"]; 121 | } 122 | 123 | - (void)pickerView:(UIPickerView *)pickerView 124 | didSelectRow:(NSInteger)row 125 | inComponent:(NSInteger)component { 126 | NSDictionary *entry = _someNiceDefaults[row]; 127 | if (entry[@"search"] != [NSNull null]) { 128 | self.searchStringTextField.text = entry[@"search"]; 129 | } else { 130 | self.searchStringTextField.text = @""; 131 | } 132 | 133 | if (entry[@"loc"] != [NSNull null]) { 134 | NSArray *location = entry[@"loc"]; 135 | self.latTextField.text = [(NSNumber *)location[0] stringValue]; 136 | self.longTextField.text = [(NSNumber *)location[1] stringValue]; 137 | } else { 138 | self.latTextField.text = @""; 139 | self.longTextField.text = @""; 140 | } 141 | } 142 | 143 | - (void)viewDidLoad { 144 | [super viewDidLoad]; 145 | _someNiceDefaults = @[@{@"search": @"1600 Amphitheatre Parkway, Mountain View, CA", 146 | @"loc": [NSNull null], 147 | @"description": @"1600 Amphitheatre Parkway"}, 148 | @{@"search": @"345 Spear Street, San Francisco, CA", 149 | @"loc": [NSNull null], 150 | @"description": @"345 Spear Street, SF"}, 151 | @{@"search": [NSNull null], 152 | @"loc": @[@48.8536629,@2.3479513], 153 | @"description": @"(48.854, 2.347) -- Notre Dame" }, 154 | @{@"search": @"pizza", 155 | @"loc": @[@40.758895,@-73.985131], 156 | @"description": @"Pizza near Times Square"}, 157 | @{@"search": @"ramen", 158 | @"loc": @[@35.680066,@139.767813], 159 | @"description": @"Ramen near Tokyo Station"}, 160 | @{@"search": @"ice cream", 161 | @"loc": @[@37.7579691,@-122.3880665], 162 | @"description": @"Ice cream in Dogpatch"}, 163 | @{@"search": [NSNull null], 164 | @"loc": @[@1.2792354,@103.8517178], 165 | @"description": @"(1.279, 103.852) - Singapore towers"}, 166 | @{@"search": @"Roppongi Hills Mori Tower Tokyo Japan", 167 | @"loc": [NSNull null], 168 | @"description": @"Mori Tower, Tokyo Japan"}]; 169 | 170 | self.locationPicker.dataSource = self; 171 | self.locationPicker.delegate = self; 172 | if (self.group == kLocationGroupStart) { 173 | self.instructionLabel.text = @"Pick a starting location"; 174 | } else { 175 | self.instructionLabel.text = @"Pick a destination"; 176 | } 177 | self.currentLocationSwitch.on = NO; 178 | self.currentLocationSwitch.hidden = !self.allowCurrentLocation; 179 | self.currentLocationLabel.hidden = !self.allowCurrentLocation; 180 | 181 | } 182 | 183 | 184 | 185 | @end 186 | 187 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // OpenInGoogleMapsSample 4 | // 5 | // Copyright 2014 Google Inc. All rights reserved. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import 20 | 21 | @interface ViewController : UIViewController 22 | 23 | @end 24 | 25 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // OpenInGoogleMapsSample 4 | // 5 | // Copyright 2014 Google Inc. All rights reserved. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import "Enums.h" 20 | #import "MapRequestModel.h" 21 | #import "OpenInGoogleMapsController.h" 22 | #import "PickLocationViewController.h" 23 | #import "ViewController.h" 24 | 25 | @interface ViewController () 26 | @property(weak, nonatomic) IBOutlet UILabel *startLabel; 27 | @property(weak, nonatomic) IBOutlet UILabel *endLabel; 28 | @property(weak, nonatomic) IBOutlet UISegmentedControl *pickMapTypeSC; 29 | @property(weak, nonatomic) IBOutlet UISwitch *satelliteSwitch; 30 | @property(weak, nonatomic) IBOutlet UISwitch *trafficSwitch; 31 | @property(weak, nonatomic) IBOutlet UISwitch *transitSwitch; 32 | @property(weak, nonatomic) IBOutlet UILabel *startLocationDescription; 33 | @property(weak, nonatomic) IBOutlet UIView *endLocationView; 34 | @property(weak, nonatomic) IBOutlet UILabel *endLocationDescription; 35 | @property(weak, nonatomic) IBOutlet UIButton *endLocationButton; 36 | @property(weak, nonatomic) IBOutlet UIView *mapFeaturesView; 37 | @property(weak, nonatomic) IBOutlet UIView *travelModeView; 38 | @property(weak, nonatomic) IBOutlet UIButton *travelMethodButton; 39 | @property(nonatomic, strong) MapRequestModel *model; 40 | @property(nonatomic, assign) LocationGroup pendingLocationGroup; 41 | @property(nonatomic, strong) UIActionSheet *travelModeActionSheet; 42 | @end 43 | 44 | // Don't forget that, in your own application, you'll need to register your URL scheme by 45 | // adding it in Info -> URL Types for your Xcode project. 46 | static NSString * const kOpenInMapsSampleURLScheme = @"OpenInGoogleMapsSample://"; 47 | 48 | @implementation ViewController 49 | #pragma mark - Relevant to openInMaps 50 | 51 | - (void)openMapInGoogleMaps { 52 | GoogleMapDefinition *mapDefinition = [[GoogleMapDefinition alloc] init]; 53 | mapDefinition.queryString = self.model.startQueryString; 54 | mapDefinition.center = self.model.startLocation; 55 | mapDefinition.viewOptions |= (self.satelliteSwitch.isOn) ? kGoogleMapsViewOptionSatellite : 0; 56 | mapDefinition.viewOptions |= (self.trafficSwitch.isOn) ? kGoogleMapsViewOptionTraffic : 0; 57 | mapDefinition.viewOptions |= (self.transitSwitch.isOn) ? kGoogleMapsViewOptionTransit : 0; 58 | if (mapDefinition.queryString && CLLocationCoordinate2DIsValid(mapDefinition.center)) { 59 | // Sets some reasonable bounds for the "Pizza near Times Square" types of maps 60 | mapDefinition.zoomLevel = 15.0f; 61 | } 62 | [[OpenInGoogleMapsController sharedInstance] openMap:mapDefinition]; 63 | 64 | } 65 | 66 | - (void)openDirectionsInGoogleMaps { 67 | GoogleDirectionsDefinition *directionsDefinition = [[GoogleDirectionsDefinition alloc] init]; 68 | if (self.model.startCurrentLocation) { 69 | directionsDefinition.startingPoint = nil; 70 | } else { 71 | GoogleDirectionsWaypoint *startingPoint = [[GoogleDirectionsWaypoint alloc] init]; 72 | startingPoint.queryString = self.model.startQueryString; 73 | startingPoint.location = self.model.startLocation; 74 | directionsDefinition.startingPoint = startingPoint; 75 | } 76 | if (self.model.destinationCurrentLocation) { 77 | directionsDefinition.destinationPoint = nil; 78 | } else { 79 | GoogleDirectionsWaypoint *destination = [[GoogleDirectionsWaypoint alloc] init]; 80 | destination.queryString = self.model.destinationQueryString; 81 | destination.location = self.model.desstinationLocation; 82 | directionsDefinition.destinationPoint = destination; 83 | } 84 | directionsDefinition.travelMode = [self travelModeAsGoogleMapsEnum:self.model.travelMode]; 85 | [[OpenInGoogleMapsController sharedInstance] openDirections:directionsDefinition]; 86 | } 87 | 88 | - (void)openStreetViewInGoogleMaps { 89 | GoogleStreetViewDefinition *streetViewDefinition = [[GoogleStreetViewDefinition alloc] init]; 90 | if (CLLocationCoordinate2DIsValid(self.model.startLocation)) { 91 | streetViewDefinition.center = self.model.startLocation; 92 | [[OpenInGoogleMapsController sharedInstance] openStreetView:streetViewDefinition]; 93 | } else { 94 | [self showSimpleAlertWithTitle:@"Please select a lat/long" 95 | description:@"To display a Street View location, you must define it by a " 96 | "lat / long"]; 97 | } 98 | } 99 | 100 | - (IBAction)openInMapsWasClicked:(id)sender { 101 | if (![[OpenInGoogleMapsController sharedInstance] isGoogleMapsInstalled]) { 102 | NSLog(@"Google Maps not installed, but using our fallback strategy"); 103 | } 104 | 105 | if (self.pickMapTypeSC.selectedSegmentIndex == 0) { 106 | [self openMapInGoogleMaps]; 107 | } else if (self.pickMapTypeSC.selectedSegmentIndex == 1) { 108 | [self openDirectionsInGoogleMaps]; 109 | } else if (self.pickMapTypeSC.selectedSegmentIndex == 2) { 110 | [self openStreetViewInGoogleMaps]; 111 | } 112 | } 113 | 114 | 115 | # pragma mark - Handle input and set locations 116 | 117 | 118 | 119 | - (IBAction)typeOfMapChanged:(UISegmentedControl *)sender { 120 | if (sender.selectedSegmentIndex == 0) { 121 | // We are displaying a map! 122 | self.startLabel.text = @"Location"; 123 | self.endLocationView.hidden = YES; 124 | self.mapFeaturesView.hidden = NO; 125 | self.travelModeView.hidden = YES; 126 | } else if (sender.selectedSegmentIndex == 1) { 127 | // We are asking for directions! 128 | self.startLabel.text = @"Start location"; 129 | self.endLocationView.hidden = NO; 130 | self.mapFeaturesView.hidden = YES; 131 | self.travelModeView.hidden = NO; 132 | } else if (sender.selectedSegmentIndex == 2) { 133 | // We are displaying street view! 134 | self.startLabel.text = @"Location"; 135 | self.endLocationView.hidden = YES; 136 | self.mapFeaturesView.hidden = YES; 137 | self.travelModeView.hidden = YES; 138 | } 139 | } 140 | 141 | - (void)updateTextStrings { 142 | self.startLocationDescription.text = [self.model descriptionForGroup:kLocationGroupStart]; 143 | self.endLocationDescription.text = [self.model descriptionForGroup:kLocationGroupEnd]; 144 | [self.travelMethodButton setTitle:[self.model travelModeDescription] 145 | forState:UIControlStateNormal]; 146 | } 147 | 148 | - (IBAction)editLocationWaspressed:(id)sender { 149 | self.pendingLocationGroup = ((UIButton *)sender).tag; 150 | [self performSegueWithIdentifier:@"segueToPickLocation" sender:self]; 151 | } 152 | 153 | - (IBAction)travelMethodButtonWasPressed:(id)sender { 154 | self.travelModeActionSheet = [[UIActionSheet alloc] initWithTitle:@"Select travel mode" 155 | delegate:self 156 | cancelButtonTitle:@"Cancel" 157 | destructiveButtonTitle:nil 158 | otherButtonTitles:nil]; 159 | NSArray *travelModeStrings = [self.model sortedTravelModeDescriptions]; 160 | for (NSString *buttonLabel in travelModeStrings) { 161 | [self.travelModeActionSheet addButtonWithTitle:buttonLabel]; 162 | } 163 | [self.travelModeActionSheet showInView:self.view]; 164 | } 165 | 166 | 167 | # pragma mark - PickLocationDelegate 168 | 169 | - (void)pickLocationController:(PickLocationViewController *)controller 170 | pickedQueryString:(NSString *)query 171 | location:(CLLocationCoordinate2D)location 172 | forGroup:(LocationGroup)group { 173 | [self.model setQueryString:query center:location forGroup:group]; 174 | [self updateTextStrings]; 175 | } 176 | 177 | - (void)pickLocationController:(PickLocationViewController *)controller 178 | pickedLocation:(CLLocationCoordinate2D)location 179 | forGroup:(LocationGroup)group { 180 | [self.model setQueryString:nil center:location forGroup:group]; 181 | [self updateTextStrings]; 182 | } 183 | 184 | - (void)pickLocationController:(PickLocationViewController *)controller 185 | pickedQueryString:(NSString *)query 186 | forGroup:(LocationGroup)group { 187 | [self.model setQueryString:query forGroup:group]; 188 | [self updateTextStrings]; 189 | } 190 | 191 | - (void)pickLocationController:(PickLocationViewController *)controller 192 | pickedCurrentLocationForGroup:(LocationGroup)group { 193 | [self.model useCurrentLocationForGroup:group]; 194 | [self updateTextStrings]; 195 | } 196 | 197 | - (void)noLocationPickedByPickLocationController:(PickLocationViewController *)controller { 198 | // Leave everything unchanged 199 | } 200 | 201 | # pragma ActionSheet delegate 202 | 203 | - (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex { 204 | if (buttonIndex == 0) { // Cancel 205 | return; 206 | } 207 | self.model.travelMode = buttonIndex - 1; 208 | [self updateTextStrings]; 209 | } 210 | 211 | 212 | # pragma mark - Miscellaneous helper methods 213 | 214 | // Our fancy new way of showing an alert! 215 | - (void)showSimpleAlertWithTitle:(NSString *)title description:(NSString *)description { 216 | if (NSClassFromString(@"UIAlertController")) { 217 | UIAlertAction *okay = 218 | [UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleDefault handler:nil]; 219 | UIAlertController *alert = 220 | [UIAlertController alertControllerWithTitle:title 221 | message:description 222 | preferredStyle:UIAlertControllerStyleAlert]; 223 | [alert addAction:okay]; 224 | [self presentViewController:alert animated:YES completion:nil]; 225 | } else { 226 | [[[UIAlertView alloc] initWithTitle:title 227 | message:description 228 | delegate:nil 229 | cancelButtonTitle:@"Okay" 230 | otherButtonTitles:nil] show]; 231 | } 232 | } 233 | 234 | // Hide the keyboard when touching outside the textfields 235 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 236 | [[self view] endEditing:YES]; 237 | } 238 | 239 | // Convert our app's "travel mode" to the official Google Enum 240 | - (GoogleMapsTravelMode)travelModeAsGoogleMapsEnum:(TravelMode)appTravelMode { 241 | switch (appTravelMode) { 242 | case kTravelModeBicycling: 243 | return kGoogleMapsTravelModeBiking; 244 | case kTravelModeDriving: 245 | return kGoogleMapsTravelModeDriving; 246 | case kTravelModePublicTransit: 247 | return kGoogleMapsTravelModeTransit; 248 | case kTravelModeWalking: 249 | return kGoogleMapsTravelModeWalking; 250 | case kTravelModeNotSpecified: 251 | return 0; 252 | } 253 | } 254 | 255 | # pragma mark - Life cycle 256 | 257 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 258 | if ([[segue destinationViewController] class] == [PickLocationViewController class]) { 259 | PickLocationViewController *destVC = [segue destinationViewController]; 260 | // Only let players set their current location if we're looking for directions 261 | destVC.allowCurrentLocation = (self.pickMapTypeSC.selectedSegmentIndex == 1); 262 | destVC.group = self.pendingLocationGroup; 263 | destVC.delegate = self; 264 | } 265 | } 266 | 267 | - (void)viewDidLoad { 268 | [super viewDidLoad]; 269 | self.model = [[MapRequestModel alloc] init]; 270 | // Add some default values 271 | [self pickLocationController:nil 272 | pickedQueryString:@"1600 Amphitheatre Parkway, Mountain View, CA 94043" 273 | forGroup:kLocationGroupStart]; 274 | [self typeOfMapChanged:self.pickMapTypeSC]; 275 | 276 | // And let's set our callback URL right away! 277 | [OpenInGoogleMapsController sharedInstance].callbackURL = 278 | [NSURL URLWithString:kOpenInMapsSampleURLScheme]; 279 | 280 | // If the user doesn't have Google Maps installed, let's try Chrome. And if they don't 281 | // have Chrome installed, let's use Apple Maps. This gives us the best chance of having an 282 | // x-callback-url that points back to our application. 283 | [OpenInGoogleMapsController sharedInstance].fallbackStrategy = 284 | kGoogleMapsFallbackChromeThenAppleMaps; 285 | } 286 | 287 | @end 288 | 289 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // OpenInGoogleMapsSample 4 | // 5 | // Created by Todd Kerpelman on 8/18/14. 6 | // Copyright (c) 2014 Example Company. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSampleTests/OpenInGoogleMapsSampleTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSampleTests/OpenInGoogleMapsSampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // OpenInGoogleMapsSampleTests.m 3 | // OpenInGoogleMapsSampleTests 4 | // 5 | // Created by Todd Kerpelman on 8/18/14. 6 | // Copyright (c) 2014 Example Company. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OpenInGoogleMapsSampleTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation OpenInGoogleMapsSampleTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /OpenInGoogleMapsSample/OpenInGoogleMapsSampleTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenInGoogleMaps-iOS is Deprecated 2 | 3 | This library was created to handle a the use case of opening the correct app to show a Map, Street View, or Directions. 4 | At [Google I/O 2017 we announced](https://www.youtube.com/watch?v=vLRutvtJwLg) a better, cross platform, approach to 5 | handling this requirement - [Maps URLs](https://developers.google.com/maps/documentation/urls/guide). 6 | 7 | Using Maps URLs, you can build a universal, cross-platform URL to launch Google Maps and perform searches, get directions 8 | and navigation, and display map views and panoramic images. The URL syntax is the same regardless of the platform in use. 9 | 10 | # OpenInGoogleMapsController 11 | 12 | The `OpenInGoogleMapsController` class is designed to make it easy for an iOS developer to open a map, show a Street View 13 | location, or show a set of directions directly in Google Maps. The class supports using the `x-callback-URL` standard so 14 | that you can add a "Back to _my app_" button directly within Google Maps, and supports a number of fallback strategies, so 15 | that you can automatically open the map in another application if the user does not have Google Maps installed. ![Analytics](https://maps-ga-beacon.appspot.com/UA-12846745-20/OpenInGoogleMaps-iOS/readme?pixel) 16 | 17 | ## About the Google Maps URL Scheme 18 | The `OpenInGoogleMapsController` class makes use of the Google Maps URL Scheme. If you want to understand how the class 19 | works under the hood, we highly recommend reading the [URL Scheme documentation](https://developers.google.com/maps/documentation/ios/urlscheme) first. 20 | 21 | ## Installing OpenInGoogleMapsController 22 | You can download the `OpenInGoogleMapsController` class, along with a sample app demonstrating its use, from the 23 | OpenInGoogleMaps [Github page](https://github.com/googlemaps/OpenInGoogleMaps-iOS). 24 | 25 | To add the class to your Xcode project, simply drag the `OpenInGoogleMapsController` .m and .h files into Xcode. Make sure 26 | you call `#import OpenInGoogleMapsController.h` where necessary. 27 | 28 | Alternately, if you want to use [CocoaPods](http://cocoapods.org) in your project, you can install the necessary files by 29 | adding the following line to your Podfile: 30 | 31 | pod "OpenInGoogleMaps" 32 | 33 | ### Running the Sample Application 34 | If you would like to try out the sample application, open `OpenInGoogleMapsSample.xcodeproj` in Xcode. You will probably 35 | want to run this on a real device, as the simulator does not have Google Maps installed. 36 | 37 | ## Using OpenInGoogleMapsController 38 | `OpenInGoogleMapsController` is a singleton class, which you can reference by calling the `sharedInstance` class method. 39 | 40 | [[OpenInGoogleMapsController sharedInstance] ] 41 | 42 | ## Supporting iOS 9 43 | In iOS 9.0, further restrictions were placed on the `canOpenURL` method -- for this method to work, you will need to add a 44 | `LSApplicationQueriesSchemes` array to your app's info.plist file, and then add items for each URL scheme you will want to 45 | query. For `OpenInGoogleMapsController`, these schemes as `comgooglemaps`, `comgooglemaps-x-callback`, and your own app's 46 | custom URL scheme. 47 | 48 | Please see the sample application for an example of this info.plist file in action. 49 | 50 | ## Adding a Callback URL 51 | 52 | Google Maps and Google Chrome both support the [x-callback-URL](http://x-callback-url.com/) specification, which allows you 53 | to easily add a "Back to _my app_" button in Google Maps. To add a callback url: 54 | 55 | 1. Within your Xcode project, select your target, and then select *Info -> URL Types* 56 | 2. Add a URLType for your application. This string should be unique for your app. Many developers choose to use their bundle identifier, without the periods (`comgooglemyapp`, for instance). 57 | 3. Set the `callbackURL` property in your `OpenInGoogleMapsController` class. 58 | 59 | NSString myURLScheme = @"comexamplemyapp://"; 60 | NSURL myCallbackURL = [NSURL URLWithString:myURLScheme]; 61 | [OpenInGoogleMapsController sharedInstance].callbackURL = myCallbackURL; 62 | 63 | When you open your maps now in Google Maps (or Google Chrome), you should see a button that redirects users back to your app 64 | when they're done viewing the map. 65 | 66 | You only need to set the callback URL once during the lifetime of your application and it will be used in all future 67 | `OpenInGoogleMapsController` requests. 68 | 69 | ## Fallback Strategies 70 | 71 | If the user does not have Google Maps installed, you can specify a number of fallback strategies for 72 | `OpenInGoogleMapsController` to try by setting the `fallbackStrategy` property. 73 | 74 | [OpenInGoogleMapsController sharedInstance].fallbackStrategy = 75 | kGoogleMapsFallbackChromeThenAppleMaps; 76 | 77 | The fallback strategies you can try are as follows: 78 | 79 | * `kGoogleMapsFallbackNone` = Do nothing and return `NO` if the user does not have Google Maps installed. This is the default. 80 | * `kGoogleMapsFallbackAppleMaps` = Open the map with Apple's Maps app instead. 81 | * `kGoogleMapsFallbackChromeThenSafari` = Open the map with Google Chrome if installed, otherwise 82 | open the map using Google Maps in Safari. 83 | * `kGoogleMapsFallbackChromeThenAppleMaps` = Open the map with Google Chrome if installed, otherwise 84 | use Apple's Maps app. 85 | * `kGoogleMapsFallbackSafari` = Open the map with Google Maps in Safari instead. 86 | 87 | If you have specified a callback URL, it will also be passed to Google Chrome. 88 | 89 | You only need to set the fallback strategy once during the lifetime of your application and it will be used in all future `OpenInGoogleMapsController` requests. 90 | 91 | ### Detecting if Google Maps is installed 92 | 93 | If you want to manually detect if Google Maps is installed, you can use the `isGoogleMapsInstalled` property. 94 | 95 | BOOL isGoogleMapsInstalled = [OpenInGoogleMapsController sharedInstance].isGoogleMapsInstalled; 96 | 97 | ## Opening a map 98 | 99 | Opening a map requires first creating a `GoogleMapDefinition` object to define the map you want opened. You can then pass the definition object to the `openMap` method. This method will return `YES` if it was able to open the map in some application, and `NO` if it was unable to open a map, either because you didn't define anything to search for, or your user does not have Google Maps installed and you did not specify a fallback strategy. 100 | 101 | GoogleMapDefinition *definition = [[GoogleMapDefinition alloc] init]; 102 | // Steps to define the definition. 103 | [[OpenInGoogleMapsController sharedInstance] openMap:definition]; 104 | 105 | 106 | ### GoogleMapDefinition 107 | 108 | The `GoogleMapDefinition` class includes several properties, some of which may be set to `nil`: 109 | 110 | * `(NSString *)queryString`: A query string which, if set, will be used to search for a place by name. 111 | * `(CLLocationCoordinate2D) center`: Defines the center of the map, in lat/long. If both this and the query string are specified, this will be used as a center for the search. To clear this value, set it to `kCLLocationCoordinate2DInvalid`. (Setting it to nil will specify a center of 0,0.) 112 | * `(GoogleMapsViewOptions) viewOptions`: A set of bitwise-ORed options that can be set on your map: 113 | * `kGoogleMapsViewOptionSatellite`: Shows a satellite view. 114 | * `kGoogleMapsViewOptionTraffic`: Shows traffic information. 115 | * `kGoogleMapsViewOptionTransit`: Shows transit information. 116 | * `float zoomLevel`: Defines the zoom level of the map. This can currently be any value from 0 to 21.0. 117 | 118 | Here's an example that opens up a map for "123 Main Street, Anytown, CA" with the "traffic" and "satellite" map layers turned on. 119 | 120 | GoogleMapDefinition *definition = [[GoogleMapDefinition alloc] init]; 121 | definition.queryString = @"123 Main Street, Anytown, CA"; 122 | definition.viewOptions = kGoogleMapsViewOptionSatellite | kGoogleMapsViewOptionTraffic; 123 | [[OpenInGoogleMapsController sharedInstance] openMap:definition]; 124 | 125 | ## Opening a street view location 126 | 127 | Opening a Street View location requires creating a `GoogleStreetViewDefinition` class to define the location you want to open. You can then pass this definition to the `openStreetView` method. This method will return `YES` if it was able to open the Street View request in some application, and `NO` if it was not, either because you didn't define a set of coordinates, or your user does not have Google Maps installed and you did not specify a fallback strategy. 128 | 129 | Note that a `YES` value does not actually guarantee the coordinates you specified were a valid Street View location. 130 | 131 | If your fallback strategy involves an app that does not support Street View, the `OpenInGoogleMapsController` class will open a zoomed-in satellite view on a map instead. 132 | 133 | Here's an example that opens up a Street View location near the Taj Mahal. 134 | 135 | GoogleStreetViewDefinition *definition = [[GoogleStreetViewDefinition alloc] init]; 136 | definition.center = CLLocationCoordinate2DMake(27.1724439,78.0420174); 137 | [[OpenInGoogleMapsController sharedInstance] openStreetView:definition]; 138 | 139 | 140 | ### GoogleStreetViewDefinition 141 | 142 | The `GoogleStreetViewDefinition` class includes one property: 143 | 144 | * `(CLLocationCoordinate2D) center`: Defines the Street View location, in lat/long. To clear this value, set it to `kCLLocationCoordinate2DInvalid`. (Setting it to nil will specify a center of 0,0.) 145 | 146 | ## Opening directions 147 | 148 | Opening a set of directions in Google Maps requires creating a `GoogleDirectionsDefinition` class to define the set of points you want to travel between. You can then pass this definition to the `openStreetView` method. This method will return `YES` if it was able to open the set of directions in some application, and `NO` if it was unable to, either because both your start and end point were empty, or your user does not have Google Maps installed and you did not specify a fallback strategy. 149 | 150 | Note that a `YES` value does not guarantee that Google Maps (or the fallback application) was able to find a set of directions between these two points: 151 | 152 | ### GoogleDirectionsWaypoint 153 | The `GoogleDirectionsDefinition` class uses the `GoogleDirectionsWaypoint` class to define its start and end points for a direction request. This class includes these properties: 154 | 155 | * `CLLocationCoordinate2D location`: Defines the location as a set of coordinates 156 | * `NSString *queryString`: Defines the location as a query string (such as an address) 157 | 158 | If both of these values are set, the location takes precedence over the query string. 159 | 160 | The `GoogleDirectionsWaypoint` class also has two class helper methods: `+ waypointWithQuery:(NSString *)queryString` and `+ waypointWithLocation:(CLLocationCoordinate2D)location` to easily construct waypoints. 161 | 162 | ### GoogleDirectionsDefinition 163 | 164 | The `GoogleDirectionsDefinition` class includes these properties: 165 | 166 | * `GoogleDirectionsWaypoint *startingPoint`: Defines the starting point. If this is set to `nil`, the directions will start at the user's current location. 167 | * `GoogleDirectionsWaypoint *destinationPoint`: Defines the destination. If this is set to `nil`, the directions will end at the user's current location. 168 | * `GoogleMapsTravelMode travelMode`: Defines how the user will get from the `startingPoint` to the `destinationPoint`. Current options are: 169 | * `kGoogleMapsTravelModeDriving` for driving. 170 | * `kGoogleMapsTravelModeTransit` for taking public transportation. 171 | * `kGoogleMapsTravelModeBiking` for biking. 172 | * `kGoogleMapsTravelModeWalking` for walking. 173 | 174 | 175 | The following example will help you plan your next burrito-centric road trip: 176 | 177 | GoogleDirectionsDefinition *definition = [[GoogleDirectionsDefinition alloc] init]; 178 | definition.startingPoint = [GoogleDirectionsWaypoint 179 | waypointWithQuery:@"La Taqueria, 2889 Mission St San Francisco, CA 94110"]; 180 | definition.destinationPoint = [GoogleDirectionsWaypoint 181 | waypointWithQuery:@"Delicious Mexican Eatery, 3314 Fort Blvd, El Paso, TX 79930"]; 182 | definition.travelMode = kGoogleMapsTravelModeDriving; 183 | [[OpenInGoogleMapsController sharedInstance] openDirections:definition]; 184 | 185 | The following example will give you biking directions from MI6 headquarters to Sherlock Holmes' address: 186 | 187 | GoogleDirectionsDefinition *definition = [[GoogleDirectionsDefinition alloc] init]; 188 | definition.startingPoint = [GoogleDirectionsWaypoint 189 | waypointWithLocation:CLLocationCoordinate2DMake(51.487242,-0.124402)]; 190 | definition.destinationPoint = [GoogleDirectionsWaypoint 191 | waypointWithQuery:@"221B Baker Street, London"]; 192 | definition.travelMode = kGoogleMapsTravelModeBiking; 193 | [[OpenInGoogleMapsController sharedInstance] openDirections:definition]; 194 | 195 | The following example will give you walking directions from your current location to the North American International Auto Show: 196 | 197 | GoogleDirectionsDefinition *definition = [[GoogleDirectionsDefinition alloc] init]; 198 | definition.startingPoint = nil; 199 | GoogleDirectionsWaypoint *destination = [[GoogleDirectionsWaypoint alloc] init]; 200 | destination.queryString = @"1 Washington Blvd, Detroit, MI 48226"; 201 | definition.destinationPoint = destination; 202 | definition.travelMode = kGoogleMapsTravelModeWalking; 203 | [[OpenInGoogleMapsController sharedInstance] openDirections:definition]; 204 | 205 | 206 | ## Reference Documentation 207 | You can find the reference documentation in the `Docs/html/` folder or 208 | [online](http://googlemaps.github.io/OpenInGoogleMaps-iOS/index.html). It makes for some 209 | thrilling late-night reading. 210 | 211 | 212 | # Special Thanks 213 | 214 | Special thanks go out to Ian Barber, Leo Hourvitz, and [Sam Thorogood](https://github.com/samthor), for thoroughly reviewing this code. Any remaining mistakes are [the author's](https://github.com/ToddKerpelman). 215 | 216 | Safari is a registered trademark of Apple Inc. 217 | 218 | Reference documentation was generated by [appledoc](http://gentlebytes.com/appledoc/). 219 | 220 | --------------------------------------------------------------------------------