Как изменить интерфейс навигации

С помощью Navigation SDK для iOS можно настраивать удобство использования с картой, определив, какие из встроенных элементов управления пользовательского интерфейса будут отображаться на карте и какие жесты можно использовать. Вы также можете изменить внешний вид интерфейса навигации. Рекомендации по допустимым изменениям интерфейса навигации приведены на странице правил.

Элементы управления пользовательского интерфейса карты

В Navigation SDK предусмотрены встроенные элементы управления, сходные с теми, которые используются в приложении "Google Карты" для iOS. Они отображаются и скрываются с помощью класса GMSUISettings. Изменения, выполненные для этого класса, сразу же отображаются на карте.

Компас

В Navigation SDK предусмотрен значок компаса, который появляется в правом верхнем углу карты при определенных условиях и только если он включен. Когда пользователь нажимает на компас, камера возвращается в положение, при котором направление равно нулю (ориентация по умолчанию), после чего компас скрывается.

Если навигация включена, а режим камеры установлен на "слежение", компас остается видимым, а нажатие на него переключает между наклонным и обзорным режимами камеры.

Чтобы избежать отвлекающих факторов для водителя, компас остается на месте, если заголовок (в режиме портретной съемки) расширяется и конфликтует с положением компаса по умолчанию. Если вы добавите дополнительный элемент управления заголовком или вспомогательное представление заголовка, компас будет скрыт, чтобы избежать конфликтов в интерфейсе.

Компас поддерживает дневной, ночной и тёмный режимы.

Компас включен по умолчанию в версии 11.0.0 и более поздних. Чтобы отключить компас, задайте для свойства compassButton объекта GMSUISettings значение false.

Swift

mapView.settings.compassButton = false

Objective-C

mapView.settings.compassButton = NO;

Кнопка "Мое местоположение"

Кнопка геолокации отображается в правом нижнем углу экрана только, если она включена. При нажатии этой кнопки камера фокусируется на текущем местоположении пользователя, если оно известно. Чтобы включить кнопку, задайте для свойства myLocationButton объекта GMSUISettings значение true.

Swift

mapView.settings.myLocationButton = true

Objective-C

mapView.settings.myLocationButton = YES;

Кнопка центрирования

Если навигация включена, кнопка центрирования появляется, когда пользователь прокручивает карту, и исчезает, когда он нажимает на нее, чтобы центрировать карту. Чтобы кнопка центрирования появилась, задайте для свойства recenterButtonEnabled объекта GMSUISettings значение true. Чтобы кнопка центрирования не появлялась, задайте для recenterButtonEnabled значение false.

Swift

mapView.settings.isRecenterButtonEnabled = true

Objective-C

mapView.settings.recenterButtonEnabled = YES;

Дополнительные элементы интерфейса карты

Navigation SDK предоставляет элементы интерфейса, которые появляются во время навигации, аналогичные тем, которые есть в приложении "Google Карты" для iOS. Вы можете настроить видимость или внешний вид этих элементов управления, как описано в этом разделе. Изменения вступят в силу во время следующей поездки пользователя.

Во время навигации в верхней части экрана появляется заголовок, а в нижней – нижний колонтитул. В заголовке навигации показано название улицы и направление следующего поворота на маршруте, а также направление поворота после него. В нижнем колонтитуле навигации показаны расчетное время и расстояние до пункта назначения, а также расчетное время прибытия.

Вы можете программно включать и отключать видимость верхнего и нижнего колонтитулов навигации, а также задавать их цвета с помощью следующих свойств:

В примере кода ниже показано, как включить видимость верхнего и нижнего колонтитулов, а затем задать для navigationHeaderPrimaryBackgroundColor синий цвет, а для navigationHeaderSecondaryBackgroundColor – красный.

Swift

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

Objective-C

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

Вы можете настроить приложение, заменив заголовок дополнительной навигации на собственное представление. Для этого нужно создать представление, реализующее протокол GMSNavigationAccessoryView. В этом протоколе есть один обязательный метод: -heightForAccessoryViewConstrainedToSize:onMapView:. Вам предоставляется максимально доступный размер для вашего представления в заданном mapView, и вы должны указать высоту, которая требуется для вашего представления.

Затем вы можете передать это представление в mapView, вызвав setHeaderAccessoryView:. mapView анимирует все текущие представления, а затем анимирует ваше пользовательское представление. Чтобы отображался пользовательский вид, заголовок навигации должен быть видимым.

Чтобы удалить специальный заголовок, передайте nil в setHeaderAccessoryView:.

