地点自动补全

Places SDK for iOS 中的自动补全服务会返回地点预测结果以响应用户搜索查询。在用户输入时,自动补全服务会返回商家、地址、 Plus Code 和地图注点等地点的建议。

您可以通过以下方式向应用中添加自动填充服务:

添加自动补全界面控件

自动补全界面控件是具有内置自动补全功能的搜索对话框。在用户输入搜索字词时,该控件会显示可供选择的预测地点列表。当用户做出选择时,系统会返回 GMSPlace 实例,然后您的应用可以使用该实例获取有关所选地点的详细信息。

您可以使用以下方式将自动填充 UI 控件添加到应用中:

添加全屏控件

如果您需要模态上下文,请使用全屏控件,其中自动补全界面会暂时取代应用的界面,直到用户做出选择。此功能由 GMSAutocompleteViewController 类提供。当用户选择一个地点后,您的应用会收到回调。

要向应用中添加全屏控件,请执行以下操作:

  1. 在主应用中创建一个界面元素来启动自动补全界面控件,例如 UIButton 上的触摸处理程序。
  2. 在父视图控制器中实现 GMSAutocompleteViewControllerDelegate 协议。
  3. 创建 GMSAutocompleteViewController 的实例,并将父级视图控制器分配为代理属性。
  4. 创建 GMSPlaceField 以定义要返回的地点数据类型。
  5. 添加 GMSAutocompleteFilter,将查询限制在特定类型的地点
  6. 使用 [self presentViewController...] 展示 GMSAutocompleteViewController
  7. 处理用户在 didAutocompleteWithPlace 委托方法中的选择。
  8. didAutocompleteWithPlacedidFailAutocompleteWithErrorwasCancelled 委托方法中关闭控制器。

以下示例演示了启动 GMSAutocompleteViewController 来响应用户点按按钮的一种可行方式。

Swift

import UIKit
import GooglePlaces

class ViewController: UIViewController {

  override func viewDidLoad() {
    makeButton()
  }

  // Present the Autocomplete view controller when the button is pressed.
  @objc func autocompleteClicked(_ sender: UIButton) {
    let autocompleteController = GMSAutocompleteViewController()
    autocompleteController.delegate = self

    // Specify the place data types to return.
    let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
      UInt(GMSPlaceField.placeID.rawValue))!
    autocompleteController.placeFields = fields

    // Specify a filter.
    let filter = GMSAutocompleteFilter()
    filter.types = [.address]
    autocompleteController.autocompleteFilter = filter

    // Display the autocomplete view controller.
    present(autocompleteController, animated: true, completion: nil)
  }

  // Add a button to the view.
  func makeButton() {
    let btnLaunchAc = UIButton(frame: CGRect(x: 5, y: 150, width: 300, height: 35))
    btnLaunchAc.backgroundColor = .blue
    btnLaunchAc.setTitle("Launch autocomplete", for: .normal)
    btnLaunchAc.addTarget(self, action: #selector(autocompleteClicked), for: .touchUpInside)
    self.view.addSubview(btnLaunchAc)
  }

}

extension ViewController: GMSAutocompleteViewControllerDelegate {

  // Handle the user's selection.
  func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
    print("Place name: \(place.name)")
    print("Place ID: \(place.placeID)")
    print("Place attributions: \(place.attributions)")
    dismiss(animated: true, completion: nil)
  }

  func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
    // TODO: handle the error.
    print("Error: ", error.localizedDescription)
  }

  // User canceled the operation.
  func wasCancelled(_ viewController: GMSAutocompleteViewController) {
    dismiss(animated: true, completion: nil)
  }

  // Turn the network activity indicator on and off again.
  func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
  }

  func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
  }

}

Objective-C

#import "ViewController.h"
@import GooglePlaces;

@interface ViewController () <GMSAutocompleteViewControllerDelegate>

@end

@implementation ViewController {
  GMSAutocompleteFilter *_filter;
}

- (void)viewDidLoad {
  [super viewDidLoad];
  [self makeButton];
}

  // Present the autocomplete view controller when the button is pressed.
- (void)autocompleteClicked {
  GMSAutocompleteViewController *acController = [[GMSAutocompleteViewController alloc] init];
  acController.delegate = self;

  // Specify the place data types to return.
  GMSPlaceField fields = (GMSPlaceFieldName | GMSPlaceFieldPlaceID);
  acController.placeFields = fields;

  // Specify a filter.
  _filter = [[GMSAutocompleteFilter alloc] init];
  _filter.types = @[ kGMSPlaceTypeBank ];
  acController.autocompleteFilter = _filter;

  // Display the autocomplete view controller.
  [self presentViewController:acController animated:YES completion:nil];
}

  // Add a button to the view.
- (void)makeButton{
  UIButton *btnLaunchAc = [UIButton buttonWithType:UIButtonTypeCustom];
  [btnLaunchAc addTarget:self
             action:NSSelectorFromString(@"autocompleteClicked") forControlEvents:UIControlEventTouchUpInside];
  [btnLaunchAc setTitle:@"Launch autocomplete" forState:UIControlStateNormal];
  btnLaunchAc.frame = CGRectMake(5.0, 150.0, 300.0, 35.0);
  btnLaunchAc.backgroundColor = [UIColor blueColor];
  [self.view addSubview:btnLaunchAc];
}

  // Handle the user's selection.
- (void)viewController:(GMSAutocompleteViewController *)viewController
didAutocompleteWithPlace:(GMSPlace *)place {
  [self dismissViewControllerAnimated:YES completion:nil];
  // Do something with the selected place.
  NSLog(@"Place name %@", place.name);
  NSLog(@"Place ID %@", place.placeID);
  NSLog(@"Place attributions %@", place.attributions.string);
}

- (void)viewController:(GMSAutocompleteViewController *)viewController
didFailAutocompleteWithError:(NSError *)error {
  [self dismissViewControllerAnimated:YES completion:nil];
  // TODO: handle the error.
  NSLog(@"Error: %@", [error description]);
}

  // User canceled the operation.
- (void)wasCancelled:(GMSAutocompleteViewController *)viewController {
  [self dismissViewControllerAnimated:YES completion:nil];
}

  // Turn the network activity indicator on and off again.
