一切就绪!

着手开发前,请先阅读我们的开发者文档

激活 Google Places API for iOS

为帮助您起步,我们将引导您在 Google 开发者控制台中先完成几项任务:

  1. 创建或选择项目
  2. 激活 Google Places API for iOS
  3. 创建相应密钥
继续

地点自动填充

Google Places API for iOS 中的自动填充服务会返回预测地点,以响应用户搜索查询。 在用户输入搜索内容时,自动填充服务会返回建议地点,例如商家、地址和景点。

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

添加自动填充 UI 控件

自动填充 UI 控件是内置自动填充功能的搜索对话框。 在用户输入搜索字词时,此控件会显示预测地点列表供您选择。 用户做出选择后,系统会返回 GMSPlace 实例,然后您的应用可以使用此实例获取有关所选地点的详情。

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

添加全屏控件

在需要模态上下文时使用全屏控件,其中自动填充 UI 会临时取代您应用的 UI,直到用户做出选择为止。

此功能由 GMSAutocompleteViewController 类提供。

当用户选择一个地点后,您的应用会收到回调。

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

  1. 在您的主应用中创建一个 UI 元素,以启动自动填充 UI 控件,例如 UIButton 上的触摸处理程序。

  2. 在父视图控制器中实现 GMSAutocompleteViewControllerDelegate 协议。

  3. 创建 GMSAutocompleteViewController 的实例,并将父视图控制器分配为代理属性。

  4. 使用 [self presentViewController...] 表示 GMSAutocompleteViewController

  5. didAutocompleteWithPlace 代理方法中处理用户的选择。

  6. didAutocompleteWithPlacedidFailAutocompleteWithErrorwasCancelled 代理方法中关闭控制器。

下面的示例显示启动 GMSAutocompleteViewController 以响应用户按按钮的一个可行方式。

Swift

import UIKit
import GooglePlaces

class ViewController: UIViewController {

  // Present the Autocomplete view controller when the button is pressed.
  @IBAction func autocompleteClicked(_ sender: UIButton) {
    let autocompleteController = GMSAutocompleteViewController()
    autocompleteController.delegate = self
    present(autocompleteController, animated: true, completion: nil)
  }
}

extension ViewController: GMSAutocompleteViewControllerDelegate {

  // Handle the user's selection.
  func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
    print("Place name: \(place.name)")
    print("Place address: \(place.formattedAddress)")
    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/GooglePlaces.h>

@interface ViewController () <GMSAutocompleteViewControllerDelegate>

@end

@implementation ViewController

...

// Present the autocomplete view controller when the button is pressed.
- (IBAction)onLaunchClicked:(id)sender {
  GMSAutocompleteViewController *acController = [[GMSAutocompleteViewController alloc] init];
  acController.delegate = self;
  [self presentViewController:acController animated:YES completion:nil];
}

// 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 address %@", place.formattedAddress);
  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

添加结果控制器

如果您想要在更大程度上控制文本输入 UI,请使用结果控制器。 结果控制器会根据输入 UI 的主要内容动态切换结果列表的可见性。

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

  1. 创建一个 GMSAutocompleteResultsViewController
  2. 在父视图控制器中实现 GMSAutocompleteResultsViewControllerDelegate 协议,并将父视图控制器分配为代理属性。

  3. 创建一个 UISearchController 对象,作为结果控制器参数在 GMSAutocompleteResultsViewController 中传递。

  4. GMSAutocompleteResultsViewController 设置为 UISearchControllersearchResultsUpdater 属性。

  5. 向应用的 UI 中添加 UISearchControllersearchBar

  6. didAutocompleteWithPlace 代理方法中处理用户的选择。

UISearchController 的搜索栏添加到您应用的 UI 中的方法有以下几种:

将搜索栏添加到导航栏

下面的代码示例演示如何添加结果控制器、将 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;
}

注:为了使搜索栏能正确显示,您应用的视图控制器必须包含在 UINavigationController 内。

将搜索栏添加到视图顶部

下面的代码示例显示将 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;
}

注:为了使搜索栏能正确显示,您应用的视图控制器必须包含在 UINavigationController 内。

使用表数据源

如果您的应用必须支持 iOS 7,那么您可以使用 GMSAutocompleteTableDataSource 类来驱动 UISearchDisplayController 的表视图。

要使用 GMSAutocompleteTableDataSource 显示搜索控制器,请执行以下操作:

  1. 在父视图控制器中实现 GMSAutocompleteTableDataSourceDelegateUISearchDisplayDelegate 协议。

  2. 创建一个 GMSAutocompleteTableDataSource 实例,并将父视图控制器分配为代理属性。

  3. 创建一个 UISearchDisplayController. 实例

  4. 向应用的 UI 中添加 UISearchControllersearchBar

  5. didAutocompleteWithPlace 代理方法中处理用户的选择。

  6. didAutocompleteWithPlacedidFailAutocompleteWithErrorwasCancelled 代理方法中关闭控制器。

