الاستماع إلى أحداث التنقّل

استخدِم هذا الدليل لتفعيل تطبيقك للاستماع إلى مجموعة متنوعة من الأحداث والاستجابة لها والتي تتغيّر أثناء تنقّل المستخدم على طول مسار معيّن. لا يتناول هذا الدليل تحديد مسار، بل يتناول فقط الاستجابة للأحداث على طول مسار.

نظرة عامة

تقدّم لك حزمة تطوير البرامج (SDK) لميزة التنقّل في نظام التشغيل iOS مستمعين مرتبطين بالموقع الجغرافي للمستخدم والظروف على طول المسار، بالإضافة إلى بيانات مهمة عن الوقت والمسافة. في أداة التحكّم في العرض الخاصة بالخريطة، يجب أن يعتمد تطبيقك بروتوكولات هذين المستمعَين: GMSRoadSnappedLocationProviderListener و GMSNavigatorListener.

تعرض هذه القائمة طرق معالجة الأحداث المتاحة لأحداث التنقّل:

الاطّلاع على الرمز

/*
 * Copyright 2020 Google Inc. All rights reserved.
 *
 * 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 GoogleNavigation
import UIKit

class ViewController: UIViewController,
  GMSNavigatorListener,
  GMSRoadSnappedLocationProviderListener
{

  var mapView: GMSMapView!
  var locationManager: CLLocationManager!

  override func loadView() {

    locationManager = CLLocationManager()

    let camera = GMSCameraPosition.camera(withLatitude: 47.67, longitude: -122.20, zoom: 14)
    mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)

    // Add listeners for GMSNavigator and GMSRoadSnappedLocationProvider.
    mapView.navigator?.add(self)
    mapView.roadSnappedLocationProvider?.add(self)

    // Set the time update threshold (seconds) and distance update threshold (meters).
    mapView.navigator?.timeUpdateThreshold = 10
    mapView.navigator?.distanceUpdateThreshold = 100

    // Show the terms and conditions.
    let companyName = "Ride Sharing Co."
    GMSNavigationServices.showTermsAndConditionsDialogIfNeeded(
      withCompanyName: companyName
    ) { termsAccepted in
      if termsAccepted {
        // Enable navigation if the user accepts the terms.
        self.mapView.isNavigationEnabled = true

        // Request authorization to use location services.
        self.locationManager.requestAlwaysAuthorization()

        // Request authorization for alert notifications which deliver guidance instructions
        // in the background.
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) {
          granted, error in
          // Handle denied authorization to display notifications.
          if !granted || error != nil {
            print("Authorization to deliver notifications was rejected.")
          }
        }
      } else {
        // Handle the case when the user rejects the terms and conditions.
      }
    }

    view = mapView

    makeButton()
  }

  // Create a route and start guidance.
  @objc func startNav() {
    var destinations = [GMSNavigationWaypoint]()
    destinations.append(
      GMSNavigationWaypoint.init(
        placeID: "ChIJnUYTpNASkFQR_gSty5kyoUk",
        title: "PCC Natural Market")!)
    destinations.append(
      GMSNavigationWaypoint.init(
        placeID: "ChIJJ326ROcSkFQRBfUzOL2DSbo",
        title: "Marina Park")!)

    mapView.navigator?.setDestinations(destinations) { routeStatus in
      guard routeStatus == .OK else {
        print("Handle route statuses that are not OK.")
        return
      }
      self.mapView.navigator?.isGuidanceActive = true
      self.mapView.cameraMode = .following
      self.mapView.locationSimulator?.simulateLocationsAlongExistingRoute()
    }

    mapView.roadSnappedLocationProvider?.startUpdatingLocation()
  }

  // Listener to handle continuous location updates.
  func locationProvider(
    _ locationProvider: GMSRoadSnappedLocationProvider,
    didUpdate location: CLLocation
  ) {
    print("Location: \(location.description)")
  }

  // Listener to handle speeding events.
  func navigator(
    _ navigator: GMSNavigator, didUpdateSpeedingPercentage percentageAboveLimit: CGFloat
  ) {
    print("Speed is \(percentageAboveLimit) above the limit.")
  }

  // Listener to handle arrival events.
  func navigator(_ navigator: GMSNavigator, didArriveAt waypoint: GMSNavigationWaypoint) {
    print("You have arrived at: \(waypoint.title)")
    mapView.navigator?.continueToNextDestination()
    mapView.navigator?.isGuidanceActive = true
  }

  // Listener for route change events.
  func navigatorDidChangeRoute(_ navigator: GMSNavigator) {
    print("The route has changed.")
  }

  // Listener for time to next destination.
  func navigator(_ navigator: GMSNavigator, didUpdateRemainingTime time: TimeInterval) {
    print("Time to next destination: \(time)")
  }

  // Delegate for distance to next destination.
  func navigator(
    _ navigator: GMSNavigator,
    didUpdateRemainingDistance distance: CLLocationDistance
  ) {
    let miles = distance * 0.00062137
    print("Distance to next destination: \(miles) miles.")
  }

  // Delegate for traffic updates to next destination
  func navigator(
    _ navigator: GMSNavigator,
    didUpdate delayCategory: GMSNavigationDelayCategory
  ) {
    print("Delay category to next destination: \(String(describing: delayCategory)).")
  }

  // Delegate for suggested lighting mode changes.
  func navigator(
    _ navigator: GMSNavigator,
    didChangeSuggestedLightingMode lightingMode: GMSNavigationLightingMode
  ) {
    print("Suggested lighting mode has changed: \(String(describing: lightingMode))")

    // Change to the suggested lighting mode.
    mapView.lightingMode = lightingMode
  }

  // Add a button to the view.
  func makeButton() {
    // Start navigation.
    let navButton = UIButton(frame: CGRect(x: 5, y: 150, width: 200, height: 35))
    navButton.backgroundColor = .blue
    navButton.alpha = 0.5
    navButton.setTitle("Start navigation", for: .normal)
    navButton.addTarget(self, action: #selector(startNav), for: .touchUpInside)
    self.mapView.addSubview(navButton)
  }

}
/*
 * Copyright 2020 Google Inc. All rights reserved.
 *
 * 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 "ViewController.h"
@import GoogleNavigation;

@interface ViewController () <GMSNavigatorListener, GMSRoadSnappedLocationProviderListener>

@end

@implementation ViewController {
  GMSMapView *_mapView;
  CLLocationManager *_locationManager;
}

- (void)loadView {

  _locationManager = [[CLLocationManager alloc] init];

  GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:47.67
                                                          longitude:-122.20
                                                               zoom:14];
  _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];

  // Add listeners for GMSNavigator and GMSRoadSnappedLocationProvider.
  [_mapView.navigator addListener:self];
  [_mapView.roadSnappedLocationProvider addListener:self];

  // Set the time update threshold (seconds) and distance update threshold (meters).
  _mapView.navigator.timeUpdateThreshold = 10;
  _mapView.navigator.distanceUpdateThreshold = 100;

  // Show the terms and conditions.
  NSString *companyName = @"Ride Sharing Co.";
  [GMSNavigationServices
   showTermsAndConditionsDialogIfNeededWithCompanyName:companyName
                                              callback:^(BOOL termsAccepted) {
     if (termsAccepted) {
       // Enable navigation if the user accepts the terms.
       _mapView.navigationEnabled = YES;

       // Request authorization to use location services.
       [_locationManager requestAlwaysAuthorization];

     } else {
       // Handle the case when the user rejects the terms and conditions.
     }
   }];

  self.view = _mapView;

  [self makeButton];
}

// Create a route and initiate navigation.
- (void)startNav {
  NSArray<GMSNavigationWaypoint *> *destinations =
  @[[[GMSNavigationWaypoint alloc] initWithPlaceID:@"ChIJnUYTpNASkFQR_gSty5kyoUk"
                                             title:@"PCC Natural Market"],
    [[GMSNavigationWaypoint alloc] initWithPlaceID:@"ChIJJ326ROcSkFQRBfUzOL2DSbo"
                                             title:@"Marina Park"]];

  [_mapView.navigator setDestinations:destinations
                             callback:^(GMSRouteStatus routeStatus){
                               _mapView.navigator.guidanceActive = YES;
                               _mapView.navigator.sendsBackgroundNotifications = YES;
                               _mapView.cameraMode = GMSNavigationCameraModeFollowing;
                               [_mapView.locationSimulator simulateLocationsAlongExistingRoute];
                             }];

  [_mapView.roadSnappedLocationProvider startUpdatingLocation];
}

#pragma mark - GMSNavigatorListener
// Listener for continuous location updates.
- (void)locationProvider:(GMSRoadSnappedLocationProvider *)locationProvider
      didUpdateLocation:(CLLocation *)location {
  NSLog(@"Location: %@", location.description);
}

// Listener to handle speeding events.
- (void)navigator:(GMSNavigator *)navigator
    didUpdateSpeedingPercentage:(CGFloat)percentageAboveLimit {
  NSLog(@"Speed is %f percent above the limit.", percentageAboveLimit);
}

// Listener to handle arrival events.
- (void)navigator:(GMSNavigator *)navigator didArriveAtWaypoint:(GMSNavigationWaypoint *)waypoint {
  NSLog(@"You have arrived at: %@", waypoint.title);
  [_mapView.navigator continueToNextDestination];
  _mapView.navigator.guidanceActive = YES;
}

// Listener for route change events.
- (void)navigatorDidChangeRoute:(GMSNavigator *)navigator {
  NSLog(@"The route has changed.");
}

// Listener for time to next destination.
- (void)navigator:(GMSNavigator *)navigator didUpdateRemainingTime:(NSTimeInterval)time {
  NSLog(@"Time to next destination: %f", time);
}

// Listener for distance to next destination.
- (void)navigator:(GMSNavigator *)navigator
      didUpdateRemainingDistance:(CLLocationDistance)distance {
  double miles = distance * 0.00062137;
  NSLog(@"%@", [NSString stringWithFormat:@"Distance to next destination: %.2f.", miles]);
}

// Listener for traffic updates for next destination
- (void)navigator:(GMSNavigator *)navigator
    didUpdateDelayCategory:(GMSNavigationDelayCategory)delayCategory {
  NSLog(@"Delay category to next destination: %ld.", delayCategory);
}

// Listener for suggested lighting mode changes.
-(void)navigator:(GMSNavigator *)navigator
      didChangeSuggestedLightingMode:(GMSNavigationLightingMode)lightingMode {
  NSLog(@"Suggested lighting mode has changed: %ld", (long)lightingMode);

  // Change to the suggested lighting mode.
  _mapView.lightingMode = lightingMode;
}

#pragma mark - Programmatic UI elements

// Add a button to the view.
- (void)makeButton {
  // Start navigation.
  UIButton *navButton = [UIButton buttonWithType:UIButtonTypeCustom];
  [navButton addTarget:self
                action:@selector(startNav)
      forControlEvents:UIControlEventTouchUpInside];
  [navButton setTitle:@"Navigate" forState:UIControlStateNormal];
  [navButton setBackgroundColor:[UIColor blueColor]];
  [navButton setAlpha:0.5];
  navButton.frame = CGRectMake(5.0, 150.0, 100.0, 35.0);
  [_mapView addSubview:navButton];
}

@end

الإقرار بالامتثال للبروتوكولات المطلوبة

قبل تنفيذ طرق التنقّل، يجب أن يتّبع "محرِّك التحكّم في العرض" البروتوكولات التالية:

SwiftObjective-C

class ViewController: UIViewController, GMSNavigatorListener,
GMSRoadSnappedLocationProviderListener {

@interface ViewController () <GMSNavigatorListener,
GMSRoadSnappedLocationProviderListener>

@end

بعد اعتماد بروتوكولات التنقّل، اضبط المستمعين على عنصر التحكّم في عرض. على سبيل المثال، يمكنك إضافة الرمز التالي إلى الطريقة viewDidLoad().

SwiftObjective-C

mapView.navigator?.add(self) mapView.roadSnappedLocationProvider?.add(self)

[_mapView.navigator addListener:self]; [_mapView.roadSnappedLocationProvider
addListener:self];

تلقّي أو إيقاف تعديلات الموقع الجغرافي

يجب تحديث الموقع الجغرافي لعرض مستوى تقدّم المستخدم على الخريطة.

تعرِض نسخة location السمات التالية:

سمة الموقع الجغرافي الوصف
الارتفاع الارتفاع الحالي
coordinate.latitude إحداثيات خط العرض الحالية التي تمّ اقتصاصها من الطريق
coordinate.longitude إحداثيات خط الطول الحالية التي تم التقاطها من الطريق
دورة اتّجاه الطائرة الحالي بالدرجات
السرعة السرعة الحالية
timestamp تاريخ/وقت القراءة الحالية

لتلقّي تحديثات مستمرة عن الموقع الجغرافي، اتصل بالرقم mapView.roadSnappedLocationProvider.startUpdatingLocation واستخدِم الرمز GMSRoadSnappedLocationProviderListener لمعالجة الحدث didUpdateLocation.

يوضّح المثال التالي طلب الاتصال بالرقم startUpdatingLocation:

SwiftObjective-C

mapView.roadSnappedLocationProvider.startUpdatingLocation()

[_mapView.roadSnappedLocationProvider startUpdatingLocation];

تنشئ التعليمة البرمجية التالية GMSRoadSnappedLocationProviderListener الذي يعالج الحدث didUpdateLocation.

SwiftObjective-C

func locationProvider(_ locationProvider: GMSRoadSnappedLocationProvider,
didUpdate location: CLLocation) { print("Location: \(location.description)") }

-   (void)locationProvider:(GMSRoadSnappedLocationProvider *)locationProvider
    didUpdateLocation:(CLLocation *)location { NSLog(@"Location: %@",
    location.description); }

لتلقّي آخر المعلومات حول الموقع الجغرافي عندما يكون التطبيق في الخلفية، اضبط allowsBackgroundLocationUpdates على true:

SwiftObjective-C

mapView.roadSnappedLocationProvider.allowsBackgroundLocationUpdates = true

 _mapView.roadSnappedLocationProvider.allowsBackgroundLocationUpdates = YES;

رصد أحداث الوصول

يستخدم تطبيقك الحدث didArriveAtWaypoint لرصد حالات الوصول إلى وجهة معيّنة. يمكنك استئناف التوجيه والانتقال إلى نقطة الالتفاف التالية من خلال الضغط على continueToNextDestination()، ثم إعادة تفعيل التوجيه. يجب أن يعيد تطبيقك تفعيل الإرشادات بعد الاتصال بالرقم continueToNextDestination().

بعد أن يطلب التطبيق continueToNextDestination، لن يتوفّر لدى المخطِّط بيانات عن الوجهة السابقة. إذا كنت تريد تحليل معلومات عن رحلة في مسار معيّن، عليك استرداد هذه المعلومات من المخطِّط قبل الاتصال بـ continueToNextDestination().

يعرض مثال الرمز البرمجي التالي طريقة للتعامل مع الحدث didArriveAtWaypoint:

SwiftObjective-C

func navigator(_ navigator: GMSNavigator, didArriveAt waypoint:
GMSNavigationWaypoint) { print("You have arrived at: \(waypoint.title)")
mapView.navigator?.continueToNextDestination()
mapView.navigator?.isGuidanceActive = true }

-   (void)navigator:(GMSNavigator *)navigator
    didArriveAtWaypoint:(GMSNavigationWaypoint *)waypoint { NSLog(@"You have
    arrived at: %@", waypoint.title); [_mapView.navigator
    continueToNextDestination]; _mapView.navigator.guidanceActive = YES; }

تلقّي آخر المعلومات عن تغييرات المسارات

لتلقّي إشعار عند تغيير المسار، أنشئ طريقة لمعالجة الحدث navigatorDidChangeRoute. يمكنك الوصول إلى المسار الجديد باستخدام سمتَي routeLegs وcurrentRouteLeg في GMSNavigator.

SwiftObjective-C

func navigatorDidChangeRoute(_ navigator: GMSNavigator) { print("The route has
changed.") }

-   (void)navigatorDidChangeRoute:(GMSNavigator *)navigator { NSLog(@"The route
    has changed."); }

تلقّي تعديلات على الوقت المتبقّي للوصول إلى الوجهة

لتلقّي تحديثات مستمرة بشأن الوقت المتبقّي للوصول إلى الوجهة، أنشئ طريقة لمعالجة حدث didUpdateRemainingTime. تقدّم المَعلمة time المدّة المقَدَّرة بالثواني للوصول إلى الوجهة التالية.

SwiftObjective-C

func navigator(_ navigator: GMSNavigator, didUpdateRemainingTime time:
TimeInterval) { print("Time to next destination: \(time)") }

-   (void)navigator:(GMSNavigator *)navigator
    didUpdateRemainingTime:(NSTimeInterval)time { NSLog(@"Time to next
    destination: %f", time); }

لضبط الحد الأدنى للتغيير في الوقت المقدَّر للوصول إلى الوجهة التالية، اضبط timeUpdateThreshold على GMSNavigator. يتم تحديد القيمة بالثواني. في حال عدم ضبط هذه السمة، تستخدم الخدمات القيمة التلقائية التي تبلغ ثانية واحدة.

SwiftObjective-C

navigator?.timeUpdateThreshold = 10

navigator.timeUpdateThreshold = 10;

تلقّي آخر المعلومات عن المسافة إلى الوجهة

لتلقّي تحديثات مستمرة عن المسافة إلى الوجهة، أنشئ طريقة لمعالجة حدث didUpdateRemainingDistance. تقدّم المَعلمة distance المسافة المقدَّرة بالكيلومترات إلى الوجهة التالية.

SwiftObjective-C

func navigator(_ navigator: GMSNavigator, didUpdateRemainingDistance distance:
CLLocationDistance) { let miles = distance * 0.00062137 print("Distance to next
destination: \(miles) miles.") }

-   (void)navigator:(GMSNavigator *)navigator
    didUpdateRemainingDistance:(CLLocationDistance)distance { double miles =
    distance * 0.00062137; NSLog(@"%@", [NSString stringWithFormat:@"Distance to
    next destination: %.2f.", miles]); }

لضبط الحد الأدنى للتغيير في المسافة المقدَّرة إلى الوجهة التالية، اضبط سمة distanceUpdateThreshold على GMSNavigator (يتم تحديد القيمة بال متر). في حال عدم ضبط هذه السمة، تستخدم الخدمات القيمة التلقائية التي تبلغ مترًا واحدًا.

SwiftObjective-C

navigator?.distanceUpdateThreshold = 100

navigator.distanceUpdateThreshold = 100;

تلقّي آخر المعلومات عن حركة المرور

لتلقّي تحديثات مستمرة عن تدفق حركة المرور في المسار المتبقّي، أنشئ طريقة لمعالجة الحدث didUpdateDelayCategory. تؤدي الدعوة إلى delayCategoryToNextDestination إلى عرض GMSNavigationDelayCategory الذي يعرض قيمة من 0 إلى 3. تستند التعديلات على الفئة إلى الترتيب الحالي لمستخدم التطبيق. إذا لم تكن بيانات عدد الزيارات متاحة، تعرِض القيمة GMSNavigationDelayCategory القيمة 0. تشير الأرقام من 1 إلى 3 إلى زيادة في معدّل التدفق من خفيف إلى كثيف.

SwiftObjective-C

func navigator(_ navigator: GMSNavigator, didUpdate delayCategory:
GMSNavigationDelayCategory) { print("Traffic flow to next destination:
\(delayCategory)") }

-   (void)navigator:(GMSNavigator *)navigator
    didUpdateDelayCategory:(GMSNavigationDelayCategory)delayCategory {
    NSLog(@"Traffic flow to next destination: %ld", (long)delayCategory); }

تعرِض السمة GMSNavigationDelayCategory مستويات التأخير التالية:

فئة التأخير الوصف
GMSNavigationDelayCategoryNoData 0 - غير متاح، ما مِن بيانات لحركة المرور أو :
المسار
GMSNavigationDelayCategoryHeavy 1 - كثيف.
GMSNavigationDelayCategoryMedium 2 - متوسط
GMSNavigationDelayCategoryLight 3 - الإضاءة

تلقّي آخر المعلومات بشأن السرعة

لتلقّي آخر الأخبار عندما يتجاوز السائق الحدّ الأقصى للسرعة، أنشئ طريقة لمعالجة الحدث didUpdateSpeedingPercentage.

SwiftObjective-C

// Listener to handle speeding events. func navigator( _ navigator:
GMSNavigator, didUpdateSpeedingPercentage percentageAboveLimit: CGFloat ) {
print("Speed is \(percentageAboveLimit) above the limit.") }

// Listener to handle speeding events. - (void)navigator:(GMSNavigator
*)navigator didUpdateSpeedingPercentage:(CGFloat)percentageAboveLimit {
NSLog(@"Speed is %f percent above the limit.", percentageAboveLimit); }

تغيير وضع الإضاءة المقترَح

لتلقّي إشعارات بالتغييرات المقدَّرة في الإضاءة، أنشئ طريقة للتعامل مع حدث didChangeSuggestedLightingMode.

SwiftObjective-C

// Define a listener for suggested changes to lighting mode. func navigator(_
navigator: GMSNavigator, didChangeSuggestedLightingMode lightingMode:
GMSNavigationLightingMode) { print("Suggested lighting mode has changed:
\(String(describing: lightingMode))")

 // Make the suggested change. mapView.lightingMode = lightingMode }

// Define a listener for suggested changes to lighting mode.
-(void)navigator:(GMSNavigator *)navigator didChangeSuggestedLightingMode:
(GMSNavigationLightingMode)lightingMode { NSLog(@"Suggested lighting mode has
changed: %ld", (long)lightingMode);

 // Make the suggested change. _mapView.lightingMode = lightingMode; }