- (void)didRequestAutocompletePredictions:(GMSAutocompleteViewController *)viewController {
  [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

- (void)didUpdateAutocompletePredictions:(GMSAutocompleteViewController *)viewController {
  [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

@end

添加结果控制器

如果您想要更好地控制文本输入界面,请使用结果控制器。结果控制器会根据输入界面的焦点动态切换结果列表的可见性。

要向应用中添加结果控制器,请执行以下操作:

  1. 创建 GMSAutocompleteResultsViewController
  2. 在父视图控制器中实现 GMSAutocompleteResultsViewControllerDelegate 协议,并将父视图控制器分配为代理属性。
  3. 创建一个 UISearchController 对象,传入 GMSAutocompleteResultsViewController 作为结果控制器参数。
  4. GMSAutocompleteResultsViewController 设置为 UISearchControllersearchResultsUpdater 属性。
  5. UISearchControllersearchBar 添加到应用界面中。
  6. 处理用户在 didAutocompleteWithPlace 委托方法中的选择。

您可以通过多种方法将 UISearchController 的搜索栏放在应用界面中:

向导航栏添加搜索栏

以下代码示例演示了如何添加结果控制器,将 searchBar 添加到导航栏中,以及处理用户的选择:

Swift

class ViewController: UIViewController {

  var resultsViewController: GMSAutocompleteResultsViewController?
  var searchController: UISearchController?
  var resultView: UITextView?

  override func viewDidLoad() {
    super.viewDidLoad()

    resultsViewController = GMSAutocompleteResultsViewController()
    resultsViewController?.delegate = self

    searchController = UISearchController(searchResultsController: resultsViewController)
    searchController?.searchResultsUpdater = resultsViewController

    // Put the search bar in the navigation bar.
    searchController?.searchBar.sizeToFit()
    navigationItem.titleView = searchController?.searchBar

    // When UISearchController presents the results view, present it in
    // this view controller, not one further up the chain.
    definesPresentationContext = true

    // Prevent the navigation bar from being hidden when searching.
    searchController?.hidesNavigationBarDuringPresentation = false
  }
}

// Handle the user's selection.
extension ViewController: GMSAutocompleteResultsViewControllerDelegate {
  func resultsController(_ resultsController: GMSAutocompleteResultsViewController,
                         didAutocompleteWith place: GMSPlace) {
    searchController?.isActive = false
    // Do something with the selected place.
    print("Place name: \(place.name)")
    print("Place address: \(place.formattedAddress)")
    print("Place attributions: \(place.attributions)")
  }

  func resultsController(_ resultsController: GMSAutocompleteResultsViewController,
                         didFailAutocompleteWithError error: Error){
    // TODO: handle the error.
    print("Error: ", error.localizedDescription)
  }

  // Turn the network activity indicator on and off again.
  func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
  }

  func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
  }
}

Objective-C

- (void)viewDidLoad {
  _resultsViewController = [[GMSAutocompleteResultsViewController alloc] init];
  _resultsViewController.delegate = self;

  _searchController = [[UISearchController alloc]
                       initWithSearchResultsController:_resultsViewController];
  _searchController.searchResultsUpdater = _resultsViewController;

  // Put the search bar in the navigation bar.
  [_searchController.searchBar sizeToFit];
  self.navigationItem.titleView = _searchController.searchBar;

  // When UISearchController presents the results view, present it in
  // this view controller, not one further up the chain.
  self.definesPresentationContext = YES;

  // Prevent the navigation bar from being hidden when searching.
  _searchController.hidesNavigationBarDuringPresentation = NO;
}

// Handle the user's selection.
- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController
  didAutocompleteWithPlace:(GMSPlace *)place {
    _searchController.active = NO;
    // Do something with the selected place.
    NSLog(@"Place name %@", place.name);
    NSLog(@"Place address %@", place.formattedAddress);
    NSLog(@"Place attributions %@", place.attributions.string);
}

- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController
didFailAutocompleteWithError:(NSError *)error {
  [self dismissViewControllerAnimated:YES completion:nil];
  // TODO: handle the error.
  NSLog(@"Error: %@", [error description]);
}

// Turn the network activity indicator on and off again.
- (void)didRequestAutocompletePredictionsForResultsController:
    (GMSAutocompleteResultsViewController *)resultsController {
  [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

- (void)didUpdateAutocompletePredictionsForResultsController:
    (GMSAutocompleteResultsViewController *)resultsController {
  [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

将搜索栏添加到视图顶部

以下代码示例展示了如何将 searchBar 添加到视图顶部。

Swift

import UIKit
import GooglePlaces

class ViewController: UIViewController {

  var resultsViewController: GMSAutocompleteResultsViewController?
  var searchController: UISearchController?
  var resultView: UITextView?

  override func viewDidLoad() {
    super.viewDidLoad()

    resultsViewController = GMSAutocompleteResultsViewController()
    resultsViewController?.delegate = self

    searchController = UISearchController(searchResultsController: resultsViewController)
    searchController?.searchResultsUpdater = resultsViewController

    let subView = UIView(frame: CGRect(x: 0, y: 65.0, width: 350.0, height: 45.0))

    subView.addSubview((searchController?.searchBar)!)
    view.addSubview(subView)
    searchController?.searchBar.sizeToFit()
    searchController?.hidesNavigationBarDuringPresentation = false

    // When UISearchController presents the results view, present it in
    // this view controller, not one further up the chain.
    definesPresentationContext = true
  }
}

// Handle the user's selection.
extension ViewController: GMSAutocompleteResultsViewControllerDelegate {
  func resultsController(_ resultsController: GMSAutocompleteResultsViewController,
                         didAutocompleteWith place: GMSPlace) {
    searchController?.isActive = false
    // Do something with the selected place.
    print("Place name: \(place.name)")
    print("Place address: \(place.formattedAddress)")
    print("Place attributions: \(place.attributions)")
  }

  func resultsController(_ resultsController: GMSAutocompleteResultsViewController,
                         didFailAutocompleteWithError error: Error){
    // TODO: handle the error.
    print("Error: ", error.localizedDescription)
  }

  // Turn the network activity indicator on and off again.
  func didRequestAutocompletePredictions(forResultsController resultsController: GMSAutocompleteResultsViewController) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
  }

  func didUpdateAutocompletePredictions(forResultsController resultsController: GMSAutocompleteResultsViewController) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
  }
}

Objective-C

- (void)viewDidLoad {
    [super viewDidLoad];

    _resultsViewController = [[GMSAutocompleteResultsViewController alloc] init];
    _resultsViewController.delegate = self;

    _searchController = [[UISearchController alloc]
                             initWithSearchResultsController:_resultsViewController];
    _searchController.searchResultsUpdater = _resultsViewController;

    UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(0, 65.0, 250, 50)];

    [subView addSubview:_searchController.searchBar];
    [_searchController.searchBar sizeToFit];
    [self.view addSubview:subView];

    // When UISearchController presents the results view, present it in
    // this view controller, not one further up the chain.
    self.definesPresentationContext = YES;
}

// Handle the user's selection.
- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController
didAutocompleteWithPlace:(GMSPlace *)place {
  [self dismissViewControllerAnimated:YES completion:nil];
  // Do something with the selected place.
  NSLog(@"Place name %@", place.name);
  NSLog(@"Place address %@", place.formattedAddress);
  NSLog(@"Place attributions %@", place.attributions.string);
}

- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController
didFailAutocompleteWithError:(NSError *)error {
  [self dismissViewControllerAnimated:YES completion:nil];
  // TODO: handle the error.
  NSLog(@"Error: %@", [error description]);
}

// Turn the network activity indicator on and off again.
- (void)didRequestAutocompletePredictionsForResultsController:
    (GMSAutocompleteResultsViewController *)resultsController {
  [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

- (void)didUpdateAutocompletePredictionsForResultsController:
    (GMSAutocompleteResultsViewController *)resultsController {
  [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

默认情况下,UISearchController 会在展示时隐藏导航栏(可停用此功能)。在导航栏可见且不透明的情况下,UISearchController 无法正确设置展示位置。

请使用以下代码解决这一问题:

Swift

navigationController?.navigationBar.translucent = false
searchController?.hidesNavigationBarDuringPresentation = false

// This makes the view area include the nav bar even though it is opaque.
// Adjust the view placement down.
self.extendedLayoutIncludesOpaqueBars = true
self.edgesForExtendedLayout = .top

Objective-C

self.navigationController.navigationBar.translucent = NO;
_searchController.hidesNavigationBarDuringPresentation = NO;

// This makes the view area include the nav bar even though it is opaque.
// Adjust the view placement down.
self.extendedLayoutIncludesOpaqueBars = YES;
self.edgesForExtendedLayout = UIRectEdgeTop;

添加使用弹出式窗口结果的搜索栏

以下代码示例展示了如何在导航栏右侧放置搜索栏,以及如何在弹出式窗口中显示结果。

Swift

import UIKit
import GooglePlaces

class ViewController: UIViewController {

  var resultsViewController: GMSAutocompleteResultsViewController?
  var searchController: UISearchController?
  var resultView: UITextView?

  override func viewDidLoad() {
    super.viewDidLoad()

    resultsViewController = GMSAutocompleteResultsViewController()
    resultsViewController?.delegate = self

    searchController = UISearchController(searchResultsController: resultsViewController)
    searchController?.searchResultsUpdater = resultsViewController

    // Add the search bar to the right of the nav bar,
    // use a popover to display the results.
    // Set an explicit size as we don't want to use the entire nav bar.
    searchController?.searchBar.frame = (CGRect(x: 0, y: 0, width: 250.0, height: 44.0))
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: (searchController?.searchBar)!)

    // When UISearchController presents the results view, present it in
    // this view controller, not one further up the chain.
    definesPresentationContext = true

    // Keep the navigation bar visible.
    searchController?.hidesNavigationBarDuringPresentation = false
    searchController?.modalPresentationStyle = .popover
  }
}
// Handle the user's selection.
extension ViewController: GMSAutocompleteResultsViewControllerDelegate {
  func resultsController(_ resultsController: GMSAutocompleteResultsViewController,
                         didAutocompleteWith place: GMSPlace) {
    searchController?.isActive = false
    // Do something with the selected place.
    print("Place name: \(place.name)")
    print("Place address: \(place.formattedAddress)")
    print("Place attributions: \(place.attributions)")
  }

  func resultsController(_ resultsController: GMSAutocompleteResultsViewController,
                         didFailAutocompleteWithError error: Error){
    // TODO: handle the error.
    print("Error: ", error.localizedDescription)
  }

  // Turn the network activity indicator on and off again.
  func didRequestAutocompletePredictions(forResultsController resultsController: GMSAutocompleteResultsViewController) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
  }

  func didUpdateAutocompletePredictions(forResultsController resultsController: GMSAutocompleteResultsViewController) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
  }
}

Objective-C

- (void)viewDidLoad {
  [super viewDidLoad];

  _resultsViewController = [[GMSAutocompleteResultsViewController alloc] init];
  _resultsViewController.delegate = self;

  _searchController = [[UISearchController alloc]
                           initWithSearchResultsController:_resultsViewController];
  _searchController.searchResultsUpdater = _resultsViewController;

  // Add the search bar to the right of the nav bar,
  // use a popover to display the results.
  // Set an explicit size as we don't want to use the entire nav bar.
  _searchController.searchBar.frame = CGRectMake(0, 0, 250.0f, 44.0f);
  self.navigationItem.rightBarButtonItem =
  [[UIBarButtonItem alloc] initWithCustomView:_searchController.searchBar];

  // When UISearchController presents the results view, present it in
  // this view controller, not one further up the chain.
  self.definesPresentationContext = YES;

  // Keep the navigation bar visible.
  _searchController.hidesNavigationBarDuringPresentation = NO;

  _searchController.modalPresentationStyle = UIModalPresentationPopover;
}

// Handle the user's selection.
- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController
didAutocompleteWithPlace:(GMSPlace *)place {
  [self dismissViewControllerAnimated:YES completion:nil];
  NSLog(@"Place name %@", place.name);
  NSLog(@"Place address %@", place.formattedAddress);
  NSLog(@"Place attributions %@", place.attributions.string);
}

- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController
didFailAutocompleteWithError:(NSError *)error {
  [self dismissViewControllerAnimated:YES completion:nil];
  // TODO: handle the error.
  NSLog(@"Error: %@", [error description]);
}

// Turn the network activity indicator on and off again.
- (void)didRequestAutocompletePredictionsForResultsController:
    (GMSAutocompleteResultsViewController *)resultsController {
  [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

- (void)didUpdateAutocompletePredictionsForResultsController:
    (GMSAutocompleteResultsViewController *)resultsController {
  [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

使用表数据源

如果您的应用具有自定义搜索文本界面,则可以使用 GMSAutocompleteTableDataSource 类来驱动在视图控制器上显示结果的表视图。

如需使用 GMSAutocompleteTableDataSource 作为视图控制器中 UITableView 的数据源和委托,请执行以下操作:

  1. 在视图控制器中实现 GMSAutocompleteTableDataSourceDelegateUISearchBarDelegate 协议。
  2. 创建一个 GMSAutocompleteTableDataSource 实例,并将视图控制器指定为委托属性。
  3. GMSAutocompleteTableDataSource 设置为视图控制器上 UITableView 实例的数据源和委托属性。
  4. 在搜索文本输入的处理程序中,对 GMSAutocompleteTableDataSource 调用 sourceTextHasChanged
  5. 处理用户在 didAutocompleteWithPlace 委托方法中的选择。
  6. didAutocompleteWithPlacedidFailAutocompleteWithErrorwasCancelled 委托方法中关闭控制器。

以下代码示例演示了如何使用 GMSAutocompleteTableDataSource 类在单独添加 UISearchBar 时驱动 UIViewController 的表视图。

Swift

// 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 GooglePlaces
import UIKit

class PlaceAutocompleteViewController: UIViewController {

  private var tableView: UITableView!
  private var tableDataSource: GMSAutocompleteTableDataSource!

  override func viewDidLoad() {
    super.viewDidLoad()

    let searchBar = UISearchBar(frame: CGRect(x: 0, y: 20, width: self.view.frame.size.width, height: 44.0))
    searchBar.delegate = self
    view.addSubview(searchBar)

    tableDataSource = GMSAutocompleteTableDataSource()
    tableDataSource.delegate = self

    tableView = UITableView(frame: CGRect(x: 0, y: 64, width: self.view.frame.size.width, height: self.view.frame.size.height - 44))
    tableView.delegate = tableDataSource
    tableView.dataSource = tableDataSource

    view.addSubview(tableView)
  }
}

extension PlaceAutocompleteViewController: UISearchBarDelegate {
  func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    // Update the GMSAutocompleteTableDataSource with the search text.
    tableDataSource.sourceTextHasChanged(searchText)
  }
}

extension PlaceAutocompleteViewController: GMSAutocompleteTableDataSourceDelegate {
  func didUpdateAutocompletePredictions(for tableDataSource: GMSAutocompleteTableDataSource) {
    // Turn the network activity indicator off.
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
    // Reload table data.
    tableView.reloadData()
  }

  func didRequestAutocompletePredictions(for tableDataSource: GMSAutocompleteTableDataSource) {
    // Turn the network activity indicator on.
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
    // Reload table data.
    tableView.reloadData()
  }

  func tableDataSource(_ tableDataSource: GMSAutocompleteTableDataSource, didAutocompleteWith place: GMSPlace) {
    // Do something with the selected place.
    print("Place name: \(place.name)")
    print("Place address: \(place.formattedAddress)")
    print("Place attributions: \(place.attributions)")
  }

  func tableDataSource(_ tableDataSource: GMSAutocompleteTableDataSource, didFailAutocompleteWithError error: Error) {
    // Handle the error.
    print("Error: \(error.localizedDescription)")
  }

  func tableDataSource(_ tableDataSource: GMSAutocompleteTableDataSource, didSelect prediction: GMSAutocompletePrediction) -> Bool {
    return true
  }
}

      

Objective-C

// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#import "PlaceAutocompleteViewController.h"
@import GooglePlaces;
@import UIKit;

@interface PlaceAutocompleteViewController () <GMSAutocompleteTableDataSourceDelegate, UISearchBarDelegate>

@end

@implementation PlaceAutocompleteViewController {
  UITableView *tableView;
  GMSAutocompleteTableDataSource *tableDataSource;
}

- (void)viewDidLoad {
  [super viewDidLoad];

  UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 20, self.view.frame.size.width, 44)];
  searchBar.delegate = self;

  [self.view addSubview:searchBar];

  tableDataSource = [[GMSAutocompleteTableDataSource alloc] init];
  tableDataSource.delegate = self;

  tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 44)];
  tableView.delegate = tableDataSource;
  tableView.dataSource = tableDataSource;

  [self.view addSubview:tableView];
}

#pragma mark - GMSAutocompleteTableDataSourceDelegate

- (void)didUpdateAutocompletePredictionsForTableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource {
  // Turn the network activity indicator off.
  UIApplication.sharedApplication.networkActivityIndicatorVisible = NO;

  // Reload table data.
  [tableView reloadData];
}

- (void)didRequestAutocompletePredictionsForTableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource {
  // Turn the network activity indicator on.
  UIApplication.sharedApplication.networkActivityIndicatorVisible = YES;

  // Reload table data.
  [tableView reloadData];
}

- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource didAutocompleteWithPlace:(GMSPlace *)place {
  // Do something with the selected place.
  NSLog(@"Place name: %@", place.name);
  NSLog(@"Place address: %@", place.formattedAddress);
  NSLog(@"Place attributions: %@", place.attributions);
}

- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource didFailAutocompleteWithError:(NSError *)error {
  // Handle the error
  NSLog(@"Error %@", error.description);
}

- (BOOL)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource didSelectPrediction:(GMSAutocompletePrediction *)prediction {
  return YES;
}

#pragma mark - UISearchBarDelegate

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
  // Update the GMSAutocompleteTableDataSource with the search text.
  [tableDataSource sourceTextHasChanged:searchText];
}

@end

      

自定义文本和背景颜色

您可以在自动补全界面控件中设置所有文本和背景的颜色,使 widget 与应用的视觉外观更加匹配。设置 UI 控件颜色的方式有以下两种:

  • 尽可能使用原生 iOS UIAppearance 协议对界面控件进行全局样式设置。这些设置适用于许多(但非全部)界面控件元素。
  • 对 widget 类使用 SDK 方法可设置 UIAppearance 协议不支持的属性。

通常,您的应用会组合使用 UIAppearance 协议和 SDK 方法。下图显示可以设置样式的元素:

自动补全界面控件颜色

下表列出了所有界面元素,并指明了应如何设置每个界面元素的样式(UIAppearance 协议或 SDK 方法)。

界面元素 方法 造型百科
导航栏色调(背景) UIAppearance 协议 UINavigationBar 代理调用 setBarTintColor
导航栏着色颜色(搜索栏文本插入符号和取消按钮) UIAppearance 协议 UINavigationBar 代理调用 setTintColor
搜索栏文字颜色 UIAppearance 协议 searchBarTextAttributes 中设置 NSForegroundColorAttributeName
搜索栏色调颜色 不适用 搜索栏是半透明的,将显示为阴影的导航栏。
搜索栏占位符文字颜色(默认搜索文字) UIAppearance 协议 placeholderAttributes 中设置 NSForegroundColorAttributeName
主要文本(也适用于错误和消息文本) SDK 方法 欢迎致电primaryTextColor
主要文本突出显示方式 SDK 方法 欢迎致电primaryTextHighlightColor
辅助文本 SDK 方法 欢迎致电secondaryTextColor
错误和消息文本 SDK 方法 欢迎致电primaryTextColor
表格单元格背景 SDK 方法 欢迎致电tableCellBackgroundColor
表格单元格颜色 SDK 方法 欢迎致电tableCellSeparatorColor
“Try again”(重试)按钮 SDK 方法 欢迎致电tintColor
活动指示器(进度旋转图标) UIAppearance 协议 UIActivityIndicatorView 代理调用 setColor
“Powered by Google”徽标,悲伤的云端图片 不适用 系统会根据背景对比度自动选择白色或灰色版本。
搜索栏文本字段中的放大镜和清晰的文本图标 不适用 要设置样式,请将所需图片替换为所需颜色的默认图片。

使用 UIAppearance 协议

您可以使用 UIAppearance 协议获取给定界面元素的外观代理,然后,您可以使用该代理设置界面元素的颜色。进行修改时,指定界面元素的所有实例都会受到影响。例如,以下示例会在 UITextField 类包含在 UISearchBar 中时全局将其颜色更改为绿色:

[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil]
    setDefaultTextAttributes:@{NSForegroundColorAttributeName:[UIColor greenColor]}];

如需详细了解如何定义颜色值,请参阅 UIColor 类参考文档

以下代码段展示了在全屏自动补全界面控件中设置所有内容的样式时需要使用的所有代理命令。将以下代码添加到 Appdelegate.m 中的 didFinishLaunchingWithOptions 方法:

// Define some colors.
UIColor *darkGray = [UIColor darkGrayColor];
UIColor *lightGray = [UIColor lightGrayColor];

// Navigation bar background.
[[UINavigationBar appearance] setBarTintColor:darkGray];
[[UINavigationBar appearance] setTintColor:lightGray];

// Color of typed text in the search bar.
NSDictionary *searchBarTextAttributes = @{
                                          NSForegroundColorAttributeName: lightGray,
                                          NSFontAttributeName : [UIFont systemFontOfSize:[UIFont systemFontSize]]
                                          };
[UITextField appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]]
    .defaultTextAttributes = searchBarTextAttributes;

