修改导航界面

使用 Navigation SDK for iOS,您可以确定将哪些内置界面控件和元素显示在地图上以及允许哪些手势,从而修改用户与地图的互动体验。您还可以修改导航界面的视觉外观。如需了解对导航界面可接受的修改准则,请参阅“政策”页面

地图界面控件

Navigation SDK 提供了一些与 Google 地图 iOS 应用中提供的内置界面控件类似的内置界面控件。您可以使用 GMSUISettings 类切换这些控件的显示状态。您对此类所做的更改会立即体现在地图上。

罗盘

Navigation SDK 提供的罗盘图形会在特定情况下显示在地图右上角(仅在启用时)。仅当相机的朝向使得其具有非正北方位(非零方位)时,才会显示罗盘。当用户点击罗盘时,相机会以动画呈现方式恢复到方位角为零的(默认朝向)位置,并且罗盘会在之后不久逐渐消失。

如果导航已启用且相机模式设为“跟随”,罗盘将保持可见状态,点按罗盘即可在倾斜视图和概览视图之间切换。

罗盘默认处于停用状态。您可以通过将 GMSUISettingscompassButton 属性设为 true 来启用罗盘。不过,您无法强制罗盘始终显示。

mapView.settings.compassButton = true

mapView.settings.compassButton = YES;

“我的位置”按钮

仅当启用了“我的位置”按钮时,“我的位置”按钮才会显示在屏幕右下角。当用户点击该按钮时,如果用户当前的位置已知,相机会以动画效果聚焦到用户的当前位置。您可以通过将 GMSUISettingsmyLocationButton 属性设为 true 来启用该按钮。

mapView.settings.myLocationButton = true

mapView.settings.myLocationButton = YES;

“重新将当前位置设为地图的中心”按钮

启用导航功能后,当用户滚动地图视图时,重新设置中心按钮会显示,当用户点按以重新设置地图中心时,该按钮会消失。如需允许显示“最近用过”按钮,请将 GMSUISettingsrecenterButtonEnabled 属性设置为 true。如需禁止显示“最近用过”按钮,请将 recenterButtonEnabled 设置为 false

mapView.settings.isRecenterButtonEnabled = true

mapView.settings.recenterButtonEnabled = YES;

地图界面配件

Navigation SDK 提供在导航期间显示的界面配件,这些配件与 Google 地图 iOS 应用中显示的配件类似。您可以按照本部分所述调整这些控件的可见性或视觉外观。您在此处所做的更改会在用户的下一次行程中体现出来。

导航期间,导航标题会显示在屏幕顶部,导航页脚会显示在底部。导航标题会显示路线上下一个转弯的街道名称和方向,以及下一个转弯的方向。导航页脚会显示到达目的地的预计时间和距离,以及预计到达时间。

您可以使用以下属性以编程方式切换导航栏标题和页脚的可见性,并设置其颜色:

  • navigationHeaderEnabled - 控制导航标题是否可见(默认值为 true)。
  • navigationFooterEnabled - 控制导航页脚是否可见(默认值为 true)。
  • navigationHeaderPrimaryBackgroundColor - 设置导航标题的主要背景颜色。
  • navigationHeaderSecondaryBackgroundColor - 设置导航标题的辅助背景颜色。

以下代码示例展示了如何为标题和页脚开启可见性,然后将 navigationHeaderPrimaryBackgroundColor 设置为蓝色,将 navigationHeaderSecondaryBackgroundColor 设置为红色。

mapView.settings.isNavigationHeaderEnabled = true
mapView.settings.isNavigationFooterEnabled = true
mapView.settings.navigationHeaderPrimaryBackgroundColor = .blue
mapView.settings.navigationHeaderSecondaryBackgroundColor = .red

mapView.settings.navigationHeaderEnabled = YES;
mapView.settings.navigationFooterEnabled = YES;
mapView.settings.navigationHeaderPrimaryBackgroundColor = [UIColor blueColor];
mapView.settings.navigationHeaderSecondaryBackgroundColor = [UIColor redColor];

