เลือกสถานที่ปัจจุบันและแสดงรายละเอียดบนแผนที่

บทแนะนำนี้จะสาธิตการสร้างแอป iOS ที่ดึงข้อมูลตำแหน่งปัจจุบันของอุปกรณ์ ระบุตำแหน่งที่เป็นไปได้ แจ้งให้ผู้ใช้เลือกรายการที่ตรงกันที่สุด และแสดงเครื่องหมายบนแผนที่สำหรับตำแหน่งที่เลือก

เหมาะสำหรับผู้ที่มีความรู้เบื้องต้นหรือระดับกลางเกี่ยวกับ Swift หรือ Objective-C และมีความรู้ทั่วไปเกี่ยวกับ Xcode อ่านคู่มือนักพัฒนาซอฟต์แวร์เพื่อดูคู่มือขั้นสูงในการสร้างแผนที่

คุณจะสร้างแผนที่ต่อไปนี้โดยใช้บทแนะนำนี้ เครื่องหมายแสดงแผนที่ถูกวางในซานฟรานซิสโก แคลิฟอร์เนีย แต่จะย้ายไปที่ใดก็ตามที่อุปกรณ์หรือเครื่องจำลองอยู่

บทแนะนำนี้ใช้ Places SDK สำหรับ iOS, Maps SDK สำหรับ iOS และเฟรมเวิร์กตำแหน่งหลักของ Apple

รับโค้ด

โคลนหรือดาวน์โหลด ที่เก็บตัวอย่างสำหรับ Google Maps สำหรับ iOS จาก GitHub

หรือให้คลิกปุ่มต่อไปนี้เพื่อดาวน์โหลดซอร์สโค้ด

ขอรหัสหน่อย

MapViewController ของ 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)")
  }
}

      

PlacesViewController ของ 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
  }
}

      

MapViewController ของ Obj-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 ของ Obj-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

      

เริ่มต้นใช้งาน

เครื่องมือจัดการแพ็กเกจ Swift

Maps SDK สำหรับ iOS สามารถติดตั้งได้โดยใช้ Swift Package Manager

  1. ตรวจสอบว่าคุณได้นำ Maps SDK สำหรับทรัพยากร Dependency ของ 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. เพิ่ม SDK ของ Places และ Maps:
    1. ไปที่ไฟล์ > เพิ่มทรัพยากร Dependency ของแพ็กเกจ
    2. ป้อน https://github.com/googlemaps/ios-places-sdk เป็น URL กด Enter เพื่อดึงข้อมูลแพ็กเกจ แล้วคลิกเพิ่มแพ็กเกจ
    3. ป้อน https://github.com/googlemaps/ios-maps-sdk เป็น URL กด Enter เพื่อดึงแพ็กเกจ แล้วคลิกเพิ่มแพ็กเกจ
  6. คุณอาจต้องรีเซ็ตแคชแพ็กเกจโดยใช้ไฟล์ > แพ็กเกจ > รีเซ็ตแคชแพ็กเกจ

ใช้ CocoaPods

  1. ดาวน์โหลดและติดตั้ง Xcode เวอร์ชัน 15.0 ขึ้นไป
  2. หากยังไม่มี CocoaPods ให้ติดตั้งใน macOS โดยเรียกใช้คำสั่งต่อไปนี้จากเทอร์มินัล
    sudo gem install cocoapods
  3. ไปที่ไดเรกทอรี tutorials/current-place-on-map
  4. เรียกใช้คำสั่ง pod install การดำเนินการนี้จะติดตั้ง SDK ของ Maps และ Places ที่ระบุไว้ใน Podfile พร้อมกับทรัพยากร Dependency ทั้งหมด
  5. เรียกใช้ pod outdated เพื่อเปรียบเทียบเวอร์ชันพ็อดที่ติดตั้งกับการอัปเดตใหม่ หากตรวจพบเวอร์ชันใหม่ ให้เรียกใช้ pod update เพื่ออัปเดต Podfile และติดตั้ง SDK เวอร์ชันล่าสุด โปรดดูรายละเอียดเพิ่มเติมในคู่มือ CocoaPods
  6. เปิด (ดับเบิลคลิก) ไฟล์ current-place-on-map.xcworkspace ของโปรเจ็กต์เพื่อเปิดใน Xcode คุณต้องใช้ไฟล์ .xcworkspace เพื่อเปิดโครงการ

รับคีย์ API และเปิดใช้ API ที่จำเป็น

หากต้องการจบบทแนะนำนี้ คุณต้องมีคีย์ Google API ที่ได้รับอนุญาตให้ใช้ Maps SDK สำหรับ iOS และ Places API

  1. ทำตามวิธีการในหัวข้อเริ่มต้นใช้งาน Google Maps Platform เพื่อตั้งค่าบัญชีสำหรับการเรียกเก็บเงินและโปรเจ็กต์ที่เปิดใช้กับทั้ง 2 ผลิตภัณฑ์เหล่านี้
  2. ทำตามวิธีการในหัวข้อรับคีย์ API เพื่อสร้างคีย์ API สำหรับโปรเจ็กต์พัฒนาที่คุณตั้งค่าไว้ก่อนหน้านี้

เพิ่มคีย์ API ลงในแอปพลิเคชัน