// Color of the placeholder text in the search bar prior to text entry.
NSDictionary *placeholderAttributes = @{
                                        NSForegroundColorAttributeName: lightGray,
                                        NSFontAttributeName : [UIFont systemFontOfSize:[UIFont systemFontSize]]
                                        };

// Color of the default search text.
// NOTE: In a production scenario, "Search" would be a localized string.
NSAttributedString *attributedPlaceholder =
[[NSAttributedString alloc] initWithString:@"Search"
                                attributes:placeholderAttributes];
[UITextField appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]]
    .attributedPlaceholder = attributedPlaceholder;

// Color of the in-progress spinner.
[[UIActivityIndicatorView appearance] setColor:lightGray];

// To style the two image icons in the search bar (the magnifying glass
// icon and the 'clear text' icon), replace them with different images.
[[UISearchBar appearance] setImage:[UIImage imageNamed:@"custom_clear_x_high"]
                  forSearchBarIcon:UISearchBarIconClear
                            state:UIControlStateHighlighted];
[[UISearchBar appearance] setImage:[UIImage imageNamed:@"custom_clear_x"]
                  forSearchBarIcon:UISearchBarIconClear
                            state:UIControlStateNormal];
[[UISearchBar appearance] setImage:[UIImage imageNamed:@"custom_search"]
                    forSearchBarIcon:UISearchBarIconSearch
                            state:UIControlStateNormal];