您可以通过将辅助导航标题视图替换为您自己的自定义配件视图来自定义应用。为此,您需要创建一个实现 GMSNavigationAccessoryView 协议的视图。此协议有一个必需的方法:-heightForAccessoryViewConstrainedToSize:onMapView:。系统会为您提供给定 mapView 上可用的最大视图大小,并且您必须提供视图所需的高度。

然后,您可以通过调用 setHeaderAccessoryView: 将此视图传递给 mapView。mapView 会以动画效果淡出所有当前视图,然后以动画效果淡入您的自定义视图。导航标题必须可见,这样您的自定义视图才能显示。

如需移除自定义标题配件视图,请将 nil 传递给 setHeaderAccessoryView:

如果您的视图必须随时更改大小,则可以调用 invalidateLayoutForAccessoryView:,并传入需要更改大小的视图。

示例

以下代码示例演示了一个实现 GMSNavigationAccessoryView 协议的自定义视图。然后,此自定义视图用于设置自定义导航栏标题配件视图。

class MyCustomView: UIView, GMSNavigationAccessoryView {

  func heightForAccessoryViewConstrained(to size: CGSize, on mapView: GMSMapView) -> CGFloat {
    // viewHeight gets calculated as the height your view needs.
    return viewHeight
  }

}

let customView = MyCustomView(...)
mapView.setHeaderAccessory(customView)

// At some later point customView changes size.
mapView.invalidateLayout(forAccessoryView: customView)

// Remove the custom header accessory view.
mapView.setHeaderAccessory(nil)

@interface MyCustomView : UIView <GMSNavigationAccessoryView>

@end

@implementation MyCustomView

- (CGFloat)heightForAccessoryViewConstrainedToSize:(CGSize)size onMapView:(GMSMapView *)mapView {
  // viewHeight gets calculated as the height your view needs.
  return viewHeight;
}

@end

MyCustomView *customView = [[MyCustomView alloc] init];
[_mapView setHeaderAccessoryView:customView];

// At some later point customView changes size.
[_mapView invalidateLayoutForAccessoryView:customView];

// Remove the custom header accessory view.
[_mapView setHeaderAccessoryView:nil];

路线列表

您可以在应用中提供分步说明。以下示例展示了一种可能的方式。这些步骤可能会因您自己的实现而异。

  1. GMSNavigator(导航器)上的 setDestinations 成功完成并在导航器上启用 guidanceActive 后,启用入口点按钮。
  2. 当用户点按入口点按钮时,创建一个与 GMSMapView (mapView) 关联的导航器的 GMSNavigationDirectionsListController(控制器)。
  3. 将控制器添加到 UIViewController(视图控制器)的实例,并将 directionsListView 添加为视图控制器的子视图。应像对 UICollectionView 一样调用控制器上的 reloadDatainvalidateLayout 方法。
  4. 将视图控制器推送到应用的视图控制器层次结构。

以下代码示例展示了如何添加 DirectionsListViewController

override func viewDidLoad() {
  super.viewDidLoad()
  // Add the directionsListView to the host view controller's view.
  let directionsListView = directionsListController.directionsListView
  directionsListView.frame = self.view.frame
  self.view.addSubview(directionsListView)
  directionsListView.translatesAutoresizingMaskIntoConstraints = false
  directionsListView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
  directionsListView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
  directionsListView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
  directionsListView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
  ...
}

override func viewWillAppear(_ animated: Bool) {
  super.viewWillAppear(animated)
  // Ensure data is fresh when the view appears.
  directionsListController.reloadData()
  ...
}

override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
  super.willTransition(to: newCollection, with: coordinator)
  // Invalidate the layout during rotation.
  coordinator.animate(alongsideTransition: {_ in
    self.directionsListController.invalidateLayout()
  })
  ...
}