Если размер представления должен измениться, вызовите метод invalidateLayoutForAccessoryView:, передав в него представление, размер которого нужно изменить.

Пример

В примере кода ниже показано специальное представление, в котором реализован протокол GMSNavigationAccessoryView. Затем это представление используется для настройки дополнительного представления заголовка навигации.

Swift

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)

Objective-C

@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];

Цвета заголовка в режиме низкой освещенности

Navigation SDK для iOS рассчитывает рекомендуемый режим освещения на основе времени суток и текущего местоположения устройства и вызывает метод прослушивателя GMSNavigatorListener.didChangeSuggestedLightingMode при изменении рекомендуемого режима (например, на рассвете или закате). Вы можете программно изменить поведение режима низкой освещенности следующими способами:

Список маршрутов

Вы можете предоставить пошаговые инструкции в своем приложении. В следующем примере показан один из возможных способов сделать это. В зависимости от реализации эти шаги могут различаться.

  1. Включите кнопку точки входа после того, как setDestinations в GMSNavigator (навигаторе) успешно завершится и guidanceActive в навигаторе будет включен.
  2. Когда пользователь нажимает кнопку точки входа, создайте GMSNavigationDirectionsListController (контроллер) с навигатором, связанным с GMSMapView (mapView).
  3. Добавьте контроллер в экземпляр UIViewController (контроллер представления), а directionsListView – в качестве подпредставления контроллера представления. Методы reloadData и invalidateLayout в контроллере должны вызываться так же, как и в случае с UICollectionView.
  4. Добавьте контроллер представления в иерархию контроллеров представлений приложения.

В примере ниже показано, как добавить DirectionsListViewController.

Swift

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)
  // Make sure 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()
  })
  ...
}

Objective-C

- (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];
  // Make sure 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];
  ...
}

...

Индикатор выполнения поездки

В навигацию добавлен индикатор выполнения поездки.

Индикатор выполнения поездки – это вертикальная полоса, которая появляется в начале карты при запуске навигации. Если он включен, то на экране показывается обзор всего маршрута, а также пункт назначения и текущее местоположение пользователя.

Это позволяет пользователям заранее узнавать о возможных проблемах, например пробках, не увеличивая масштаб. При необходимости они могут изменить маршрут. Если пользователь меняет маршрут, индикатор выполнения сбрасывается, как если бы поездка началась заново.

На индикаторе выполнения поездки отображаются следующие статусы:

  • Статус трафика – статус трафика на предстоящем участке маршрута.

  • Текущее местоположение – текущее местоположение водителя в поездке.

  • Пройденный маршрут – пройденная часть поездки.

Включите индикатор выполнения поездки, задав свойство navigationTripProgressBarEnabled в GMSUISettings.

Swift

mapView.settings.isNavigationTripProgressBarEnabled = true

Objective-C

mapView.settings.navigationTripProgressBarEnabled = YES;

Расположение индикатора выполнения поездки

  • Левая сторона панели примерно совпадает с левой стороной спидометра, логотипа Google и кнопки "Вернуть в центр" (когда она видна). Ширина – 12 пт.
  • Индикатор выполнения поездки динамически реагирует на вертикальное пространство на экране. Нижний край панели находится на расстоянии 210 пт от нижнего края экрана. Верхняя часть индикатора выполнения поездки находится на расстоянии не менее 170 пт от верхней части экрана, а максимальная высота индикатора составляет 400 пт.
  • Если индикатор выполнения поездки перекрывает заголовок навигации или другие элементы интерфейса навигации, он отображается под ними.

Prompt Visibility API (экспериментальная функция)

Prompt Visibility API позволяет избежать конфликтов между элементами интерфейса, созданными Navigation SDK, и вашими собственными элементами интерфейса. Для этого нужно добавить прослушиватель, который будет получать обратный вызов перед появлением элемента интерфейса Navigation SDK и сразу после его удаления. Дополнительную информацию, в том числе примеры кода, можно найти в разделе Prompt Visibility API на странице Настройка уведомлений о сбоях в реальном времени.

Управление спидометром

Если навигация включена и выбран режим поездки на автомобиле, Navigation SDK для iOS показывает в нижнем углу карты элемент управления с текущим ограничением скорости. Если водитель превышает ограничение скорости, элемент управления разворачивается и показывает второй спидометр с текущей скоростью.

Вы можете настроить уровни предупреждений, чтобы изменить форматирование спидометра, когда водитель превышает ограничение скорости на определенную величину. Например, можно указать, что текущая скорость должна отображаться красным цветом, если водитель превышает ограничение на 5 миль/ч, и на красном фоне, если превышение составляет 10 миль/ч.