下面的代码示例演示如何使用 GMSAutocompleteTableDataSource 类驱动 UISearchDisplayController 的表视图。

Swift

import UIKit
import GooglePlaces

class ViewController: UIViewController, UISearchDisplayDelegate {

  var searchBar: UISearchBar?
  var tableDataSource: GMSAutocompleteTableDataSource?
  var searchDisplayController: UISearchDisplayController?

  override func viewDidLoad() {
    super.viewDidLoad()

    searchBar = UISearchBar(CGRect(x: 0, y: 0, width: 250.0, height: 44.0))

    tableDataSource = GMSAutocompleteTableDataSource()
    tableDataSource?.delegate = self

    searchDisplayController = UISearchDisplayController(searchBar: searchBar!, contentsController: self)
    searchDisplayController?.searchResultsDataSource = tableDataSource
    searchDisplayController?.searchResultsDelegate = tableDataSource
    searchDisplayController?.delegate = self

    view.addSubview(searchBar!)
  }

  func didUpdateAutocompletePredictionsForTableDataSource(tableDataSource: GMSAutocompleteTableDataSource) {
    // Turn the network activity indicator off.
    UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    // Reload table data.
    searchDisplayController?.searchResultsTableView.reloadData()
  }

  func didRequestAutocompletePredictionsForTableDataSource(tableDataSource: GMSAutocompleteTableDataSource) {
    // Turn the network activity indicator on.
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    // Reload table data.
    searchDisplayController?.searchResultsTableView.reloadData()
  }

}

extension ViewController: GMSAutocompleteTableDataSourceDelegate {
  func tableDataSource(tableDataSource: GMSAutocompleteTableDataSource, didAutocompleteWithPlace place: GMSPlace) {
    searchDisplayController?.active = false
    // Do something with the selected place.
    print("Place name: \(place.name)")
    print("Place address: \(place.formattedAddress)")
    print("Place attributions: \(place.attributions)")
  }

  func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String?) -> Bool {
    tableDataSource?.sourceTextHasChanged(searchString)
    return false
  }

  func tableDataSource(tableDataSource: GMSAutocompleteTableDataSource, didFailAutocompleteWithError error: NSError) {
    // TODO: Handle the error.
    print("Error: \(error.description)")
  }

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

Objective-C

- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.

  _searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 20, 250.0f, 44.0f)];

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

  _searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar
                                                               contentsController:self];
  _searchDisplayController.searchResultsDataSource = _tableDataSource;
  _searchDisplayController.searchResultsDelegate = _tableDataSource;
  _searchDisplayController.delegate = self;

  [self.view addSubview:_searchBar];
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
  [_tableDataSource sourceTextHasChanged:searchString];
  return NO;
}

// Handle the user's selection.
- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource
  didAutocompleteWithPlace:(GMSPlace *)place {
  [_searchDisplayController setActive:NO animated:YES];
  // Do something with the selected place.
  NSLog(@"Place name %@", place.name);
  NSLog(@"Place address %@", place.formattedAddress);
  NSLog(@"Place attributions %@", place.attributions.string);
}

- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource
  didFailAutocompleteWithError:(NSError *)error {
  [_searchDisplayController setActive:NO animated:YES];
  // TODO: handle the error.
  NSLog(@"Error: %@", [error description]);
}