// Color of selected table cells.
UIView *selectedBackgroundView = [[UIView alloc] init];
selectedBackgroundView.backgroundColor = [UIColor lightGrayColor];
[UITableViewCell appearanceWhenContainedIn:[GMSAutocompleteViewController class], nil]
    .selectedBackgroundView = selectedBackgroundView;

设置界面控件样式属性

一部分界面控制元素具有不受 UIAppearance 协议影响的属性,因此必须直接设置。以下代码示例展示了如何定义前景颜色和背景颜色,并将其应用于名为 acController 的界面控件实例。将以下代码添加到 ViewController.m 中的 onLaunchClicked 方法:

UIColor *darkGray = [UIColor darkGrayColor];
UIColor *lightGray = [UIColor lightGrayColor];

acController.secondaryTextColor = [UIColor colorWithWhite:1.0f alpha:0.5f];
acController.primaryTextColor = lightGray;
acController.primaryTextHighlightColor = [UIColor grayColor];
acController.tableCellBackgroundColor = darkGray;
acController.tableCellSeparatorColor = lightGray;
acController.tintColor = lightGray;

以编程方式获取地点预测

您可以创建自定义搜索界面,以替代自动补全 widget 提供的界面。为此,您的应用必须以编程方式获取地点预测结果。您的应用可通过以下其中一种方式获取预测地点名称和/或地址列表:

正在呼叫GMSPlacesClient findAutocompletePredictionsFromQuery:

如需获取预测地点名称和/或地址的列表,请先实例化 GMSPlacesClient,然后使用以下参数调用 GMSPlacesClient findAutocompletePredictionsFromQuery: 方法:

  • 包含用户输入的文本的 autocompleteQuery 字符串。
  • GMSAutocompleteSessionToken,用于标识各个会话。您的应用应在每次自动补全请求调用中传递相同的令牌,然后在后续调用 fetchPlacefromPlaceID: 时传递该令牌以及地点 ID,以检索用户选择的地点的地点详情。
  • GMSAutocompleteFilter 用于:
    • 使结果偏向于或限制在特定地区。
    • 将结果限制为特定地点类型
    • GMSPlaceLocationBias/Restriction 对象,用于使结果偏向于由纬度和经度边界指定的特定区域。
  • 用于处理返回的预测结果的回调方法。

以下代码示例展示了对 findAutocompletePredictionsFromQuery: 的调用。

Swift

/**
 * Create a new session token. Be sure to use the same token for calling
 * findAutocompletePredictions, as well as the subsequent place details request.
 * This ensures that the user's query and selection are billed as a single session.
 */
let token = GMSAutocompleteSessionToken.init()

// Create a type filter.
let filter = GMSAutocompleteFilter()
filter.types = [.bank] 
filter.locationBias = GMSPlaceRectangularLocationOption( northEastBounds,
                                   southWestBounds);

placesClient?.findAutocompletePredictions(fromQuery: "cheesebu",

                                          filter: filter,
                                          sessionToken: token,
                                          callback: { (results, error) in
    if let error = error {
      print("Autocomplete error: \(error)")
      return
    }
    if let results = results {
      for result in results {
        print("Result \(result.attributedFullText) with placeID \(result.placeID)")
      }
    }
})

Objective-C

/**
 * Create a new session token. Be sure to use the same token for calling
 * findAutocompletePredictionsFromQuery:, as well as the subsequent place details request.
 * This ensures that the user's query and selection are billed as a single session.
 */
GMSAutocompleteSessionToken *token = [[GMSAutocompleteSessionToken alloc] init];

// Create a type filter.
GMSAutocompleteFilter *_filter = [[GMSAutocompleteFilter alloc] init];
_filter.types = @[ kGMSPlaceTypeBank ];

[_placesClient findAutocompletePredictionsFromQuery:@"cheesebu"
filter:_filter sessionToken:token callback:^(NSArray<GMSAutocompletePrediction *> * _Nullable results, NSError * _Nullable error) {
  if (error != nil) {
    NSLog(@"An error occurred %@", [error localizedDescription]);
    return;
  }
  if (results != nil) {
    for (GMSAutocompletePrediction *result in results) {
      NSLog(@"Result %@ with PlaceID %@", result.attributedFullText, result.placeID);
    }
  }
}];

该 API 会调用指定的回调方法,传入 GMSAutocompletePrediction 对象数组。

每个 GMSAutocompletePrediction 对象都包含以下信息:

  • attributedFullText - 预测结果的完整文本,格式为 NSAttributedString。例如,“悉尼歌剧院,悉尼新南威尔士州”。每个与用户输入匹配的文本范围都有一个 kGMSAutocompleteMatchAttribute 属性。例如,您可以使用此属性突出显示用户查询中的匹配文本,如下所示。
  • placeID - 预测地点的地点 ID。地点 ID 是唯一标识地点的文本标识符。如需详细了解地点 ID,请参阅地点 ID 概览
  • distanceMeters - 从指定 origin 到目的地的直线距离。如果未设置 origin 属性,则系统不会返回任何距离值。

