在地图上选择当前地点并显示详细信息
本教程介绍了如何构建 iOS 应用来:
- 获取当前设备位置信息。
- 获取设备可能所处的位置列表。
- 提示用户进行最佳地点匹配。
- 在地图上显示标记。
请按照本教程中的说明操作,使用 Places SDK for iOS、Maps SDK for iOS 和 Apple Core Location 框架来构建 iOS 应用。
获取代码
从 GitHub 克隆或下载 Google Maps SDK for iOS。
< > 显示/隐藏 MapViewController.swift 的 Swift 代码。
/*
* Copyright 2016 Google Inc. All rights reserved.
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
import UIKit
import GoogleMaps
import GooglePlaces
class MapViewController: UIViewController {
var locationManager: CLLocationManager!
var currentLocation: CLLocation?
var mapView: GMSMapView!
var placesClient: GMSPlacesClient!
var preciseLocationZoomLevel: Float = 15.0
var approximateLocationZoomLevel: Float = 10.0
// An array to hold the list of likely places.
var likelyPlaces: [GMSPlace] = []
// The currently selected place.
var selectedPlace: GMSPlace?
// Update the map once the user has made their selection.
@IBAction func unwindToMain(segue: UIStoryboardSegue) {
// Clear the map.
mapView.clear()
// Add a marker to the map.
if let place = selectedPlace {
let marker = GMSMarker(position: place.coordinate)
marker.title = selectedPlace?.name
marker.snippet = selectedPlace?.formattedAddress
marker.map = mapView
}
listLikelyPlaces()
}
override func viewDidLoad() {
super.viewDidLoad()
// Initialize the location manager.
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.distanceFilter = 50
locationManager.startUpdatingLocation()
locationManager.delegate = self
placesClient = GMSPlacesClient.shared()
// A default location to use when location permission is not granted.
let defaultLocation = CLLocation(latitude: -33.869405, longitude: 151.199)
// Create a map.
let zoomLevel = locationManager.accuracyAuthorization == .fullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel
let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude,
longitude: defaultLocation.coordinate.longitude,
zoom: zoomLevel)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
mapView.settings.myLocationButton = true
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.isMyLocationEnabled = true
// Add the map to the view, hide it until we've got a location update.
view.addSubview(mapView)
mapView.isHidden = true
listLikelyPlaces()
}
// Populate the array with the list of likely places.
func listLikelyPlaces() {
// Clean up from previous sessions.
likelyPlaces.removeAll()
let placeFields: GMSPlaceField = [.name, .coordinate]
placesClient.findPlaceLikelihoodsFromCurrentLocation(withPlaceFields: placeFields) { (placeLikelihoods, error) in
guard error == nil else {
// TODO: Handle the error.
print("Current Place error: \(error!.localizedDescription)")
return
}
guard let placeLikelihoods = placeLikelihoods else {
print("No places found.")
return
}
// Get likely places and add to the list.
for likelihood in placeLikelihoods {
let place = likelihood.place
self.likelyPlaces.append(place)
}
}
}
// Prepare the segue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueToSelect" {
if let nextViewController = segue.destination as? PlacesViewController {
nextViewController.likelyPlaces = likelyPlaces
}
}
}
}
// Delegates to handle events for the location manager.
extension MapViewController: CLLocationManagerDelegate {
// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
print("Location: \(location)")
let zoomLevel = locationManager.accuracyAuthorization == .fullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel
let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude,
longitude: location.coordinate.longitude,
zoom: zoomLevel)
if mapView.isHidden {
mapView.isHidden = false
mapView.camera = camera
} else {
mapView.animate(to: camera)
}
listLikelyPlaces()
}
// Handle authorization for the location manager.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// Check accuracy authorization
let accuracy = manager.accuracyAuthorization
switch accuracy {
case .fullAccuracy:
print("Location accuracy is precise.")
case .reducedAccuracy:
print("Location accuracy is not precise.")
@unknown default:
fatalError()
}
// Handle authorization status
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
// Display the map using the default location.
mapView.isHidden = false
case .notDetermined:
print("Location status not determined.")
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("Location status is OK.")
@unknown default:
fatalError()
}
}
// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error)")
}
}
< > 显示/隐藏 MapViewController.m 的 Objective-C 代码。
//
// MapsViewController.m
// current-place-on-map
//
// Created by Chris Arriola on 9/18/20.
// Copyright © 2020 William French. All rights reserved.
//
#import "MapViewController.h"
#import "PlacesViewController.h"
@import CoreLocation;
@import GooglePlaces;
@import GoogleMaps;
@interface MapViewController () <CLLocationManagerDelegate>
@end
@implementation MapViewController {
CLLocationManager *locationManager;
CLLocation * _Nullable currentLocation;
GMSMapView *mapView;
GMSPlacesClient *placesClient;
float preciseLocationZoomLevel;
float approximateLocationZoomLevel;
// An array to hold the list of likely places.
NSMutableArray<GMSPlace *> *likelyPlaces;
// The currently selected place.
GMSPlace * _Nullable selectedPlace;
}
- (void)viewDidLoad {
[super viewDidLoad];
preciseLocationZoomLevel = 15.0;
approximateLocationZoomLevel = 15.0;
// Initialize the location manager.
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager requestWhenInUseAuthorization];
locationManager.distanceFilter = 50;
[locationManager startUpdatingLocation];
locationManager.delegate = self;
placesClient = [GMSPlacesClient sharedClient];
// A default location to use when location permission is not granted.
CLLocationCoordinate2D defaultLocation = CLLocationCoordinate2DMake(-33.869405, 151.199);
// Create a map.
float zoomLevel = locationManager.accuracyAuthorization == CLAccuracyAuthorizationFullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel;
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:defaultLocation.latitude
longitude:defaultLocation.longitude
zoom:zoomLevel];
mapView = [GMSMapView mapWithFrame:self.view.bounds camera:camera];
mapView.settings.myLocationButton = YES;
mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
mapView.myLocationEnabled = YES;
// Add the map to the view, hide it until we've got a location update.
[self.view addSubview:mapView];
mapView.hidden = YES;
[self listLikelyPlaces];
}
// Populate the array with the list of likely places.
- (void) listLikelyPlaces
{
// Clean up from previous sessions.
likelyPlaces = [NSMutableArray array];
GMSPlaceField placeFields = GMSPlaceFieldName | GMSPlaceFieldCoordinate;
[placesClient findPlaceLikelihoodsFromCurrentLocationWithPlaceFields:placeFields callback:^(NSArray<GMSPlaceLikelihood *> * _Nullable likelihoods, NSError * _Nullable error) {
if (error != nil) {
// TODO: Handle the error.
NSLog(@"Current Place error: %@", error.localizedDescription);
return;
}
if (likelihoods == nil) {
NSLog(@"No places found.");
return;
}
for (GMSPlaceLikelihood *likelihood in likelihoods) {
GMSPlace *place = likelihood.place;
[likelyPlaces addObject:place];
}
}];
}
// Update the map once the user has made their selection.
- (void) unwindToMain:(UIStoryboardSegue *)segue
{
// Clear the map.
[mapView clear];
// Add a marker to the map.
if (selectedPlace != nil) {
GMSMarker *marker = [GMSMarker markerWithPosition:selectedPlace.coordinate];
marker.title = selectedPlace.name;
marker.snippet = selectedPlace.formattedAddress;
marker.map = mapView;
}
[self listLikelyPlaces];
}
// Prepare the segue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"segueToSelect"]) {
if ([segue.destinationViewController isKindOfClass:[PlacesViewController class]]) {
PlacesViewController *placesViewController = (PlacesViewController *)segue.destinationViewController;
placesViewController.likelyPlaces = likelyPlaces;
}
}
}
// Delegates to handle events for the location manager.
#pragma mark - CLLocationManagerDelegate
// Handle incoming location events.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
CLLocation *location = locations.lastObject;
NSLog(@"Location: %@", location);
float zoomLevel = locationManager.accuracyAuthorization == CLAccuracyAuthorizationFullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel;
GMSCameraPosition * camera = [GMSCameraPosition cameraWithLatitude:location.coordinate.latitude
longitude:location.coordinate.longitude
zoom:zoomLevel];
if (mapView.isHidden) {
mapView.hidden = NO;
mapView.camera = camera;
} else {
[mapView animateToCameraPosition:camera];
}
[self listLikelyPlaces];
}
// Handle authorization for the location manager.
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
// Check accuracy authorization
CLAccuracyAuthorization accuracy = manager.accuracyAuthorization;
switch (accuracy) {
case CLAccuracyAuthorizationFullAccuracy:
NSLog(@"Location accuracy is precise.");
break;
case CLAccuracyAuthorizationReducedAccuracy:
NSLog(@"Location accuracy is not precise.");
break;
}
// Handle authorization status
switch (status) {
case kCLAuthorizationStatusRestricted:
NSLog(@"Location access was restricted.");
break;
case kCLAuthorizationStatusDenied:
NSLog(@"User denied access to location.");
// Display the map using the default location.
mapView.hidden = NO;
case kCLAuthorizationStatusNotDetermined:
NSLog(@"Location status not determined.");
case kCLAuthorizationStatusAuthorizedAlways:
case kCLAuthorizationStatusAuthorizedWhenInUse:
NSLog(@"Location status is OK.");
}
}
// Handle location manager errors.
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
[manager stopUpdatingLocation];
NSLog(@"Error: %@", error.localizedDescription);
}
@end
< > 显示/隐藏 PlacesViewController.swift 的 Swift 代码。
/*
* Copyright 2017 Google Inc. All rights reserved.
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
import UIKit
import GooglePlaces
class PlacesViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
// An array to hold the list of possible locations.
var likelyPlaces: [GMSPlace] = []
var selectedPlace: GMSPlace?
// Cell reuse id (cells that scroll out of view can be reused).
let cellReuseIdentifier = "cell"
override func viewDidLoad() {
super.viewDidLoad()
// Register the table view cell class and its reuse id.
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
// This view controller provides delegate methods and row data for the table view.
tableView.delegate = self
tableView.dataSource = self
tableView.reloadData()
}
// Pass the selected place to the new view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "unwindToMain" {
if let nextViewController = segue.destination as? MapViewController {
nextViewController.selectedPlace = selectedPlace
}
}
}
}
// Respond when a user selects a place.
extension PlacesViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedPlace = likelyPlaces[indexPath.row]
performSegue(withIdentifier: "unwindToMain", sender: self)
}
// Adjust cell height to only show the first five items in the table
// (scrolling is disabled in IB).
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.tableView.frame.size.height/5
}
// Make table rows display at proper height if there are less than 5 items.
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
if (section == tableView.numberOfSections - 1) {
return 1
}
return 0
}
}
// Populate the table with the list of most likely places.
extension PlacesViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return likelyPlaces.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath)
let collectionItem = likelyPlaces[indexPath.row]
cell.textLabel?.text = collectionItem.name
return cell
}
}
< > 显示/隐藏 PlacesViewController.m 的 Objective-C 代码。
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "PlacesViewController.h"
@interface PlacesViewController () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, weak) UITableView *tableView;
@end
@implementation PlacesViewController {
// Cell reuse id (cells that scroll out of view can be reused).
NSString *cellReuseIdentifier;
}
- (void)viewDidLoad {
[super viewDidLoad];
cellReuseIdentifier = @"cell";
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
}
#pragma mark - UITableViewDelegate
// Respond when a user selects a place.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedPlace = [self.likelyPlaces objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:@"unwindToMain" sender:self];
}
// Adjust cell height to only show the first five items in the table
// (scrolling is disabled in IB).
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return self.tableView.frame.size.height/5;
}
// Make table rows display at proper height if there are less than 5 items.
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
if (section == tableView.numberOfSections - 1) {
return 1;
}
return 0;
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.likelyPlaces.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [tableView dequeueReusableCellWithIdentifier:cellReuseIdentifier forIndexPath:indexPath];
}
@end
设置开发项目

请按照以下步骤安装 Places SDK for iOS 和 Maps SDK for iOS:
- 下载并安装 Xcode 13.0 版或更高版本。
- 如果您还没有 CocoaPods,请在 macOS 上从终端运行以下命令进行安装:
sudo gem install cocoapods
- 在保存示例代码库的目录中(获取代码),导航到
tutorials/current-place-on-map
目录。
- 运行
pod install
命令。此操作会安装 Podfile
中指定的 API 及其可能具有的任何依赖项。
- 打开(双击)项目的 current-place-on-map.xcworkspace 以在 Xcode 中将其打开。您必须使用
.xcworkspace
文件打开项目。
如需了解详细的安装说明,请参阅使用入门(地图)和使用入门(地点)。
启用必要的 API 并获取 API 密钥
如需完成本教程,您需要一个已获得 Maps SDK for iOS 和 Places API 使用授权的 Google API 密钥。
- 按照 Google Maps Platform 使用入门中的说明,设置结算帐号和同时启用这两种产品的项目。
- 按照获取 API 密钥中的说明,为您之前设置的开发项目创建 API 密钥。
将 API 密钥添加到您的应用
Swift
按照以下方法向 AppDelegate.swift
添加 API 密钥:
- 请注意,以下 import 语句已添加到文件中:
import GooglePlaces
import GoogleMaps
- 在
application(_:didFinishLaunchingWithOptions:)
方法中修改以下代码行,将 YOUR_API_KEY 替换为您的 API 密钥:
GMSPlacesClient.provideAPIKey("YOUR_API_KEY")
GMSServices.provideAPIKey("YOUR_API_KEY")
Objective-C
按照以下方法向 AppDelegate.m
添加 API 密钥:
- 请注意,以下 import 语句已添加到文件中:
@import GooglePlaces;
@import GoogleMaps;
- 在
application(_:didFinishLaunchingWithOptions:)
方法中修改以下代码行,将 YOUR_API_KEY 替换为您的 API 密钥:
[GMSPlacesClient provideAPIKey: @"YOUR_API_KEY"]
[GMSServices provideAPIKey: @"YOUR_API_KEY"]
构建和运行您的应用
- 将 iOS 设备连接到您的计算机,或者从 Xcode 的方案弹出式菜单中选择模拟器。
- 如果您使用的是设备,请确保位置信息服务已启用。
如果您使用的是模拟器,请从功能菜单中选择一个位置。
- 在 Xcode 中,点击 Product/Run 菜单选项(或 play 按钮图标)。
Xcode 构建应用,然后在设备或模拟器上运行该应用。
您将看到一张地图,该地图会以您当前所在位置为中心显示许多标记,与本页上的图像类似。
问题排查:
- 如果您没有看到地图,请检查您是否已获取 API 密钥,以及是否已将其添加到应用,如上文所述。查看 Xcode 的调试控制台,查找有关 API 密钥的错误消息。
- 如果您已按 iOS 软件包标识符限制了 API 密钥,请修改密钥以为应用添加软件包标识符:
com.google.examples.current-place-on-map
。
- 如果针对位置服务的权限请求遭拒,地图将无法正确显示。
- 如果您使用的是设备,请转到设置/常规/隐私/位置信息服务,然后重新启用位置信息服务。
- 如果您使用的是模拟器,请转到模拟器/重置内容和设置...
下次运行应用时,请务必接受位置信息服务提示。
- 确保 Wi-Fi 或 GPS 连接良好。
- 如果应用启动但未显示任何地图,请确保您已使用适当的位置权限更新项目的 Info.plist。如需详细了解权限处理,请参阅下文中的在应用中请求位置信息权限指南。
- 使用 Xcode 调试工具查看日志并调试应用。
了解代码
本部分教程介绍了当前地图位置应用中最重要的部分,以帮助您了解如何构建类似的应用。
当前地图上的地点应用具有两个视图控制器:一个用于显示显示用户当前所选地点的地图,另一个用于向用户显示可供选择的地点列表。请注意,每个视图控制器具有相同的变量,分别用于跟踪可能的地点列表 (likelyPlaces
) 和指示用户的选择 (selectedPlace
)。视图之间的导航是通过使用序列实现的。
请求位置权限
您的应用必须提示用户同意使用位置信息服务。为此,请在应用的 Info.plist
文件中添加 NSLocationAlwaysUsageDescription
键,并将每个键的值设置为描述应用打算如何使用位置数据的字符串。
设置营业地点管理员
使用 CLLocationManager 查找设备的当前位置,并在设备移动到新位置时请求定期更新。本教程提供了获取设备位置信息所需的代码。如需了解详情,请参阅 Apple 开发者文档中的获取用户位置指南。
- 在类级别声明位置管理器、当前位置、地图视图、地点客户端和默认缩放级别。
Swift
var locationManager: CLLocationManager!
var currentLocation: CLLocation?
var mapView: GMSMapView!
var placesClient: GMSPlacesClient!
var preciseLocationZoomLevel: Float = 15.0
var approximateLocationZoomLevel: Float = 10.0
Objective-C
CLLocationManager *locationManager;
CLLocation * _Nullable currentLocation;
GMSMapView *mapView;
GMSPlacesClient *placesClient;
float preciseLocationZoomLevel;
float approximateLocationZoomLevel;
- 在
viewDidLoad()
中初始化营业地点管理器和 GMSPlacesClient
。
Swift
// Initialize the location manager.
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.distanceFilter = 50
locationManager.startUpdatingLocation()
locationManager.delegate = self
placesClient = GMSPlacesClient.shared()
Objective-C
// Initialize the location manager.
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager requestWhenInUseAuthorization];
locationManager.distanceFilter = 50;
[locationManager startUpdatingLocation];
locationManager.delegate = self;
placesClient = [GMSPlacesClient sharedClient];
- 声明变量以存储可能地点的列表以及用户选择的地点。
Swift
// An array to hold the list of likely places.
var likelyPlaces: [GMSPlace] = []
// The currently selected place.
var selectedPlace: GMSPlace?
Objective-C
// An array to hold the list of likely places.
NSMutableArray<GMSPlace *> *likelyPlaces;
// The currently selected place.
GMSPlace * _Nullable selectedPlace;
- 添加扩展,以便使用扩展子句处理营业地点管理器的事件。
Swift
// Delegates to handle events for the location manager.
extension MapViewController: CLLocationManagerDelegate {
// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
print("Location: \(location)")
let zoomLevel = locationManager.accuracyAuthorization == .fullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel
let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude,
longitude: location.coordinate.longitude,
zoom: zoomLevel)
if mapView.isHidden {
mapView.isHidden = false
mapView.camera = camera
} else {
mapView.animate(to: camera)
}
listLikelyPlaces()
}
// Handle authorization for the location manager.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// Check accuracy authorization
let accuracy = manager.accuracyAuthorization
switch accuracy {
case .fullAccuracy:
print("Location accuracy is precise.")
case .reducedAccuracy:
print("Location accuracy is not precise.")
@unknown default:
fatalError()
}
// Handle authorization status
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
// Display the map using the default location.
mapView.isHidden = false
case .notDetermined:
print("Location status not determined.")
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("Location status is OK.")
@unknown default:
fatalError()
}
}
// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error)")
}
}
Objective-C
// Delegates to handle events for the location manager.
#pragma mark - CLLocationManagerDelegate
// Handle incoming location events.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
CLLocation *location = locations.lastObject;
NSLog(@"Location: %@", location);
float zoomLevel = locationManager.accuracyAuthorization == CLAccuracyAuthorizationFullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel;
GMSCameraPosition * camera = [GMSCameraPosition cameraWithLatitude:location.coordinate.latitude
longitude:location.coordinate.longitude
zoom:zoomLevel];
if (mapView.isHidden) {
mapView.hidden = NO;
mapView.camera = camera;
} else {
[mapView animateToCameraPosition:camera];
}
[self listLikelyPlaces];
}
// Handle authorization for the location manager.
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
// Check accuracy authorization
CLAccuracyAuthorization accuracy = manager.accuracyAuthorization;
switch (accuracy) {
case CLAccuracyAuthorizationFullAccuracy:
NSLog(@"Location accuracy is precise.");
break;
case CLAccuracyAuthorizationReducedAccuracy:
NSLog(@"Location accuracy is not precise.");
break;
}
// Handle authorization status
switch (status) {
case kCLAuthorizationStatusRestricted:
NSLog(@"Location access was restricted.");
break;
case kCLAuthorizationStatusDenied:
NSLog(@"User denied access to location.");
// Display the map using the default location.
mapView.hidden = NO;
case kCLAuthorizationStatusNotDetermined:
NSLog(@"Location status not determined.");
case kCLAuthorizationStatusAuthorizedAlways:
case kCLAuthorizationStatusAuthorizedWhenInUse:
NSLog(@"Location status is OK.");
}
}
// Handle location manager errors.
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
[manager stopUpdatingLocation];
NSLog(@"Error: %@", error.localizedDescription);
}
添加地图
创建地图,并将其添加到主视图控制器的 viewDidLoad()
视图中。在接收到位置信息更新之前,地图将保持隐藏状态(位置信息更新在 CLLocationManagerDelegate
扩展中进行处理)。
Swift
// A default location to use when location permission is not granted.
let defaultLocation = CLLocation(latitude: -33.869405, longitude: 151.199)
// Create a map.
let zoomLevel = locationManager.accuracyAuthorization == .fullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel
let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude,
longitude: defaultLocation.coordinate.longitude,
zoom: zoomLevel)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
mapView.settings.myLocationButton = true
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.isMyLocationEnabled = true
// Add the map to the view, hide it until we've got a location update.
view.addSubview(mapView)
mapView.isHidden = true
Objective-C
// A default location to use when location permission is not granted.
CLLocationCoordinate2D defaultLocation = CLLocationCoordinate2DMake(-33.869405, 151.199);
// Create a map.
float zoomLevel = locationManager.accuracyAuthorization == CLAccuracyAuthorizationFullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel;
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:defaultLocation.latitude
longitude:defaultLocation.longitude
zoom:zoomLevel];
mapView = [GMSMapView mapWithFrame:self.view.bounds camera:camera];
mapView.settings.myLocationButton = YES;
mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
mapView.myLocationEnabled = YES;
// Add the map to the view, hide it until we've got a location update.
[self.view addSubview:mapView];
mapView.hidden = YES;
提示用户选择他们当前的地点
使用 Places SDK for iOS 获取基于用户当前位置的五大地点可能性,并将列表呈现在 UITableView
中。用户选择地点后,向地图添加标记。
- 获取用于填充
UITableView
的地点列表,用户可以在其中选择他们当前所在的位置。
Swift
// Populate the array with the list of likely places.
func listLikelyPlaces() {
// Clean up from previous sessions.
likelyPlaces.removeAll()
let placeFields: GMSPlaceField = [.name, .coordinate]
placesClient.findPlaceLikelihoodsFromCurrentLocation(withPlaceFields: placeFields) { (placeLikelihoods, error) in
guard error == nil else {
// TODO: Handle the error.
print("Current Place error: \(error!.localizedDescription)")
return
}
guard let placeLikelihoods = placeLikelihoods else {
print("No places found.")
return
}
// Get likely places and add to the list.
for likelihood in placeLikelihoods {
let place = likelihood.place
self.likelyPlaces.append(place)
}
}
}
Objective-C
// Populate the array with the list of likely places.
- (void) listLikelyPlaces
{
// Clean up from previous sessions.
likelyPlaces = [NSMutableArray array];
GMSPlaceField placeFields = GMSPlaceFieldName | GMSPlaceFieldCoordinate;
[placesClient findPlaceLikelihoodsFromCurrentLocationWithPlaceFields:placeFields callback:^(NSArray<GMSPlaceLikelihood *> * _Nullable likelihoods, NSError * _Nullable error) {
if (error != nil) {
// TODO: Handle the error.
NSLog(@"Current Place error: %@", error.localizedDescription);
return;
}
if (likelihoods == nil) {
NSLog(@"No places found.");
return;
}
for (GMSPlaceLikelihood *likelihood in likelihoods) {
GMSPlace *place = likelihood.place;
[likelyPlaces addObject:place];
}
}];
}
- 打开一个新视图,以向用户显示可能的地点。当用户点按“获取地点”时,我们将跳转到新的视图,并向用户显示可供选择的地点列表。
prepare
函数会使用当前可能地点的列表更新 PlacesViewController
,并在执行分块时自动调用。
Swift
// Prepare the segue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueToSelect" {
if let nextViewController = segue.destination as? PlacesViewController {
nextViewController.likelyPlaces = likelyPlaces
}
}
}
Objective-C
// Prepare the segue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"segueToSelect"]) {
if ([segue.destinationViewController isKindOfClass:[PlacesViewController class]]) {
PlacesViewController *placesViewController = (PlacesViewController *)segue.destinationViewController;
placesViewController.likelyPlaces = likelyPlaces;
}
}
}
- 在
PlacesViewController
中,通过 UITableViewDataSource
代理扩展使用最有可能的地点列表来填充表。
Swift
// Populate the table with the list of most likely places.
extension PlacesViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return likelyPlaces.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath)
let collectionItem = likelyPlaces[indexPath.row]
cell.textLabel?.text = collectionItem.name
return cell
}
}
Objective-C
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.likelyPlaces.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [tableView dequeueReusableCellWithIdentifier:cellReuseIdentifier forIndexPath:indexPath];
}
@end
- 使用
UITableViewDelegate
委托扩展程序处理用户的选择。
Swift
class PlacesViewController: UIViewController {
// ...
// Pass the selected place to the new view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "unwindToMain" {
if let nextViewController = segue.destination as? MapViewController {
nextViewController.selectedPlace = selectedPlace
}
}
}
}
// Respond when a user selects a place.
extension PlacesViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedPlace = likelyPlaces[indexPath.row]
performSegue(withIdentifier: "unwindToMain", sender: self)
}
// Adjust cell height to only show the first five items in the table
// (scrolling is disabled in IB).
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.tableView.frame.size.height/5
}
// Make table rows display at proper height if there are less than 5 items.
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
if (section == tableView.numberOfSections - 1) {
return 1
}
return 0
}
}
Objective-C
@interface PlacesViewController () <UITableViewDataSource, UITableViewDelegate>
// ...
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
}
#pragma mark - UITableViewDelegate
// Respond when a user selects a place.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedPlace = [self.likelyPlaces objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:@"unwindToMain" sender:self];
}
// Adjust cell height to only show the first five items in the table
// (scrolling is disabled in IB).
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return self.tableView.frame.size.height/5;
}
// Make table rows display at proper height if there are less than 5 items.
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
if (section == tableView.numberOfSections - 1) {
return 1;
}
return 0;
}
向地图添加标记
当用户做出选择时,使用展开细分可返回上一个视图,然后向地图添加标记。返回主视图控制器后,系统会自动调用 unwindToMain
IBAction。
Swift
// Update the map once the user has made their selection.
@IBAction func unwindToMain(segue: UIStoryboardSegue) {
// Clear the map.
mapView.clear()
// Add a marker to the map.
if let place = selectedPlace {
let marker = GMSMarker(position: place.coordinate)
marker.title = selectedPlace?.name
marker.snippet = selectedPlace?.formattedAddress
marker.map = mapView
}
listLikelyPlaces()
}
Objective-C
// Update the map once the user has made their selection.
- (void) unwindToMain:(UIStoryboardSegue *)segue
{
// Clear the map.
[mapView clear];
// Add a marker to the map.
if (selectedPlace != nil) {
GMSMarker *marker = [GMSMarker markerWithPosition:selectedPlace.coordinate];
marker.title = selectedPlace.name;
marker.snippet = selectedPlace.formattedAddress;
marker.map = mapView;
}
[self listLikelyPlaces];
}
恭喜!您构建了一个 iOS 应用,可让用户选择他们当前的地点,并在 Google 地图上显示结果。在学习本课程的过程中,您学习了如何使用 Places SDK for iOS、Maps SDK for iOS 和 Apple Core Location 框架。