- (void)didUpdateAutocompletePredictionsForTableDataSource:
  (GMSAutocompleteTableDataSource *)tableDataSource {
  // Turn the network activity indicator off.
  [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
  // Reload table data.
  [_searchDisplayController.searchResultsTableView reloadData];
}

- (void)didRequestAutocompletePredictionsForTableDataSource:
  (GMSAutocompleteTableDataSource *)tableDataSource {
  // Turn the network activity indicator on.
  [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
  // Reload table data.
  [_searchDisplayController.searchResultsTableView reloadData];
}

注:UISearchDisplayController API 不支持异步数据更新的概念,因此,必须通过在 GMSAutoCompleteResultsDelegate 协议的 didUpdateAutocompletePredictionsdidRequestAutocompletePredictions 方法中重新加载表数据,强制执行表更新。

自定义文本和背景颜色

您可以设置自动填充 UI 控件中的所有文本和背景的颜色,以便使小部件的视觉外观与应用的视觉外观更搭配。

设置 UI 控件颜色的方式有以下两种:

  • 在可能的情况下,通过使用原生 iOS UIAppearance 协议全局设置 UI 控件的样式。 这些设置适用于很多(但不是全部)UI 控件元素。

  • 通过使用小部件类上的 SDK 方法来设置不受 UIAppearance 协议支持的属性。

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

自动填充 UI 控件颜色

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

UI 元素 方法 如何设置样式
导航栏着色(背景) UIAppearance 协议 UINavigationBar 代理上调用 setBarTintColor
导航栏着色颜色(搜索栏文本“Carat”和“Cancel”按钮) 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
“由 Google 提供支持” 徽标,“愁云”图像 不适用 根据背景对比度自动选择白色或灰色版本。
搜索栏文本字段中的放大镜和清除文本图标 不适用 要设置样式,请使用所需颜色的图像替换默认图片。

注:这些属性仅影响 UI 元素的颜色设置。 不支持更改 UI 元素的大小或表单元格中使用的字体。

使用 UIAppearance 协议

您可以使用 UIAppearance 协议获取给定 UI 元素的外观代理,然后,您可以使用此代理设置 UI 元素的颜色。

如果进行修改,会影响给定 UI 元素的所有实例。 例如,以下示例会在 UITextField 类包含在 UISearchBar 中时将其颜色全局更改为绿色:

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

要了解有关定义颜色值的更多信息,请参阅 UIColor 类引用

以下代码段显示用于对全屏自动填充 UI 控件中的所有内容设置样式所需的所有代理命令。 将此代码添加到 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;

设置 UI 控件样式属性

UI 控件元素的子集具有不受 UIAppearance 协议影响的属性,因此必须直接进行设置。 下面的代码示例演示如何定义前景和背景颜色,以及将其应用到名为 acController 的 UI 控件实例中。

将此代码添加到 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;

注:对于 GMSAutocompleteViewControllerGMSAutocompleteResultsViewController,您可以使用 Interface Builder 设置这些值。

以编程方式获取地点预测

您可以创建自定义搜索 UI,作为自动填充小部件提供的 UI 的备用 UI。 为此,您的应用必须以编程方式获取地点预测。 您的应用可以通过以下方式之一获取预测地点名称和/或地址列表:

调用 GMSPlacesClient

如需获取预测地点名称和/或地址的列表,请调用 GMSPlacesClient autocompleteQuery:bounds:filter:callback: 方法,传递以下参数:

  • autocompleteQuery 字符串包含用户输入的文本。
  • GMSCoordinateBounds 对象使结果偏向于按纬度和经度范围指定的特定区域。

  • GMSAutocompleteFilter 将结果限定位特定地点类型。 如需检索所有类型,请提供零值。 该过滤器支持下列地点类型:

    • kGMSPlacesAutocompleteTypeFilterNoFilter – 空过滤器;返回所有结果。

    • kGMSPlacesAutocompleteTypeFilterGeocode – 仅返回地理编码结果,而非商家。 使用此请求可以消除指定位置模糊的结果。

    • kGMSPlacesAutocompleteTypeFilterAddress – 仅返回具有精确地址的自动填充结果。 当您知道用户正在寻找完全指定的地址时,使用此类型。

    • kGMSPlacesAutocompleteTypeFilterEstablishment – 仅返回商家地点。

    • kGMSPlacesAutocompleteTypeFilterRegion – 仅返回与以下类型之一匹配的地址:

      • locality
      • sublocality
      • postal_code
      • country
      • administrative_area_level_1
      • administrative_area_level_2
    • kGMSPlacesAutocompleteTypeFilterCity – 仅返回与 localityadministrative_area_level_3 匹配的结果。

如需了解详细信息,请参阅地点类型

  • 用于处理返回的预测的回调方法。

以下代码示例显示 autocompleteQuery:bounds:filter:callback: 的简单调用。

Swift

func placeAutocomplete() {
  let filter = GMSAutocompleteFilter()
  filter.type = .establishment
  placesClient.autocompleteQuery("Sydney Oper", bounds: nil, filter: filter, callback: {(results, error) -> Void 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

- (void)placeAutocomplete {

GMSAutocompleteFilter *filter = [[GMSAutocompleteFilter alloc] init];
filter.type = kGMSPlacesAutocompleteTypeFilterEstablishment;

[_placesClient autocompleteQuery:@"Sydney Oper"
                          bounds:nil
                          filter:filter
                        callback:^(NSArray *results, NSError *error) {
                          if (error != nil) {
                            NSLog(@"Autocomplete error %@", [error localizedDescription]);
                            return;
                          }

                          for (GMSAutocompletePrediction* result in results) {
                            NSLog(@"Result '%@' with placeID %@", result.attributedFullText.string, result.placeID);
                          }
                        }];
}

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

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

  • attributedFullTextNSAttributedString 形式的完整预测文本。 例如,“Sydney Opera House, Sydney, New South Wales, Australia”。 每个匹配用户输入的文本范围都有一个 kGMSAutocompleteMatchAttribute 属性。 举例来说,您可以利用该属性突出显示用户查询中的匹配文本,如下所示。

  • placeID – 预测地点的地点 ID。 地点 ID 是唯一标识地点的文本标识符。 如需了解有关地点 ID 的详细信息,请参阅地点 ID 概览

以下代码示例说明了如何利用 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)
    let bounds = GMSCoordinateBounds(coordinate: neBoundsCorner,
                                     coordinate: swBoundsCorner)

    // Set up the autocomplete filter.
    let filter = GMSAutocompleteFilter()
    filter.type = .establishment

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

    textField = UITextField(frame: CGRect(x: 5.0, y: 10.0,
                                          width: view.bounds.size.width - 5.0,
                                          height: 44.0))
    textField?.autoresizingMask = .flexibleWidth
    textField?.addTarget(self, action: #selector(textFieldDidChange(textField:)),
                         for: .editingChanged)

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

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

  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", prediction.attributedPrimaryText)
    }

    resultText?.text = resultsStr as String
  }

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

}

Objective-C

#import "FetcherSampleViewController.h"
#import <GooglePlaces/GooglePlaces.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);
  GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithCoordinate:neBoundsCorner
                                                                     coordinate:swBoundsCorner];

  // Set up the autocomplete filter.
  GMSAutocompleteFilter *filter = [[GMSAutocompleteFilter alloc] init];
  filter.type = kGMSPlacesAutocompleteTypeFilterEstablishment;

  // Create the fetcher.
  _fetcher = [[GMSAutocompleteFetcher alloc] initWithBounds:bounds
                                                     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

设置自动填充的 GMSCoordinateBounds

您可以为自动填充服务提供 GMSCoordinateBounds,从而为提供的完成项进行引导。 例如,如果您的视图控制器中已有 Google 地图,则可利用当前视口的边界让自动完成结果实现偏向设置。

Swift

func placeAutocomplete() {
  let visibleRegion = mapView.projection.visibleRegion()
  let bounds = GMSCoordinateBounds(coordinate: visibleRegion.farLeft, coordinate: visibleRegion.nearRight)

  let filter = GMSAutocompleteFilter()
  filter.type = .establishment
  placesClient.autocompleteQuery("Sydney Oper", bounds: bounds, filter: filter, callback: {
    (results, error) -> Void in
    guard error == nil else {
      print("Autocomplete error \(error)")
      return
    }
    if let results = results {
      for result in results {
        print("Result \(result.attributedFullText) with placeID \(result.placeID)")
      }
    }
  })
}

Objective-C

- (void)placeAutocomplete {

GMSVisibleRegion visibleRegion = self.mapView.projection.visibleRegion;
GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithCoordinate:visibleRegion.farLeft
                                                                   coordinate:visibleRegion.nearRight];
GMSAutocompleteFilter *filter = [[GMSAutocompleteFilter alloc] init];
filter.type = kGMSPlacesAutocompleteTypeFilterEstablishment;

[_placesClient autocompleteQuery:@"Sydney Oper"
                          bounds:bounds
                          filter:filter
                        callback:^(NSArray *results, NSError *error) {
                          if (error != nil) {
                            NSLog(@"Autocomplete error %@", [error localizedDescription]);
                            return;
                          }

                          for (GMSAutocompletePrediction* result in results) {
                            NSLog(@"Result '%@' with placeID %@", result.attributedFullText.string, result.placeID);
                          }
                        }];
}