เพิ่มคีย์ 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 กับคอมพิวเตอร์ หรือเลือก เครื่องจำลอง จากเมนูป๊อปอัปในรูปแบบ Xcode
  2. หากคุณใช้อุปกรณ์ ให้ตรวจสอบว่าได้เปิดใช้บริการตำแหน่งแล้ว หากใช้เครื่องจำลอง ให้เลือกตำแหน่งจากเมนูฟีเจอร์
  3. ใน Xcode ให้คลิกตัวเลือกเมนู Product/Run (หรือไอคอนปุ่มเล่น)
    • Xcode จะสร้างแอป จากนั้นเรียกใช้แอปในอุปกรณ์หรือโปรแกรมจำลอง
    • คุณควรจะเห็นแผนที่พร้อมเครื่องหมายจำนวนหนึ่งตั้งอยู่ตรงกลางรอบๆ ตำแหน่งปัจจุบันของคุณ

การแก้ปัญหา:

  • หากไม่เห็นแผนที่ ให้ตรวจสอบว่าคุณได้รับคีย์ API และเพิ่มลงในแอปตามที่อธิบายไว้ข้างต้นแล้ว ตรวจสอบ คอนโซลการแก้ไขข้อบกพร่องของ Xcode เพื่อหาข้อความแสดงข้อผิดพลาดเกี่ยวกับคีย์ API
  • หากคุณจำกัดคีย์ API ตามตัวระบุชุด iOS ให้แก้ไขคีย์เพื่อเพิ่มตัวระบุชุดสำหรับแอป: com.google.examples.current-place-on-map
  • แผนที่จะแสดงไม่ถูกต้องหากคำขอสิทธิ์สำหรับบริการตำแหน่งถูกปฏิเสธ
    • หากคุณใช้อุปกรณ์ ให้ไปที่การตั้งค่า/General/Privacy/Location Services แล้วเปิดใช้บริการตำแหน่งอีกครั้ง
    • หากใช้เครื่องจำลอง ให้ไปที่เครื่องมือจำลอง/รีเซ็ตเนื้อหาและการตั้งค่า...
    ในครั้งต่อไปที่แอปทำงาน โปรดยอมรับข้อความแจ้งของบริการตำแหน่ง
  • ตรวจสอบว่าคุณมีการเชื่อมต่อ Wi-Fi หรือ GPS ที่ดี
  • หากแอปเปิดขึ้นแต่ไม่มีแผนที่ปรากฏ โปรดตรวจสอบว่าคุณได้อัปเดต Info.plist สำหรับโปรเจ็กต์ด้วยสิทธิ์เข้าถึงตำแหน่งที่เหมาะสมแล้ว ดูข้อมูลเพิ่มเติมเกี่ยวกับการจัดการสิทธิ์ได้ที่คำแนะนำเกี่ยวกับการขอสิทธิ์เข้าถึงตำแหน่งในแอปด้านล่าง
  • ใช้เครื่องมือแก้ไขข้อบกพร่อง Xcode เพื่อดูบันทึกและแก้ไขข้อบกพร่องของแอป

ทำความเข้าใจโค้ด

ส่วนนี้ของบทแนะนำจะอธิบายส่วนที่สำคัญที่สุดของแอปในcurrent-place-on-map เพื่อช่วยให้คุณเข้าใจวิธีสร้างแอปที่คล้ายกัน

แอปcurrent-place-on-mapมีตัวควบคุมมุมมอง 2 แบบ รายการแรกแสดงแผนที่ที่แสดงสถานที่ที่ผู้ใช้เลือกอยู่ ส่วนรายการแรกสำหรับแสดงรายการสถานที่ที่น่าไปซึ่งผู้ใช้เลือก โปรดทราบว่าตัวควบคุมการดูแต่ละรายการมีตัวแปรเหมือนกันสำหรับการติดตามรายการสถานที่ที่น่าจะเกิดขึ้น (likelyPlaces) และสำหรับการระบุการเลือกของผู้ใช้ (selectedPlace) การไปยังมุมมองต่างๆ ทำได้โดยใช้ซีก

กำลังขอสิทธิ์เข้าถึงตำแหน่ง

แอปของคุณต้องแจ้งให้ผู้ใช้ยินยอมให้ใช้บริการตำแหน่ง ในการดำเนินการดังกล่าว ให้ใส่คีย์ NSLocationAlwaysUsageDescription ในไฟล์ Info.plist ของแอป และตั้งค่าของแต่ละคีย์เป็นสตริงที่อธิบายวิธีที่แอปจะใช้ข้อมูลตำแหน่ง

การตั้งค่าผู้จัดการสถานที่

ใช้ CLLocationManager เพื่อค้นหาตำแหน่งปัจจุบันของอุปกรณ์และขอการอัปเดตเป็นประจำเมื่ออุปกรณ์ย้ายไปยังตำแหน่งใหม่ บทแนะนำนี้มีโค้ดที่คุณต้องใช้ ในการรับตำแหน่งของอุปกรณ์ โปรดดูรายละเอียดเพิ่มเติมในคำแนะนำเกี่ยวกับการรับตำแหน่งของผู้ใช้ในเอกสารประกอบสำหรับนักพัฒนาซอฟต์แวร์ Apple

  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 สำหรับ iOS เพื่อรับสถานที่ที่เป็นไปได้สูงสุด 5 อันดับแรกโดยอิงตามตำแหน่งปัจจุบันของผู้ใช้ และแสดงรายการใน 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 ด้วยรายการสถานที่ที่น่าจะเกิดขึ้นในปัจจุบัน และมีการเรียกใช้โดยอัตโนมัติเมื่อมีการสร้าง Segue
  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 สำหรับ iOS, Maps SDK สำหรับ iOS และเฟรมเวิร์กตำแหน่งหลักของ Apple แล้ว