- (void)viewDidLoad {
  [super viewDidLoad];
  // Add the directionsListView to the host view controller's view.
  UIView *directionsListView = _directionsListController.directionsListView;
  directionsListView.frame = self.view.bounds;
  [self.view addSubview:directionsListView];
  directionsListView.translatesAutoresizingMaskIntoConstraints = NO;
  [directionsListView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
  [directionsListView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
  [directionsListView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
  [directionsListView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
  ...
}

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  // Ensure data is fresh when the view appears.
  [_directionsListController reloadData];
  ...
}

- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection
              withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
  [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
  void(^animationBlock)(id <UIViewControllerTransitionCoordinatorContext>context) =
      ^void(id <UIViewControllerTransitionCoordinatorContext>context) {
    [_directionsListController invalidateLayout];
  };
  // Invalidate the layout during rotation.
  [coordinator animateAlongsideTransition:animationBlock
                               completion:nil];
  ...
}

...

行程进度条

导航栏中添加了行程进度条。

行程进度条是一种垂直条,会在导航开始时显示在地图的右后角。启用后,它会显示整个行程的概览,以及用户的目的地和当前位置。

这样,用户无需放大,即可快速预测即将发生的任何问题(例如交通问题)。然后,他们可以根据需要重新规划行程。如果用户重新规划行程,进度条会重置,就像从该点开始新的行程一样。

行程进度条会显示以下状态指示器:

  • 交通状况 - 即将到来的交通状况。

  • 当前位置 - 相应行程中驾驶员的当前位置。

  • 路线已行驶时间 - 行程已行驶部分。

通过在 GMSUISettings 中设置 navigationTripProgressBarEnabled 属性,启用行程进度条。

mapView.settings.isNavigationTripProgressBarEnabled = true

mapView.settings.navigationTripProgressBarEnabled = YES;

交通信号灯和停止标志

导航期间显示的停止标志和红绿灯。

您可以在 mapView 中启用在导航期间显示红绿灯和停止标志的功能,以便为路线和行程操作提供更多背景信息。

默认情况下,Navigation SDK for iOS 中会停用交通信号灯和停止标志。如需启用此功能,请分别调用每个选项的 GMSMapView 设置:showsTrafficLightsshowsStopSigns


mapView.settings.showsTrafficLights = true
mapView.settings.showsStopSigns = true

mapView.settings.showsTrafficLights = YES;
mapView.settings.showsStopSigns = YES;

速度计控制

启用导航功能并将行驶模式设为“驾车”后,Navigation SDK for iOS 会在地图的下角显示一个限速控件,用于显示当前限速。当驾驶员超速时,该控件会展开,显示第二个速度计,其中显示驾驶员的当前速度。

您可以设置提醒级别,以便在驾驶员超出速度限制的指定幅度时更改速度表显示的格式。例如,您可以指定当驾驶员超速 5 英里/小时时,当前速度显示为红色文本,当驾驶员超速 10 英里/小时时,当前速度显示为红色背景。

如需显示限速控制,请将 GMSUISettingsshouldDisplaySpeedometer 属性设置为 true。如需停用限速控件的显示,请将 shouldDisplaySpeedometer 设置为 false

mapView.shouldDisplaySpeedometer = true

mapView.shouldDisplaySpeedometer = YES;

如需详细了解如何为速度表设置提醒,请参阅配置速度表提醒

目的地标记

您可以通过设置 GMSUISettingsshowsDestinationMarkers 属性,显示或隐藏给定路线的目的地标记。以下示例展示了如何关闭目的地标记。

mapView.settings.showsDestinationMarkers = false

mapView.settings.showsDestinationMarkers = NO;

地图体验功能

借助 Navigation SDK,您可以进一步自定义用户的导航体验。您对实例所做的更改会在用户下次更新应用时反映出来。

停用默认地图手势

您可以通过设置 GMSUISettings 类的属性(作为 GMSMapView 的属性提供)来停用地图上的默认手势。您可以编程方式启用或停用以下手势。请注意,停用此手势不会限制对相机设置的程序化访问。

  • scrollGestures - 控制滚动手势是启用还是停用。如果处于启用状态,用户可以通过滑动来平移摄像头。
  • zoomGestures - 控制缩放手势是处于启用还是停用状态。如果启用,用户可以通过双击、双指点按或双指张合来缩放相机。请注意,启用 scrollGestures 后,点按两次或双指张合可能会将摄像头平移到指定点。
  • tiltGestures - 控制是否启用倾斜手势。如果启用,用户可以使用两指垂直向下或向上滑动来倾斜摄像头。
  • rotateGestures - 控制旋转手势是否启用或停用。如果启用,用户可以使用双指旋转手势来旋转相机。

在此示例中,平移和缩放手势均已停用。

mapView.settings.scrollGestures = false
mapView.settings.zoomGestures = false

mapView.settings.scrollGestures = NO;
mapView.settings.zoomGestures = NO;

位置控件和界面元素

您可以使用以下属性,将控件和其他界面元素相对于导航标题和页脚的位置进行定位:

  • navigationHeaderLayoutGuide
  • navigationFooterLayoutGuide

以下代码示例展示了如何使用布局指南在地图视图中放置一对标签:

/* Add a label to the top left, positioned below the header. */
let topLabel = UILabel()
topLabel.text = "Top Left"
mapView.addSubview(topLabel)
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.topAnchor.constraint(equalTo: mapView.navigationHeaderLayoutGuide.bottomAnchor).isActive = true
topLabel.leadingAnchor.constraint(equalTo: mapView.leadingAnchor).isActive = true

/* Add a label to the bottom right, positioned above the footer. */
let bottomLabel = UILabel()
bottomLabel.text = "Bottom Right"
mapView.addSubview(bottomLabel)
bottomLabel.translatesAutoresizingMaskIntoConstraints = false
bottomLabel.bottomAnchor.constraint(equalTo: mapView.navigationFooterLayoutGuide.topAnchor).isActive = true
bottomLabel.trailingAnchor.constraint(equalTo: mapView.trailingAnchor).isActive = true

/* Add a label to the top left, positioned below the header. */
UILabel *topLabel = [[UILabel alloc] init];
topLabel.text = @"Top Left";
[view addSubview:topLabel];
topLabel.translatesAutoresizingMaskIntoConstraints = NO;
[topLabel.topAnchor
    constraintEqualToAnchor:mapView.navigationHeaderLayoutGuide.bottomAnchor].active = YES;
[topLabel.leadingAnchor constraintEqualToAnchor:mapView.leadingAnchor].active = YES;

/* Add a label to the bottom right, positioned above the footer. */
UILabel *bottomLabel = [[UILabel alloc] init];
bottomLabel.text = @"Bottom Right";
[view addSubview:bottomLabel];
bottomLabel.translatesAutoresizingMaskIntoConstraints = NO;
[bottomLabel.bottomAnchor
    constraintEqualToAnchor:mapView.navigationFooterLayoutGuide.topAnchor].active = YES;
[bottomLabel.trailingAnchor constraintEqualToAnchor:mapView.trailingAnchor].active = YES;

隐藏备选路线

如果界面因信息过多而显得杂乱无序,您可以通过显示的备选路线数量少于默认值(2 条)或根本不显示备选路线来减少杂乱。您可以在提取路线之前配置此选项,方法是配置 GMSNavigationRoutingOptions,并使用以下枚举值之一设置 alternateRoutesStrategy

枚举值说明
GMSNavigationAlternateRoutesStrategyAll 默认。最多显示两个备选路线。
GMSNavigationAlternateRoutesStrategyOne 显示一条备选路线(如果有)。
GMSNavigationAlternateRoutesStrategyNone 隐藏备选路线。

示例

以下代码示例演示了如何完全隐藏备选路线。

let routingOptions = GMSNavigationRoutingOptions(alternateRoutesStrategy: .none)
navigator?.setDestinations(destinations,
                           routingOptions: routingOptions) { routeStatus in
  ...
}

GMSNavigationRoutingOptions *routingOptions = [[GMSNavigationRoutingOptions alloc] initWithAlternateRoutesStrategy:GMSNavigationAlternateRoutesStrategyNone];
[navigator setDestinations:destinations
            routingOptions:routingOptions
                  callback:^(GMSRouteStatus routeStatus){...}];