以下代码示例说明了如何使用 enumerateAttribute 以粗体形式突出显示结果中与用户查询中的文本匹配的那部分内容:

Swift

let regularFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
let boldFont = UIFont.boldSystemFont(ofSize: UIFont.labelFontSize)

let bolded = prediction.attributedFullText.mutableCopy() as! NSMutableAttributedString
bolded.enumerateAttribute(kGMSAutocompleteMatchAttribute, in: NSMakeRange(0, bolded.length), options: []) {
  (value, range: NSRange, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
    let font = (value == nil) ? regularFont : boldFont
    bolded.addAttribute(NSFontAttributeName, value: font, range: range)
}

label.attributedText = bolded
    

Objective-C

UIFont *regularFont = [UIFont systemFontOfSize:[UIFont labelFontSize]];
UIFont *boldFont = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];

NSMutableAttributedString *bolded = [prediction.attributedFullText mutableCopy];
[bolded enumerateAttribute:kGMSAutocompleteMatchAttribute
                   inRange:NSMakeRange(0, bolded.length)
                   options:0
                usingBlock:^(id value, NSRange range, BOOL *stop) {
                  UIFont *font = (value == nil) ? regularFont : boldFont;
                  [bolded addAttribute:NSFontAttributeName value:font range:range];
                }];

label.attributedText = bolded;
    

使用提取器

如果您想从头开始构建自己的自动补全控件,可以使用 GMSAutocompleteFetcher,它会在 GMSPlacesClient 中封装 autocompleteQuery 方法。提取器会限制请求,仅返回最近输入的搜索文本的结果。它不提供 UI 元素。

如需实现 GMSAutocompleteFetcher,请按以下步骤操作:

  1. 实现 GMSAutocompleteFetcherDelegate 协议。
  2. 创建一个 GMSAutocompleteFetcher 对象。
  3. 在用户输入时对提取器调用 sourceTextHasChanged
  4. 使用 didAutcompleteWithPredictionsdidFailAutocompleteWithError 协议方法处理预测和错误。

以下代码示例演示了如何使用提取器获取用户输入并在文本视图中显示地点匹配项。其中省略了用于选择地点的功能。FetcherSampleViewController 衍生自 FetcherSampleViewController.h 中的 UIViewController

Swift

import UIKit
import GooglePlaces

class ViewController: UIViewController {

  var textField: UITextField?
  var resultText: UITextView?
  var fetcher: GMSAutocompleteFetcher?

  override func viewDidLoad() {
    super.viewDidLoad()

    view.backgroundColor = .white
    edgesForExtendedLayout = []

    // Set bounds to inner-west Sydney Australia.
    let neBoundsCorner = CLLocationCoordinate2D(latitude: -33.843366,
                                                longitude: 151.134002)
    let swBoundsCorner = CLLocationCoordinate2D(latitude: -33.875725,
                                                longitude: 151.200349)

    // Set up the autocomplete filter.
    let filter = GMSAutocompleteFilter()
    filter.locationRestriction = GMSPlaceRectangularLocationOption(neBoundsCorner, swBoundsCorner)

    // Create a new session token.
    let token: GMSAutocompleteSessionToken = GMSAutocompleteSessionToken.init()

    // Create the fetcher.
    fetcher = GMSAutocompleteFetcher(bounds: nil, filter: filter)
    fetcher?.delegate = self
    fetcher?.provide(token)

    textField = UITextField(frame: CGRect(x: 5.0, y: 10.0,
                                          width: view.bounds.size.width - 5.0,
                                          height: 64.0))
    textField?.autoresizingMask = .flexibleWidth
    textField?.addTarget(self, action: #selector(textFieldDidChange(textField:)),
                         for: .editingChanged)
    let placeholder = NSAttributedString(string: "Type a query...")

    textField?.attributedPlaceholder = placeholder

    resultText = UITextView(frame: CGRect(x: 0, y: 65.0,
                                          width: view.bounds.size.width,
                                          height: view.bounds.size.height - 65.0))
    resultText?.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
    resultText?.text = "No Results"
    resultText?.isEditable = false

    self.view.addSubview(textField!)
    self.view.addSubview(resultText!)
  }

  @objc func textFieldDidChange(textField: UITextField) {
    fetcher?.sourceTextHasChanged(textField.text!)
  }

}

extension ViewController: GMSAutocompleteFetcherDelegate {
  func didAutocomplete(with predictions: [GMSAutocompletePrediction]) {
    let resultsStr = NSMutableString()
    for prediction in predictions {
      resultsStr.appendFormat("\n Primary text: %@\n", prediction.attributedPrimaryText)
      resultsStr.appendFormat("Place ID: %@\n", prediction.placeID)
    }

    resultText?.text = resultsStr as String
  }

  func didFailAutocompleteWithError(_ error: Error) {
    resultText?.text = error.localizedDescription
  }
}

Objective-C

#import "FetcherSampleViewController.h"
#import <GooglePlaces/GooglePlaces.h>
#import <GoogleMapsBase/GoogleMapsBase.h>

@interface FetcherSampleViewController () <GMSAutocompleteFetcherDelegate>

@end

@implementation FetcherSampleViewController {
  UITextField *_textField;
  UITextView *_resultText;
  GMSAutocompleteFetcher* _fetcher;
}

- (void)viewDidLoad {
  [super viewDidLoad];

  self.view.backgroundColor = [UIColor whiteColor];
  self.edgesForExtendedLayout = UIRectEdgeNone;

  // Set bounds to inner-west Sydney Australia.
  CLLocationCoordinate2D neBoundsCorner = CLLocationCoordinate2DMake(-33.843366, 151.134002);
  CLLocationCoordinate2D swBoundsCorner = CLLocationCoordinate2DMake(-33.875725, 151.200349);

  GMSAutocompleteFilter *autocompleteFilter = [[GMSAutocompleteFilter alloc] init];
  autocompleteFilter.locationRestriction =
        GMSPlaceRectangularLocationOption(neBoundsCorner, swBoundsCorner);

  // Create the fetcher.
  _fetcher = [[GMSAutocompleteFetcher alloc] initWithBounds:nil
                                                     filter:filter];
  _fetcher.delegate = self;

  // Set up the UITextField and UITextView.
  _textField = [[UITextField alloc] initWithFrame:CGRectMake(5.0f,
                                                             0,
                                                             self.view.bounds.size.width - 5.0f,
                                                             44.0f)];
  _textField.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  [_textField addTarget:self
                 action:@selector(textFieldDidChange:)
       forControlEvents:UIControlEventEditingChanged];
  _resultText =[[UITextView alloc] initWithFrame:CGRectMake(0,
                                                            45.0f,
                                                            self.view.bounds.size.width,
                                                            self.view.bounds.size.height - 45.0f)];
  _resultText.backgroundColor = [UIColor colorWithWhite:0.95f alpha:1.0f];
  _resultText.text = @"No Results";
  _resultText.editable = NO;
  [self.view addSubview:_textField];
  [self.view addSubview:_resultText];
}

