Memilih Tempat Saat Ini dan Menampilkan Detail pada Peta
Tutorial ini menunjukkan cara mem-build aplikasi iOS untuk:
- Mendapatkan lokasi perangkat saat ini.
- Mendapatkan daftar tempat perangkat kemungkinan berada.
- Minta pengguna untuk menemukan tempat terbaik.
- Tampilkan penanda di peta.
Ikuti tutorial ini untuk membuat aplikasi iOS menggunakan Places SDK for iOS, Maps SDK for iOS, dan framework Apple Core Location.
Mendapatkan kode
Clone atau download Google Maps SDK for iOS dari GitHub.
Menyiapkan project pengembangan
Ikuti langkah-langkah berikut untuk menginstal Places SDK for iOS dan Maps SDK for iOS:
- Download dan instal Xcode versi 13.0 atau yang lebih baru.
- Jika Anda belum memiliki CocoaPods,
instal di macOS dengan menjalankan perintah berikut dari terminal:
sudo gem install cocoapods
- Di direktori tempat Anda menyimpan repositori contoh (Dapatkan kode), buka direktori
tutorials/current-place-on-map
. - Jalankan perintah
pod install
. Tindakan ini akan menginstal API yang ditentukan dalamPodfile
, beserta dependensi yang mungkin dimilikinya. - Jalankan
pod outdated
untuk membandingkan versi pod yang diinstal dengan update baru. Jika versi baru terdeteksi, jalankanpod update
untuk mengupdatePodfile
dan instal SDK terbaru. Untuk mengetahui detail selengkapnya, lihat Panduan CocoaPods. - Buka (klik dua kali) current-place-on-map.xcworkspace project untuk membukanya
di Xcode. Anda harus menggunakan file
.xcworkspace
untuk membuka project.
Untuk mendapatkan petunjuk penginstalan yang mendetail, lihat Memulai (Maps), dan Memulai (Places).
Mengaktifkan API yang diperlukan dan mendapatkan kunci API
Untuk menyelesaikan tutorial ini, Anda memerlukan kunci Google API yang telah diberi otorisasi untuk menggunakan Maps SDK for iOS dan Places API.
- Ikuti petunjuk di Memulai Google Maps Platform untuk menyiapkan akun penagihan dan project yang diaktifkan dengan kedua produk ini.
- Ikuti petunjuk tentang Mendapatkan Kunci API untuk membuat kunci API untuk project pengembangan yang Anda siapkan sebelumnya.
Menambahkan kunci API ke aplikasi
Tambahkan kunci API ke AppDelegate.swift
sebagai berikut:
- Perhatikan bahwa pernyataan impor berikut telah ditambahkan ke file:
import GooglePlaces import GoogleMaps
- Edit baris berikut dalam metode
application(_:didFinishLaunchingWithOptions:)
Anda, yang menggantikan YOUR_API_KEY dengan kunci API Anda:GMSPlacesClient.provideAPIKey("YOUR_API_KEY") GMSServices.provideAPIKey("YOUR_API_KEY")
Mem-build dan menjalankan aplikasi
- Hubungkan perangkat iOS ke komputer Anda, atau pilih simulator dari menu pop-up skema Xcode.
- Jika Anda menggunakan perangkat, pastikan layanan lokasi diaktifkan. Jika Anda menggunakan simulator, pilih lokasi dari menu Features.
- Di Xcode, klik opsi menu Product/Run (atau ikon tombol putar).
- Xcode mem-build aplikasi, lalu menjalankan aplikasi di perangkat atau di simulator.
- Anda akan melihat peta dengan sejumlah penanda yang dipusatkan di sekitar lokasi Anda saat ini.
Pemecahan masalah:
- Jika Anda tidak melihat peta, pastikan bahwa Anda telah mendapatkan kunci API dan menambahkannya ke aplikasi, seperti yang dijelaskan di atas. Periksa konsol proses debug Xcode untuk pesan error tentang kunci API.
- Jika Anda telah membatasi kunci API berdasarkan ID paket iOS, edit kunci untuk menambahkan ID paket untuk aplikasi:
com.google.examples.current-place-on-map
. - Peta tidak akan ditampilkan dengan benar jika permintaan izin untuk layanan lokasi ditolak.
- Jika Anda menggunakan perangkat, buka Settings/General/Privacy/Location Services, dan aktifkan kembali layanan lokasi.
- Jika Anda menggunakan simulator, buka Simulator/Reset Content and Settings....
- Pastikan Anda memiliki koneksi Wi-Fi atau GPS yang bagus.
- Jika aplikasi diluncurkan, tetapi tidak ada peta yang ditampilkan, pastikan Anda telah memperbarui Info.plist untuk project dengan izin akses lokasi yang sesuai. Untuk informasi selengkapnya tentang penanganan izin, lihat panduan untuk meminta izin akses lokasi di aplikasi Anda di bawah.
- Gunakan alat proses debug Xcode untuk melihat log dan melakukan debug pada aplikasi.
Memahami kode
Bagian tutorial ini menjelaskan bagian yang paling signifikan dari aplikasi current-place-on-map untuk membantu Anda memahami cara membuat aplikasi serupa.
Aplikasi current-place-on-map menampilkan dua pengontrol tampilan:
Satu untuk menampilkan peta yang menunjukkan tempat yang saat ini dipilih pengguna, dan satu
untuk menampilkan daftar tempat yang mungkin dapat dipilih pengguna. Perhatikan,
setiap pengontrol tampilan memiliki variabel yang sama untuk melacak daftar
kemungkinan tempat (likelyPlaces
), dan untuk menunjukkan pilihan
pengguna (selectedPlace
). Navigasi antartampilan
diselesaikan menggunakan segues.
Meminta izin akses lokasi
Aplikasi Anda harus meminta izin kepada pengguna untuk menggunakan layanan lokasi. Untuk melakukannya, sertakan kunci NSLocationAlwaysUsageDescription
dalam
file Info.plist
untuk aplikasi, dan tetapkan nilai setiap kunci ke
string yang menjelaskan cara aplikasi menggunakan data lokasi.
Menyiapkan pengelola lokasi
Gunakan CLLocationManager untuk menemukan lokasi perangkat saat ini dan meminta update reguler saat perangkat berpindah ke lokasi baru. Tutorial ini menyediakan kode yang Anda butuhkan untuk mendapatkan lokasi perangkat. Untuk mengetahui detail selengkapnya, baca panduan untuk Mendapatkan Lokasi Pengguna di Dokumentasi Developer Apple.
- Deklarasikan pengelola lokasi, lokasi saat ini, tampilan peta, klien tempat, dan tingkat zoom default di tingkat class.
- Lakukan inisialisasi pengelola lokasi dan
GMSPlacesClient
diviewDidLoad()
. - Deklarasikan variabel untuk menyimpan daftar kemungkinan tempat, dan tempat yang dipilih pengguna.
- Tambahkan delegasi untuk menangani peristiwa untuk pengelola lokasi, dengan menggunakan klausa ekstensi.
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;
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); }
Menambahkan peta
Buat peta dan tambahkan ke tampilan di viewDidLoad()
di
pengontrol tampilan utama. Peta akan tetap tersembunyi sampai pembaruan lokasi diterima
(pembaruan lokasi ditangani di ekstensi
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;
Meminta pengguna memilih tempat saat ini
Gunakan Places SDK for iOS untuk mendapatkan lima kemungkinan tempat teratas berdasarkan lokasi pengguna saat ini, dan tampilkan daftar dalam UITableView
. Saat pengguna memilih tempat, tambahkan penanda ke peta.
- Mendapatkan daftar kemungkinan tempat untuk mengisi
UITableView
, tempat pengguna dapat memilih tempat mereka berada saat ini. - Buka tampilan baru untuk menyajikan kemungkinan tempat kepada pengguna. Saat pengguna
mengetuk "Get Place", kita beralih ke tampilan baru, dan menampilkan daftar kemungkinan
tempat untuk dipilih pengguna. Fungsi
prepare
memperbaruiPlacesViewController
dengan daftar kemungkinan tempat saat ini, dan secara otomatis dipanggil saat segue dilakukan. - Di
PlacesViewController
, isi tabel menggunakan daftar kemungkinan tempat, menggunakan ekstensi delegasiUITableViewDataSource
. - Tangani pilihan pengguna menggunakan ekstensi delegasi
UITableViewDelegate
.
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]; } }]; }
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; } } }
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
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; }
Menambahkan penanda ke peta
Saat pengguna membuat pilihan, gunakan segue escape untuk kembali ke tampilan sebelumnya, dan tambahkan penanda ke peta. PMA
unwindToMain
dipanggil secara otomatis setelah kembali ke pengontrol tampilan utama.
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]; }
Selamat! Anda telah membuat aplikasi iOS yang memungkinkan pengguna memilih tempat mereka saat ini, dan menampilkan hasilnya di peta Google. Setelah melakukannya, Anda telah mempelajari cara menggunakan Places SDK for iOS, Maps SDK for iOS, dan framework Apple Core Location.