ניווט במסלול

במדריך הזה מוסבר איך לתכנן מסלול באפליקציה ליעד יחיד באמצעות Navigation SDK ל-iOS.

סקירה כללית

  1. משלבים את Navigation SDK באפליקציה, כפי שמתואר בקטע הגדרת הפרויקט.
  2. מגדירים GMSMapView.
  3. מבקשים מהמשתמש לאשר את התנאים וההגבלות, ולאשר את שירותי המיקום והתראות ברקע.
  4. יוצרים מערך שמכיל יעד אחד או יותר.
  5. מגדירים GMSNavigator כדי לשלוט במסלולי ניווט מפורטים.

    • מוסיפים יעדים באמצעות setDestinations.
    • כדי להתחיל בניווט, מגדירים את isGuidanceActive ל-true.
    • אפשר להשתמש ב-simulateLocationsAlongExistingRoute כדי לדמות את התקדמות הרכב במסלול, לצורך בדיקה, ניפוי באגים והדגמה של האפליקציה.

הצגת הקוד

/*
 * Copyright 2017 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 {

  var mapView: GMSMapView!
  var locationManager: CLLocationManager!

  override func loadView() {

    locationManager = CLLocationManager()

    // Set up the map view.
    let camera = GMSCameraPosition.camera(withLatitude: 47.67, longitude: -122.20, zoom: 14)
    mapView = GMSMapView.map(withFrame: .zero, camera: camera)

    // 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
        self.mapView.settings.compassButton = 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("User rejected request to display notifications.")
          }
        }
      } else {
        // Handle rejection of terms and conditions.
      }
    }

    view = mapView

    makeButton()
  }

  // Create a route and start guidance.
  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.locationSimulator?.simulateLocationsAlongExistingRoute()
      self.mapView.cameraMode = .following
    }
  }

  // Add a button to the view.
  func makeButton() {
    // A button to 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 2017 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 ()
@end

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

- (void)loadView {

  _locationManager = [[CLLocationManager alloc] init];

  // Set up the map view.
  GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:47.67
                                                          longitude:-122.20
                                                               zoom:14];
  _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];

  // 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;
       _mapView.settings.compassButton = YES;

       // Request authorization to use the current device location.
       [_locationManager requestAlwaysAuthorization];

       // Request authorization for alert notifications which deliver guidance instructions
       // in the background.
       UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
       UNAuthorizationOptions options = UNAuthorizationOptionAlert;
       [center requestAuthorizationWithOptions:options
                             completionHandler:^(BOOL granted, NSError *_Nullable error) {
                               if (!error && granted) {
                                 NSLog(@"iOS Notification Permission: newly Granted");
                               } else {
                                 NSLog(@"iOS Notification Permission: Failed or Denied");
                               }
                             }];
     } else {
       // Handle rejection of 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.locationSimulator simulateLocationsAlongExistingRoute];
                               _mapView.navigator.guidanceActive = YES;
                               _mapView.navigator.sendsBackgroundNotifications = YES;
                               _mapView.cameraMode = GMSNavigationCameraModeFollowing;
                             }];
}

// Add a button to the view.
- (void)makeButton {
  // A button to 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.frame = CGRectMake(5.0, 150.0, 100.0, 35.0);
  [_mapView addSubview:navButton];
}

@end

בקשה מהמשתמש להעניק את ההרשאות הנדרשות

לפני שמשתמשים ב-Navigation SDK, המשתמשים צריכים לאשר את התנאים וההגבלות ולהעניק הרשאה לשימוש בשירותי המיקום, שנדרשים לניווט. אם האפליקציה תפעל ברקע, היא תצטרך גם לבקש מהמשתמש לאשר את ההתראות של הנחיות הנסיעה. בקטע הזה מוסבר איך להציג את הבקשות הנדרשות לאישור.

מתן הרשאה לשירותי המיקום

‏Navigation SDK משתמש בשירותי מיקום, שדורשים הרשאה מהמשתמשים. כדי להפעיל את שירותי המיקום ולהציג את תיבת הדו-שיח של ההרשאה:

  1. מוסיפים את המפתח NSLocationAlwaysUsageDescription אל Info.plist.
  2. בערך, מוסיפים הסבר קצר למה לאפליקציה נדרשים שירותי מיקום. לדוגמה: "האפליקציה הזו זקוקה להרשאה להשתמש בשירותי המיקום לניווט מפורט."

  3. כדי להציג את תיבת הדו-שיח של ההרשאה, צריך להפעיל את requestAlwaysAuthorization() של מופע מנהל המיקום.

self.locationManager.requestAlwaysAuthorization()

[_locationManager requestAlwaysAuthorization];

מתן הרשאה לקבלת התראות על ניווט ברקע

כדי לספק התראות כשהאפליקציה פועלת ברקע, ל-Navigation SDK נדרשת הרשאת משתמש. מוסיפים את הקוד הבא כדי לבקש מהמשתמש הרשאה להציג את ההתראות האלה:

UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) {
  granted, error in
    // Handle denied authorization to display notifications.
    if !granted || error != nil {
      print("User rejected request to display notifications.")
    }
}

// Request authorization for alert notifications.
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNAuthorizationOptions options = UNAuthorizationOptionAlert;
[center requestAuthorizationWithOptions:options
                      completionHandler:
 ^(
   BOOL granted,
   NSError *_Nullable error) {
     if (!error && granted) {
       NSLog(@"iOS Notification Permission: newly Granted");
     } else {
       NSLog(@"iOS Notification Permission: Failed or Denied");
     }
   }];

מאשרים את התנאים וההגבלות

משתמשים בקוד הבא כדי להציג את תיבת הדו-שיח של התנאים וההגבלות ולהפעיל את הניווט כשהמשתמש מאשר את התנאים. שימו לב שהדוגמה הזו כוללת את הקוד לשירותי המיקום ולהתראות על מסלול הגעה (שמוצג למעלה).

  let termsAndConditionsOptions = GMSNavigationTermsAndConditionsOptions(companyName: "Ride Sharing Co.")

  GMSNavigationServices.showTermsAndConditionsDialogIfNeeded(
    with: termsAndConditionsOptions) { termsAccepted in
    if termsAccepted {
      // Enable navigation if the user accepts the terms.
      self.mapView.isNavigationEnabled = true
      self.mapView.settings.compassButton = 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 rejection of notification authorization.
        if !granted || error != nil {
          print("Authorization to deliver notifications was rejected.")
        }
      }
    } else {
      // Handle rejection of terms and conditions.
    }
  }

GMSNavigationTermsAndConditionsOptions *termsAndConditionsOptions = [[GMSNavigationTermsAndConditionsOptions alloc] initWithCompanyName:@"Ride Sharing Co."];

[GMSNavigationServices
  showTermsAndConditionsDialogIfNeededWithOptions:termsAndConditionsOptions
  callback:^(BOOL termsAccepted) {
   if (termsAccepted) {
     // Enable navigation if the user accepts the terms.
     _mapView.navigationEnabled = YES;
     _mapView.settings.compassButton = YES;

     // Request authorization to use the current device location.
     [_locationManager requestAlwaysAuthorization];

     // Request authorization for alert notifications which deliver guidance instructions
     // in the background.
     UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
     UNAuthorizationOptions options = UNAuthorizationOptionAlert;
     [center requestAuthorizationWithOptions:options
                           completionHandler:
     ^(
       BOOL granted,
       NSError *_Nullable error) {
         if (!error && granted) {
           NSLog(@"iOS Notification Permission: newly Granted");
         } else {
           NSLog(@"iOS Notification Permission: Failed or Denied");
         }
       }];
   } else {
     // Handle rejection of the terms and conditions.
   }
 }];

יצירת מסלול והפעלת ההנחיות

כדי לתכנן מסלול, צריך להפעיל את השיטה setDestinations() של Navigator עם מערך שמכיל יעד אחד או יותר (GMSNavigationWaypoint) שרוצים לבקר בו. אם החישוב הצליח, המסלול יוצג במפה. כדי להתחיל בהדרכה לאורך המסלול, החל מהיעד הראשון, מגדירים את isGuidanceActive לערך true ב-callback.

בדוגמה הבאה מוצגים:

  • יצירת מסלול חדש עם שני יעדים.
  • תחילת ההדרכה.
  • הפעלת התראות על הנחיות ברקע.
  • סימולציה של נסיעה במסלול (אופציונלי).
  • מגדירים את מצב המצלמה ל'מעקב' (אופציונלי).

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
    self.mapView.navigator?.isGuidanceActive = true
    self.mapView.locationSimulator?.simulateLocationsAlongExistingRoute()
    self.mapView.cameraMode = .following
  }
}

- (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.locationSimulator simulateLocationsAlongExistingRoute];
                               _mapView.navigator.guidanceActive = YES;
                               _mapView.cameraMode = GMSNavigationCameraModeFollowing;
                             }];
}

מידע נוסף על מזהי מקומות זמין במאמר מזהי מקומות.

הגדרת מצב נסיעה

מצב הנסיעה קובע איזה סוג מסלול יוחזר ואת האופן שבו נקבע המסלול של המשתמש. אפשר להגדיר למסלול אחד מתוך ארבעה אמצעי תחבורה: נהיגה, רכיבה על אופניים, הליכה ומונית. במצב נהיגה ובמצב מונית, המסלול של המשתמש מבוסס על כיוון הנסיעה. במצב רכיבה על אופניים ובמצב הליכה, המסלול מיוצג על ידי הכיוון שאליו פונה המכשיר.

מגדירים את המאפיין travelMode של תצוגת המפה, כפי שמתואר בדוגמה הבאה:

self.mapView.travelMode = .cycling

_mapView.travelMode = GMSNavigationTravelModeCycling;

הגדרת דרכים שיש להימנע מהן

אפשר להשתמש במאפיינים avoidsHighways ו-avoidsTolls BOOL כדי להימנע מכבישים מהירים ו/או מכבישי אגרה לאורך מסלול.

self.mapView.navigator?.avoidsTolls = true

_mapView.navigator.avoidsTolls = YES;

כלי למציאת מזהי מקומות

אתם יכולים להשתמש בכלי לחיפוש מזהי מקומות כדי למצוא מזהי מקומות לשימוש ביעדים במסלולים. מוסיפים יעד מ-placeID באמצעות GMSNavigationWaypoint.

טקסט צף

אפשר להוסיף טקסט צף בכל מקום באפליקציה, כל עוד הוא לא מכסה את השיוך ל-Google. ב-Navigation SDK אין תמיכה בהצמדת הטקסט לקו הרוחב/האורך במפה או לתווית. למידע נוסף, קראו את המאמר חלונות מידע.