- (void)textFieldDidChange:(UITextField *)textField {
  NSLog(@"%@", textField.text);
  [_fetcher sourceTextHasChanged:textField.text];
}

#pragma mark - GMSAutocompleteFetcherDelegate
- (void)didAutocompleteWithPredictions:(NSArray *)predictions {
  NSMutableString *resultsStr = [NSMutableString string];
  for (GMSAutocompletePrediction *prediction in predictions) {
      [resultsStr appendFormat:@"%@\n", [prediction.attributedPrimaryText string]];
  }
  _resultText.text = resultsStr;
}

- (void)didFailAutocompleteWithError:(NSError *)error {
  _resultText.text = [NSString stringWithFormat:@"%@", error.localizedDescription];
}

@end

会话令牌

会话令牌将用户自动补全搜索的查询和选择阶段归入不同的会话,以便进行结算。会话在用户开始输入查询内容时开始,并在用户选择地点时结束。每个会话可以有多个查询,后跟一个地点选择。会话结束后,令牌将失效;您的应用必须为每个会话生成全新令牌。我们建议您为所有程序化自动补全会话使用会话令牌(当您使用全屏控制器或结果控制器时,API 会自动处理这种情况)。

Places SDK for iOS 使用 GMSAutocompleteSessionToken 来标识每个会话。您的应用应在开始每个新会话时传递新的会话令牌,然后在后续调用 fetchPlacefromPlaceID: 时传递同一令牌以及地点 ID,以检索用户选择的地点的地点详情。

详细了解会话令牌

使用以下代码生成新的会话令牌:

let token: GMSAutocompleteSessionToken = GMSAutocompleteSessionToken.init()

用量限额

在应用中显示提供方说明

  • 如果您的应用以编程方式使用自动补全服务,您的界面必须显示“由 Google 提供支持”提供方说明,或者显示在 Google 品牌地图中。
  • 如果您的应用使用自动补全界面控件,则无需执行任何其他操作(默认显示所需的提供方说明)。
  • 如果您在按 ID 获取地点后检索和显示其他地点信息,则还必须显示第三方提供方说明。

如需了解详情,请参阅有关归因的文档。

控制网络活动指示器

如需控制应用状态栏中的网络活动指示标志,您必须为您正在使用的自动补全类实现适当的可选委托方法,并自行开启和关闭网络指示标志。

  • 对于 GMSAutocompleteViewController,您必须实现委托方法 didRequestAutocompletePredictions:didUpdateAutocompletePredictions:
  • 对于 GMSAutocompleteResultsViewController,您必须实现委托方法 didRequestAutocompletePredictionsForResultsController:didUpdateAutocompletePredictionsForResultsController:
  • 对于 GMSAutocompleteTableDataSource,您必须实现委托方法 didRequestAutocompletePredictionsForTableDataSource:didUpdateAutocompletePredictionsForTableDataSource:

通过实现这些方法并将 [UIApplication sharedApplication].networkActivityIndicatorVisible 分别设置为 YESNO,状态栏将与自动补全界面正确地匹配。

限制自动补全结果

您可以设置自动补全界面控件,将结果限制为特定地理区域,并且/或者将结果过滤为一个或多个地点类型,或者按特定国家/地区进行过滤。如需限制结果,您可以执行以下操作:

  • 如需优先显示指定区域内的偏差(偏差),请在 GMSAutocompleteFilter 上设置 locationBias(系统可能仍会返回来自定义区域外的一些结果)。如果还设置了 locationRestriction,则 locationBias 将被忽略。
  • 如需仅显示(限制)所定义区域内的结果,请在 GMSAutocompleteFilter 上设置 locationRestriction(仅返回指定区域内的结果)。

    • 注意:此限制仅适用于整个路线,可能会根据与位置限制重叠的路线返回位于矩形边界之外的合成结果。
  • 如需仅返回符合特定地点类型的结果,请在 GMSAutocompleteFilter 上设置 type(例如,指定 TypeFilter.ADDRESS 将使该 widget 仅返回具有精确地址的结果)。

  • 如需仅返回最多五个指定国家/地区内的结果,请在 GMSAutocompleteFilter 上设置 countries

使结果偏向于特定区域

如需优先使用所定义区域中的结果(偏向),请在 GMSAutocompleteFilter 上设置 locationBias,如下所示:

  northEast = CLLocationCoordinate2DMake(39.0, -95.0);
  southWest = CLLocationCoordinate2DMake(37.5, -100.0);
  GMSAutocompleteFilter *filter = [[GMSAutocompleteFilter alloc] init];
  filter.locationBias = GMSPlaceRectangularLocationOption(northEast, southWest);

将结果范围限制在特定区域

如需仅显示(限制)所定义区域内的结果,请在 GMSAutocompleteFilter 上设置 locationRestriction,如下所示:

  northEast = CLLocationCoordinate2DMake(39.0, -95.0);
  southWest = CLLocationCoordinate2DMake(37.5, -100.0);
  GMSAutocompleteFilter *filter = [[GMSAutocompleteFilter alloc] init];
  filter.locationRestriction = GMSPlaceRectangularLocationOption(northEast, southWest);

按国家/地区过滤结果

如需过滤最多五个指定国家/地区中的结果,请在 GMSAutocompleteFilter 上设置 countries,如下所示:

  GMSAutocompleteFilter *filter = [[GMSAutocompleteFilter alloc] init];
  filter.countries = @[ @"au", @"nz" ];

按地点类型或类型集合过滤结果

通过设置 GMSAutoCompleteFiltertypes 属性,将结果限制为特定类型或类型集合。使用此属性指定表 1、2 和 3 中列出的地点类型过滤条件。若未指定任何类型,系统将返回所有类型。

如需指定类型或类型集合过滤条件,请执行以下操作:

  • 使用 types 属性可以为地点类型上显示的表 1 和表 2 最多指定五个类型值。类型值由 GMSPlaceType 中的常量定义。

  • 使用 types 属性指定地点类型上显示的表 3 中的类型集合。类型集合值由 GMSPlaceType 中的常量定义。

    只能在请求中使用表 3 中的一种类型。如果您指定了表 3 中的值,则无法指定表 1 或表 2 中的值。否则,会发生错误。

例如,如需仅返回符合特定地点类型的结果,请在 GMSAutocompleteFilter 上设置 types。以下示例展示了如何将过滤条件设置为仅返回具有精确地址的结果:

  GMSAutocompleteFilter *filter = [[GMSAutocompleteFilter alloc] init];
  filter.types = @[ kGMSPlaceTypeAirport, kGMSPlaceTypeAmusementPark ];

地点自动补全优化

本部分介绍了帮助您充分利用地点自动补全服务的最佳实践。