Чтобы показать элемент управления ограничением скорости, задайте для свойства shouldDisplaySpeedometer объекта GMSUISettings значение true. Чтобы отключить показ элемента управления ограничением скорости, задайте для shouldDisplaySpeedometer значение false.

Swift

mapView.settings.shouldDisplaySpeedometer = true

Objective-C

mapView.settings.shouldDisplaySpeedometer = YES;

Подробнее о том, как настроить оповещения спидометра…

Маркеры пунктов назначения

Вы можете показать или скрыть маркеры пунктов назначения для заданного маршрута, установив свойство showsDestinationMarkers объекта GMSUISettings. В примере ниже показано, как отключить маркеры пунктов назначения.

Swift

mapView.settings.showsDestinationMarkers = false

Objective-C

mapView.settings.showsDestinationMarkers = NO;

Функции карт

Navigation SDK позволяет дополнительно настраивать навигацию для пользователей. Изменения, которые вы вносите в экземпляр, отражаются при следующем обновлении приложения пользователем.

Выделение пункта назначения и входов

Выделение пункта назначения и входов

Если пункт назначения создан с помощью placeID, здание будет выделено и по возможности будет показан значок входа. Эти визуальные подсказки помогают пользователям различать и переходить к нужному месту назначения.

Чтобы создать целевую страницу с помощью placeID, используйте один из инициализаторов GMSNavigationWaypoint, который принимает placeID. Например, пункты назначения, созданные в руководстве по навигации по маршруту, включают выделение пункта назначения и метки входа, если они доступны.

Показ 3D-зданий во время навигации

Показ 3D-зданий во время навигации

По умолчанию 3D-здания показываются в режиме навигации. Чтобы отключить эту функцию, задайте для параметра buildingsEnabled значение false (Swift) или NO (Objective-C).

Отключение жестов по умолчанию

Вы можете отключить жесты по умолчанию на карте, задав свойства класса GMSUISettings, который доступен как свойство GMSMapView. Ниже перечислены жесты, которые можно включить и отключить программным способом. Обратите внимание, что отключение жеста не ограничивает программный доступ к настройкам камеры.

  • scrollGestures – определяет, включены ли жесты прокрутки. Если этот параметр включен, пользователи могут перемещать камеру, проводя пальцем по экрану.
  • zoomGestures  – определяет, включены ли жесты масштабирования. Если этот параметр включен, пользователи могут дважды нажать на экран, нажать на него двумя пальцами или свести пальцы, чтобы изменить масштаб камеры. Обратите внимание, что двойное нажатие или сведение пальцев при включенных scrollGestures может привести к панорамированию камеры в указанную точку.
  • tiltGestures – определяет, включены ли жесты наклона. Если эта функция включена, пользователи могут наклонять камеру, проводя по экрану двумя пальцами вверх или вниз.
  • rotateGestures  – определяет, включены ли жесты вращения. Если этот параметр включен, пользователи могут поворачивать камеру, вращая карту двумя пальцами.

В этом примере отключены жесты панорамирования и масштабирования.

Swift

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

Objective-C

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

Элементы управления и интерфейса

Вы можете размещать элементы управления и другие элементы интерфейса относительно верхнего и нижнего колонтитулов навигации, используя следующие свойства:

  • navigationHeaderLayoutGuide
  • navigationFooterLayoutGuide
  • bottomTrailingButtonsLayoutGuide

В следующем примере кода показано, как использовать направляющие макета для размещения пары меток в режиме просмотра карты.

Swift

/* 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

Objective-C

/* 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;

Пример использования bottomTrailingButtonsLayoutGuide для размещения кнопки сообщения о помехах в реальном времени приведен в разделе Настройка сообщений о помехах в реальном времени.

Скрыть альтернативные маршруты

Если пользовательский интерфейс перегружен информацией, вы можете уменьшить ее объем, показывая меньше альтернативных маршрутов, чем по умолчанию (два), или не показывая их вообще. Вы можете настроить этот параметр перед получением маршрутов, настроив GMSNavigationRoutingOptions и задав для alternateRoutesStrategy одно из следующих перечисляемых значений:

Перечисляемое значениеОписание
GMSNavigationAlternateRoutesStrategyAll По умолчанию. Показывает до двух альтернативных маршрутов.
GMSNavigationAlternateRoutesStrategyOne Показывает один альтернативный маршрут (если он доступен).
GMSNavigationAlternateRoutesStrategyNone Скрывает альтернативные маршруты.

Пример

В следующем примере кода показано, как полностью скрыть альтернативные маршруты.

Swift

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

Objective-C

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