在地圖上選取目前位置並顯示相關詳細資料

本教學課程示範如何建構可擷取裝置目前位置的 iOS 應用程式。 識別可能的位置,提示使用者選取最符合的項目,並顯示地圖標記 字詞位置

適合有 Swift 或 Objective-C 且初級或中級知識的人士, 大致瞭解 Xcode如需進階指南 建立地圖後,請參閱 開發人員指南

您會使用本教學課程建立下列地圖。地圖標記的位置為臺北 但將移至位於加州舊金山,但會移至裝置或模擬器所在的任何地方。

本教學課程使用 Places SDK for iOSMaps SDK for iOS, 和 Apple Core 位置資訊架構

取得程式碼

複製或下載 GitHub 的 Google 地圖 iOS 範例存放區

您也可以按一下下列按鈕下載原始碼:

提供程式碼

MapViewController

Swift

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)")
  }
}
      

Objective-C

#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

import UIKit
import GooglePlaces

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
  }
}

// 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

#import "PlacesViewController.h"

@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;
}

#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
      

開始使用

Swift 套件管理工具

您可以使用 Swift Package Manager 安裝 Maps SDK for iOS。

  1. 確認您已移除所有現有的 Maps SDK for iOS 依附元件。
  2. 開啟終端機視窗,然後前往 tutorials/current-place-on-map 目錄。
  3. 確認 Xcode 工作區已關閉,並執行下列指令:
    sudo gem install cocoapods-deintegrate cocoapods-clean
    pod deintegrate
    pod cache clean --all
    rm Podfile
    rm current-place-on-map.xcworkspace
  4. 開啟 Xcode 專案並刪除 Podfile。
  5. 新增 Places SDK 和 Maps SDK:
    1. 前往「檔案」>新增套件依附元件
    2. 輸入 https://github.com/googlemaps/ios-places-sdk 做為網址,請按下 Enter 來提取套件,然後按一下 Add Package
    3. 輸入 https://github.com/googlemaps/ios-maps-sdk 做為網址,請按下 Enter 來提取套件,然後按一下 Add Package
  6. 您可能需要使用 [檔案] > [檔案] 來重設套件快取套裝行程 >重設套件快取

使用 CocoaPods

  1. 下載並安裝 Xcode 15.0 以上版本。
  2. 如果您尚未安裝 CocoaPods, 從終端機執行下列指令,以便在 macOS 上安裝:
    sudo gem install cocoapods
  3. 前往 tutorials/current-place-on-map 目錄。
  4. 執行 pod install 指令。這會安裝 Podfile 中指定的 Maps SDK 和 Places SDK,以及所有依附元件。
  5. 執行 pod outdated,將已安裝的 Pod 版本與任何新的更新進行比較。如果偵測到新版本,請執行 pod update 以更新 Podfile 並安裝最新的 SDK。詳情請參閱 CocoaPods 指南
  6. 開啟 (按兩下) 專案的 current-place-on-map.xcworkspace 檔案即可在 Xcode 中開啟您必須使用 .xcworkspace 檔案才能開啟專案。

取得 API 金鑰並啟用必要的 API

如想完成本教學課程,請取得可授予此角色的 Google API 金鑰 使用 Maps SDK for iOSPlaces API

  1. 按照操作說明開始使用 Google 地圖平台,設定帳單帳戶和同時啟用這兩項產品的專案。
  2. 按照取得 API 金鑰中的操作說明,為您先前設定的開發專案建立 API 金鑰。

Add the API key to your application

將 API 金鑰新增到您的 AppDelegate.swift 中,如下所示:

  1. 請注意,檔案已新增下列匯入陳述式:
    import GooglePlaces
    import GoogleMaps
  2. application(_:didFinishLaunchingWithOptions:) 中編輯下列程式碼 方法,請將 YOUR_API_KEY 換成您的 API 金鑰:
    GMSPlacesClient.provideAPIKey("YOUR_API_KEY")
    GMSServices.provideAPIKey("YOUR_API_KEY")
,瞭解如何調查及移除這項存取權。