下面列出了一些一般准则:

  • 若要开发可正常运行的界面,最快的方式是使用 Maps JavaScript API 自动补全 widget、Places SDK for Android 自动补全 widget 或 Places SDK for iOS 自动补全界面控件
  • 从一开始就了解重要的地点自动补全数据字段
  • 位置自定义调整和位置限制字段是可选的,但可能会对自动补全性能产生重大影响。
  • 使用错误处理可确保您的应用在 API 返回错误时妥善降级。
  • 请确保您的应用可以处理没有选择任何内容的情况,并能够让用户继续操作。

费用优化最佳实践

基本费用优化

为了优化地点自动补全服务的使用费用,请在地点详情和地点自动补全 widget 中使用字段掩码,以便仅返回所需的地点数据字段

高级费用优化

请考虑通过程序化方式实现地点自动补全,以便采用“按请求”定价模式,并请求关于所选地点(而不是地点详情)的 Geocoding API 结果。如果同时满足以下两个条件,与采用“按会话”(基于会话)定价模式相比,将“按请求”定价模式与 Geocoding API 搭配使用的性价比更高:

  • 如果您只需要用户所选地点的纬度/经度或地址,那么采用 Geocoding API 提供此类信息所需的费用会低于调用地点详情。
  • 如果用户平均在不超过四次自动补全预测请求之内选择自动补全预测结果,那么“按请求”定价模式可能会比“按会话”定价模式的性价比更高。
如果在选择符合您需求的地点自动补全实现方面需要帮助,请选择与以下问题的答案相对应的标签页。

除了所选预测结果的地址和纬度/经度外,您的应用是否需要任何其他信息?

是,需要更多详细信息

将基于会话的地点自动补全与地点详情搭配使用
由于您的应用需要地点名称、商家状态或营业时间等地点详情,因此您的地点自动补全实现应使用会话令牌(以程序化方式构建,或内置到 JavaScriptAndroidiOS widget 中),总费用为每次会话 0.017 美元,外加相应的地点数据 SKU 费用(具体取决于您申请的地点数据字段)。1

widget 实现
会话管理功能自动内置于 JavaScriptAndroidiOS widget 中。这包括针对所选预测结果的“地点自动补全”请求和“地点详情”请求。请务必指定 fields 参数,以确保您只请求所需的地点数据字段

程序化实现
在“地点自动补全”请求中使用会话令牌。在请求关于所选预测结果的地点详情时,添加以下参数:

  1. “地点自动补全”响应中的地点 ID
  2. “地点自动补全”请求中使用的会话令牌
  3. fields 参数,用于指定您所需的地点数据字段

否,只需要地址和位置信息

对于您的应用,Geocoding API 可能比地点详情性价比更高,具体取决于您使用地点自动补全的性能。每个应用的自动补全效率各有不同,具体取决于用户输入的内容、使用应用所在的位置,以及是否遵循了性能优化最佳实践

为了回答以下问题,请分析用户在您的应用中选择地点自动补全预测结果之前平均会输入多少个字符。

平均而言,用户是否能够在不超过四次请求之内选择地点自动补全预测结果?

兼容

在不使用会话令牌的情况下以程序化方式实现地点自动补全,并针对所选的地点预测调用 Geocoding API。
Geocoding API 提供地址和纬度/经度坐标,价格为每个请求 0.005 美元。发出四次地点自动补全 - 按请求结算请求的费用为 0.01132 美元,因此四次请求加上针对所选地点预测调用 Geocoding API 的总费用将为 0.01632 美元,低于按会话结算的自动补全价格(即每次会话 0.017 美元)。1

您可以考虑采用性能最佳实践,帮助用户以更少的字符找到所需的预测结果。

将基于会话的地点自动补全与地点详情搭配使用
由于您预计在用户选择地点自动补全预测结果之前应用发出的平均请求次数较多,所需费用会超过“按会话”定价模式的费用,因此建议您在实现地点自动补全时,针对“地点自动补全”请求和关联的“地点详情”请求使用会话令牌,总费用为每次会话 0.017 美元。1

widget 实现
会话管理功能自动内置于 JavaScriptAndroidiOS widget 中。这包括针对所选预测结果的“地点自动补全”请求和“地点详情”请求。请务必指定 fields 参数,以确保您只请求基本数据字段。

程序化实现
在“地点自动补全”请求中使用会话令牌。在请求关于所选预测结果的地点详情时,添加以下参数:

  1. “地点自动补全”响应中的地点 ID
  2. “地点自动补全”请求中使用的会话令牌
  3. fields 参数,用于指定地址和几何图形等基本数据字段

考虑延迟“地点自动补全”请求
您可以采取一些策略,例如延迟“地点自动补全”请求,直到用户输入前三个或四个字符,从而减少应用发出的请求数量。例如,在用户输入第三个字符后针对每个字符发出“地点自动补全”请求,也就是说,如果用户输入七个字符,然后选择了您发出一次 Geocoding API 请求所获得的预测结果,总费用将为:0.01632 美元(4 * 0.00283 美元 [自动补全 - 按请求结算] + 0.005 美元 [地理编码])。1

如果延迟请求会导致平均程序化请求次数低于四次,您可以按照使用 Geocoding API 提高地点自动补全性能的实现指南操作。请注意,如果用户希望每次输入新的字符后都能看到预测结果,可能会将延迟请求视为时间上的延迟。

您可以考虑采用性能最佳实践,帮助用户以较少的字符找到所需的预测结果。


  1. 此处所列的费用以美元为单位。如需了解完整的定价信息,请参阅 Google Maps Platform 结算页面。

性能最佳实践

下面的指南介绍了优化地点自动补全性能的方式:

  • 向地点自动补全实现添加国家/地区限制、位置自定义调整和语言偏好设置(适用于程序化实现)。您无需为 widget 进行语言偏好设置,因为它们会从用户的浏览器或移动设备中选择语言偏好设置。
  • 如果地点自动补全附有地图,您可以根据地图视口来设置位置偏向。
  • 如果用户未选择任一自动补全预测结果,通常是因为这些预测结果都不是所需的结果地址,您可以重复使用原始用户输入来尝试获取更相关的结果: 适合回退到 Geocoding API 的其他场景包括:
    • 在地点自动补全支持不完整的国家/地区(例如捷克、爱沙尼亚和立陶宛)输入的 subpremise 地址的用户。例如,捷克语地址“Stroupeīnického 3191/17, Praha”会在地点自动补全中生成部分预测结果。
    • 用户在输入地址时,需要输入路段前缀,例如纽约的“23-30 29th St, Queens”或夏威夷考爱岛“47-380 Kamehameha Hwy, Kaneohe”。

问题排查

虽然会发生各种错误,但您的应用遇到的大部分错误通常是因配置错误(例如使用的 API 密钥不正确或 API 密钥配置不正确)或配额错误(您的应用已超出其配额)导致的。如需详细了解配额,请参阅用量限额

使用自动补全控件时发生的错误会在各种委托协议的 didFailAutocompleteWithError() 方法中返回。提供的 NSError 对象的 code 属性设置为 GMSPlacesErrorCode 枚举的其中一个值。