Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
useNavigation as useAppNavigation,
type NavigationProp,
} from '@react-navigation/native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { createStackNavigator } from '@react-navigation/stack';
import { View, Button, Text } from 'react-native';
import { CommonStyles } from './styles/components';
Expand Down Expand Up @@ -48,6 +49,7 @@ export type StackNavigation = NavigationProp<RootStackParamList>;
const HomeScreen = () => {
const { navigate } = useAppNavigation<StackNavigation>();
const isFocused = useIsFocused();
const insets = useSafeAreaInsets();
const [sdkVersion, setSdkVersion] = useState<string>('');

const { navigationController } = useNavigation();
Expand All @@ -67,7 +69,7 @@ const HomeScreen = () => {
}, [navigationController]);

return (
<View style={CommonStyles.centered}>
<View style={[CommonStyles.centered, { paddingBottom: insets.bottom }]}>
{/* SDK Version Display */}
<View style={{ padding: 16, alignItems: 'center' }}>
<Text style={{ fontSize: 16, fontWeight: 'bold', color: '#333' }}>
Expand Down
4 changes: 3 additions & 1 deletion example/src/screens/IntegrationTestsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import React, { useState, useMemo, useCallback } from 'react';
import { Button, Text, View } from 'react-native';
import Snackbar from 'react-native-snackbar';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

import {
type Circle,
Expand Down Expand Up @@ -80,6 +81,7 @@ const IntegrationTestsScreen = () => {
const [failureMessage, setFailuremessage] = useState('');
const [navigationViewController, setNavigationViewController] =
useState<NavigationViewController | null>(null);
const insets = useSafeAreaInsets();

const onMapReady = useCallback(async () => {
console.log('Map is ready, initializing navigator...');
Expand Down Expand Up @@ -220,7 +222,7 @@ const IntegrationTestsScreen = () => {
}, [testStatus, detoxStepNumber]);

return (
<View style={CommonStyles.container}>
<View style={[CommonStyles.container, { paddingBottom: insets.bottom }]}>
<Text>See CONTRIBUTING.md to see how to run integration tests.</Text>
<View style={{ flex: 6, margin: 5 }}>
<NavigationView
Expand Down
5 changes: 3 additions & 2 deletions example/src/screens/MapIdScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
type NavigationViewCallbacks,
useNavigation,
} from '@googlemaps/react-native-navigation-sdk';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import usePermissions from '../checkPermissions';

const MapIdScreen = () => {
Expand All @@ -43,7 +44,7 @@ const MapIdScreen = () => {
useState<NavigationViewController | null>(null);
const [navigationUiEnabled, setNavigationUIEnabled] = useState(true);
const [nightMode, setNightMode] = useState<number>(0); // 0: Auto, 1: Force Day, 2: Force Night

const insets = useSafeAreaInsets();
const { arePermissionsApproved } = usePermissions();
const { navigationController, addListeners, removeListeners } =
useNavigation();
Expand Down Expand Up @@ -172,7 +173,7 @@ const MapIdScreen = () => {
return (
<ScrollView
style={CommonStyles.container}
contentContainerStyle={styles.content}
contentContainerStyle={[styles.content, { paddingBottom: insets.bottom }]}
>
{confirmedMapId === null ? (
// Configuration screen
Expand Down
8 changes: 3 additions & 5 deletions example/src/screens/MultipleMapsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import PagerView, {
type PagerViewOnPageSelectedEvent,
} from 'react-native-pager-view';
import Snackbar from 'react-native-snackbar';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

import {
NavigationInitErrorCode,
Expand Down Expand Up @@ -66,6 +67,7 @@ enum OverlayType {
}

const MultipleMapsScreen = () => {
const insets = useSafeAreaInsets();
const [mapsVisible, setMapsVisible] = useState(true);
const { arePermissionsApproved } = usePermissions();
const [overlayType, setOverlayType] = useState<OverlayType>(OverlayType.None);
Expand All @@ -81,10 +83,6 @@ const MultipleMapsScreen = () => {
const { navigationController, addListeners, removeListeners } =
useNavigation();

useEffect(() => {
console.log('mapViewController1 changed', mapViewController1);
}, [mapViewController1]);

const onArrival = useCallback(
(event: ArrivalEvent) => {
if (event.isFinalDestination) {
Expand Down Expand Up @@ -303,7 +301,7 @@ const MultipleMapsScreen = () => {
}, []);

return arePermissionsApproved ? (
<View style={CommonStyles.container}>
<View style={[CommonStyles.container, { paddingBottom: insets.bottom }]}>
<View style={CommonStyles.buttonContainer}>
<Button
title={mapsVisible ? 'Hide maps' : 'Show maps'}
Expand Down
4 changes: 3 additions & 1 deletion example/src/screens/NavigationScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import React, { useEffect, useState, useMemo, useCallback } from 'react';
import { Button, View } from 'react-native';
import Snackbar from 'react-native-snackbar';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

import {
NavigationInitErrorCode,
Expand Down Expand Up @@ -61,6 +62,7 @@ enum OverlayType {

const NavigationScreen = () => {
const { arePermissionsApproved } = usePermissions();
const insets = useSafeAreaInsets();
const [overlayType, setOverlayType] = useState<OverlayType>(OverlayType.None);
const [mapViewController, setMapViewController] =
useState<MapViewController | null>(null);
Expand Down Expand Up @@ -294,7 +296,7 @@ const NavigationScreen = () => {
};

return arePermissionsApproved ? (
<View style={CommonStyles.container}>
<View style={[CommonStyles.container, { paddingBottom: insets.bottom }]}>
<NavigationView
style={MapStyles.mapView}
androidStylingOptions={MapStylingOptions.android}
Expand Down
2 changes: 1 addition & 1 deletion ios/react-native-navigation-sdk/BaseCarSceneDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ - (void)sceneDidBecomeActive:(UIScene *)scene {
- (void)attachSession {
if ([NavModule sharedInstance] != nil && [[NavModule sharedInstance] hasSession] &&
!_sessionAttached) {
[self.navViewController attachToNavigationSession:[[NavModule sharedInstance] getSession]];
[self.navViewController attachToNavigationSessionIfNeeded];
[self.navViewController setHeaderEnabled:NO];
[self.navViewController setRecenterButtonEnabled:NO];
[self.navViewController setFooterEnabled:NO];
Expand Down
7 changes: 6 additions & 1 deletion ios/react-native-navigation-sdk/NavModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ - (void)initializeSession {
[self->_session.roadSnappedLocationProvider addListener:self];

NavViewModule *navViewModule = [NavViewModule sharedInstance];
[navViewModule attachViewsToNavigationSession:_session];
[navViewModule attachViewsToNavigationSession];

[self onNavigationReady];
}
Expand Down Expand Up @@ -175,6 +175,7 @@ - (void)showTermsAndConditionsDialog {
}

if (self->_session.navigator != nil) {
[self->_session.navigator removeListener:self];
[self->_session.navigator clearDestinations];
self->_session.navigator.guidanceActive = NO;
self->_session.navigator.sendsBackgroundNotifications = NO;
Expand All @@ -186,6 +187,10 @@ - (void)showTermsAndConditionsDialog {

self->_session.started = NO;
self->_session = nil;

NavViewModule *navViewModule = [NavViewModule sharedInstance];
[navViewModule navigationSessionDestroyed];

if (_navigationSessionDisposedCallback) {
_navigationSessionDisposedCallback();
}
Expand Down
20 changes: 17 additions & 3 deletions ios/react-native-navigation-sdk/NavView.m
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,24 @@ - (void)handleGroundOverlayClick:(GMSGroundOverlay *)groundOverlay {

- (void)willMoveToSuperview:(UIView *)newSuperview {
[super willMoveToSuperview:newSuperview];
if (newSuperview == nil && _viewController && self.cleanupBlock) {
// As newSuperview is nil, the view is being removed from its superview,
// call the cleanup block provided by the view manager
if (newSuperview == nil && _viewController) {
// View is being removed from hierarchy, cleanup the view controller
[self cleanup];
}
}

- (void)dealloc {
[self cleanup];
}

- (void)cleanup {
if (self.cleanupBlock) {
self.cleanupBlock(self.reactTag);
self.cleanupBlock = nil;
}

if (_viewController) {
[_viewController.view removeFromSuperview];
_viewController = nil;
}
}
Expand Down
3 changes: 2 additions & 1 deletion ios/react-native-navigation-sdk/NavViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ typedef void (^OnArrayResult)(NSArray *_Nullable result);
- (void)removePolygon:(NSString *)polygonId;
- (void)removeCircle:(NSString *)circleId;
- (void)removeGroundOverlay:(NSString *)overlayId;
- (BOOL)attachToNavigationSession:(GMSNavigationSession *)session;
- (BOOL)attachToNavigationSessionIfNeeded;
- (void)navigationSessionDestroyed;
- (void)onPromptVisibilityChange:(BOOL)visible;
- (void)setTravelMode:(GMSNavigationTravelMode)travelMode;
- (void)setPadding:(UIEdgeInsets)insets;
Expand Down
134 changes: 118 additions & 16 deletions ios/react-native-navigation-sdk/NavViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ @implementation NavViewController {
NSString *_mapId;
MapViewType _mapViewType;
id<INavigationViewCallback> _viewCallbacks;
BOOL _isSessionAttached;
NSNumber *_isNavigationUIEnabled;
}

- (instancetype)initWithMapViewType:(MapViewType)mapViewType {
Expand All @@ -62,17 +64,126 @@ - (void)loadView {
}

_mapView = [[GMSMapView alloc] initWithOptions:options];

if (_mapViewType == NAVIGATION) {
_mapView.navigationEnabled = YES;
}
self.view = _mapView;
_mapView.delegate = self;
}

- (void)viewDidLoad {
[super viewDidLoad];

[_viewCallbacks handleMapReady];
}

- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];

[self attachToNavigationSessionIfNeeded];
}

- (BOOL)attachToNavigationSessionIfNeeded {
// Only attach if view has proper type, state and dimensions (not zero size)
if (_mapViewType != NAVIGATION || _isSessionAttached || _mapView.bounds.size.width == 0 ||
_mapView.bounds.size.height == 0) {
return NO;
}

NavModule *navModule = [NavModule sharedInstance];
if (navModule != nil && [navModule hasSession]) {
[self attachToNavigationSession:[navModule getSession]];
if (navModule == nil || ![navModule hasSession]) {
return NO;
}
dispatch_async(dispatch_get_main_queue(), ^{
[_viewCallbacks handleMapReady];
});

GMSNavigationSession *session = [navModule getSession];
if (_mapView == nil || session == nil) {
return NO;
}

// `enableNavigationWithSession` returns false if TOS is not accepted.
// This should not be possible in normal usage as the NavModule ensures TOS acceptance before
// navigation session creation.
BOOL result = [_mapView enableNavigationWithSession:session];

if (result) {
_mapView.navigationUIDelegate = self;
[self applyStylingOptions];

[self restoreNavigationUIState];

_isSessionAttached = YES;

[self forceInvalidateView];
}

return result;
}

- (void)forceInvalidateView {
if (_mapView) {
// Defer to next run loop to ensure view is properly sized
dispatch_async(dispatch_get_main_queue(), ^{
if (self->_mapView) {
[self->_mapView setNeedsLayout];
[self->_mapView.layer setNeedsDisplay];
}
});
}
}

- (void)restoreNavigationUIState {
if (_mapView) {
if (_isNavigationUIEnabled != nil) {
_mapView.navigationEnabled = [_isNavigationUIEnabled boolValue];
} else {
_mapView.navigationEnabled = _mapViewType == NAVIGATION;
}
}
}

- (void)navigationSessionDestroyed {
_isSessionAttached = NO;
if (_mapView) {
_mapView.navigationUIDelegate = nil;
_mapView.navigationEnabled = NO;
}
}

- (void)cleanup {
_isSessionAttached = NO;

// Remove all delegates to break retain cycles
if (_mapView) {
_mapView.delegate = nil;
_mapView.navigationUIDelegate = nil;
_mapView.navigationEnabled = NO;
[_mapView clear];

[_mapView removeFromSuperview];
_mapView = nil;
}

// Clear local arrays and set to nil
[_markerList removeAllObjects];
[_polylineList removeAllObjects];
[_polygonList removeAllObjects];
[_circleList removeAllObjects];
[_groundOverlayList removeAllObjects];

_markerList = nil;
_polylineList = nil;
_polygonList = nil;
_circleList = nil;
_groundOverlayList = nil;

// Clear callbacks
_viewCallbacks = nil;
}

- (void)dealloc {
[self cleanup];
[self.view removeFromSuperview];
self.view = nil;
}

- (void)mapViewDidTapRecenterButton:(GMSMapView *)mapView {
Expand Down Expand Up @@ -195,6 +306,7 @@ - (void)setNavigationUIEnabled:(BOOL)isEnabled {
if (_mapViewType != NAVIGATION) {
return;
}
_isNavigationUIEnabled = @(isEnabled);
_mapView.navigationEnabled = isEnabled;
}

Expand Down Expand Up @@ -327,16 +439,6 @@ - (void)setSpeedLimitIconEnabled:(BOOL)isEnabled {

#pragma mark - View Controller functions

- (BOOL)attachToNavigationSession:(GMSNavigationSession *)session {
if (_mapViewType != NAVIGATION) {
return NO;
}
BOOL result = [_mapView enableNavigationWithSession:session];
_mapView.navigationUIDelegate = self;
[self applyStylingOptions];
return result;
}

- (void)onPromptVisibilityChange:(BOOL)isVisible {
[_viewCallbacks handlePromptVisibilityChanged:isVisible];
}
Expand Down
3 changes: 2 additions & 1 deletion ios/react-native-navigation-sdk/NavViewModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface NavViewModule : NSObject <RCTBridgeModule>
@property(nonatomic, strong) NSMapTable<NSNumber *, NavViewController *> *viewControllers;

- (void)attachViewsToNavigationSession:(GMSNavigationSession *)session;
- (void)attachViewsToNavigationSession;
- (void)navigationSessionDestroyed;
- (void)informPromptVisibilityChange:(BOOL)visible;
- (void)setTravelMode:(GMSNavigationTravelMode)travelMode;

Expand Down
Loading
Loading