建構並執行應用程式

  1. 將 iOS 裝置連接到電腦,或選取 模擬工具
  2. 如果你使用裝置,請確認定位服務已啟用。 如果您使用模擬器,請從「Features」(地圖項目) 中選取位置 或前往 Google 試算表選單
  3. 在 Xcode 中,按一下「Product/Run」選單選項 (或播放 按鈕圖示)。
    • Xcode 會建構應用程式,然後在裝置或 模擬工具
    • 您應該會看到一份地圖,上面有數個以您目前位置為中心的標記 或 HTTP/HTTPS 位置

疑難排解:

  • 如未看到地圖,請檢查是否已取得 API 金鑰並新增 按照上述步驟,將專案連結至應用程式。確認 Xcode 的偵錯主控台,顯示關於 API 金鑰的錯誤訊息。
  • 如果您已使用 iOS 軟體包 ID 限制 API 金鑰,請編輯金鑰,加入應用程式的軟體包 ID:com.google.examples.current-place-on-map
  • 如果權限要求 ,地圖將無法正確顯示 定位服務遭拒。
    • 如果您使用裝置,請前往 設定/一般/隱私權/位置 服務,以及重新啟用定位服務。
    • 如果你使用模擬器,請前往「Simulator/Reset Content」 設定...
    ,瞭解如何調查及移除這項存取權。 應用程式下次執行時,請務必接受定位服務提示。
  • 確認您的 Wi-Fi 或 GPS 連線品質良好。
  • 如果應用程式啟動後未顯示地圖,請確認您是否 已用適當位置更新專案的 Info.plist 授予其要求的權限。如要進一步瞭解權限處理方式,請參閱相關指南 在應用程式中要求位置存取權
  • 使用 Xcode 偵錯工具 查看記錄檔並對應用程式偵錯。

瞭解程式碼

本教學課程這一段將說明 current-place-on-map 應用程式則會協助您瞭解如何

current-place-on-map 應用程式具有兩個檢視控制器: 其中一個 顯示地圖 (顯示使用者目前選取的地點),另外一個 向使用者推薦可能的地點清單。請注意, 每個檢視控制器都具有相同的變數,用於追蹤 可能地點 (likelyPlaces),並用於表示使用者的 選項 (selectedPlace)。如何切換檢視畫面為 方法是使用 segues

要求位置存取權

您的應用程式必須提示使用者同意使用定位服務。待辦 請在標頭中加入 NSLocationAlwaysUsageDescription 鍵 應用程式的 Info.plist 檔案,然後將每個鍵的值設為 字串,說明應用程式預計如何使用位置資料。

設定位置管理員

使用 CLLocationManager 找出裝置目前的位置,並在裝置移動到新位置時要求定期更新。本教學課程會提供 裝置的位置。詳情請參閱指南 取得使用者的位置

  1. 宣告位置管理員、目前位置、地圖檢視、地點用戶端 以及預設縮放等級
  2. 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;
          
  3. 初始化營業地點管理員和 GMSPlacesClient 位置: viewDidLoad()
  4. 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];
          
  5. 宣告變數以存放可能地點清單,以及使用者選取的地點。
  6. 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;
          
  7. 使用擴充功能子句新增委派程式,以便處理位置管理員的事件。
  8. 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。使用者選取地點時,在 。

  1. 取得可填入 UITableView 的可能地點清單。 讓使用者選取他們目前所在的地點
  2. 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];
        }
      }];
    }
          
  3. 開啟新的檢視畫面,向使用者顯示可能的地點。當使用者輕觸「取得地點」時,我們會切換至新檢視畫面,並向使用者顯示可供選擇的可能地點清單。prepare 函式更新 PlacesViewController 與目前可能的地點清單。 並在執行同層時自動呼叫。
  4. 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;
        }
      }
    }
          
  5. PlacesViewController 中,將最常使用的清單填入資料表 可能的地點,請使用 UITableViewDataSource 委派擴充功能。
  6. 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
          
  7. 使用 UITableViewDelegate 處理使用者選擇 委派擴充功能。
  8. 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 iOSMaps SDK for iOS, 和 Apple Core 位置資訊架構