使用限制

在应用中显示提供方说明

  • 如果您的应用以编程方式使用自动填充服务,您的 UI 必须显示提供方说明“由 Google 提供支持”,或在带有 Google 品牌的地图中显示。

  • 如果您的应用使用自动填充 UI 空间,无需采取任何其他操作(默认显示所需的提供方说明)。

  • 如果您在按 ID 获取地点后检索并显示其他地点信息,则也必须显示第三方提供方说明。

如需了解详情,请参阅有关提供方说明的文档。

控制网络活动指标

如需控制应用状态栏中的网络活动指标,您必须为正在使用的自动填充类实现适当的可选代理方法,并自行打开和关闭网络指标。

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

通过实现这些方法并将 [UIApplication sharedApplication].networkActivityIndicatorVisible 分别设置为 YESNO,状态栏会正确匹配自动填充 UI。

故障排除

尽管会发生很多错误,但您的应用遇到的大部分错误通常是因配置错误(例如使用的 API 密钥不正确、或 API 密钥配置不正确)或配额错误(您的应用超过其配额)引起的。

如需了解有关配额的详细信息,请参阅使用限制

使用自动填充控件过程中出现的错误会在各种代理协议的 didFailAutocompleteWithError() 方法中返回。 提供的 NSError 对象的 code 属性设置为 GMSPlacesErrorCode 枚举的其中一个值。

发送以下问题的反馈:

此网页
location_on
Google Places API for iOS