I have a react-native-maps MapView which is utilized in a fitToCoordinates function. The function zooms the map to fit a start point (current user location) and an end point. Below is the MapView object:
<MapView
ref={map => { this.map = map; }}
region={{
latitude: this.props.startLatitude,
longitude: this.props.startLongitude,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421
}}
style={styles.mapContainer}
showsCompass={false}
showsUserLocation
>
<MapViewDirections
origin={{ latitude: this.props.startLatitude, longitude: this.props.startLongitude }}
destination={this.props.optimalDestination}
apikey={GOOGLE_API_KEY}
strokeWidth={2}
strokeColor={COLORS.MAIN.hexCode}
/>
{this.walkLine()}
{this.optimalAddressFound()}
{this.fitCoordinates()}
</MapView>
The fitToCoordinates function referenced in the MapView object is as follows:
fitCoordinates() {
if (this.props.optimalAddressFound && this.props.destinationMapFit) {
this.map.fitToCoordinates(
[{ latitude: this.props.startLatitude,
longitude: this.props.startLongitude },
{ latitude: this.props.optimalAddressLat,
longitude: this.props.optimalAddressLng }],
{ edgePadding: { top: 30, right: 30, bottom: 30, left: 30 } }
);
}
}
Everything works fine, except when a user opens the React Navigation Drawer Menu, navigates to another page, and then returns to the Map page. If they do that, the ref to the MapView object becomes undefined and the fitToCoordinates function fails (TypeError: Cannot ready property 'fitToCoordinates' of undefined).
My question is, how can I maintain the map ref if my user navigates to other screens? I have tried creating a function to store the MapView object in my Redux store but it seems that the Redux store cannot accept an object in the Reducer.
if you use react-navigation, you would need to set a key for your Map-Screen to make sure the same screen is reused instead of creating a new one each time.
something like
navigation.navigate({key: 'map-screen', routeName: 'Map'});
Related
[enter image description here][1]I am developing Live tracking App for Android and IOS using google maps APIs and the technology i am using is React native.
I am able to list the multiple maps using an array map method but not all the map routes are coming within the frame only the last map is fitting in the frame view, because mapRef is getting overwritten. How do I update mapRef instead of overwritting.
const mapRef = useRef();
useEffect(() => {
screen();
}, [dataSource]);
const screen = () => {
trips = dataSource.map((val, key) => {
let origin = val.route_origin;
let ori = " "+origin;
let destination = val.route_destination;
let dest = " "+destination;
let newDate = val.trip_date;
{console.log("newdate:",newDate)}
var trip_date = moment(newDate).format('DD/MM/YYYY');
let trip_time = val.trip_time;
<MapView
style={styles.maps}
initialRegion={{
latitude: 13.3379,
longitude: 77.1173,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
ref={mapRef} // **mapRef is the issue here,it is getting overwritten so fitTocoordinates is updating only for the last mapView**
zoomEnabled={true}
>
<MapView.Marker
pinColor={"green"}
coordinate={{
latitude: 12.9716,
longitude:77.5946,
}}
></MapView.Marker>
<MapViewDirections
origin={ori}
destination={dest}
apikey={"*******************************"} // insert your API Key here
strokeWidth={3}
strokeColor="hotpink"
optimizeWaypoints={true}
onReady={(result) => {
mapRef.current.fitToCoordinates(result.coordinates, {
mapPadding: {
right: Dimensions.get("window").width / 40,
bottom: Dimensions.get("window").height / 70,
left: Dimensions.get("window").width / 40,
top: Dimensions.get("window").height / 70,
},
});
}}
/>
</MapView>
})}
console.log("tripsarr",trips);
settrip1(trips);```
[1]: https://i.stack.imgur.com/5TWr3.png
So I am building an app that is refreshing a GPS position with a marker. The problem is that the initial region is not near the marker and will change.
I have a button to refresh the GPS position, is there a way to have the map go to the marker?
I do not see anything in the documentation that states this.
<MapView
style={styles.map}
initialRegion={region}
ref={_map}
onRegionChangeComplete={region => {setRegion(region)}}
>
<Marker
coordinate={{
latitude: lat,
longitude: long,
}}
key={lat}
title="Location"
style={{width: 26, height: 40}}
description={address}
resizeMode='contain'
/>
</MapView>
You can use de asttribute animateToRegio of react-native-maps.
Something like this:
function getLocation(){
Geolocation.getCurrentPosition(position => {
const region = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: 0.0043,
longitudeDelta: 0.0034
};
map.current.animateToRegion(region, 500);
});
};
Then, on a button or whatever you want, you can call that function to go to your position, or you can change latitude and longitude to your marker coords
I am trying to update the content of a <Callout> tag on a google maps view using the react-native-maps npm package. I would like to dynamically load extra content after a map marker is pressed. I have simplified my code to the example below:-
I have already tried using the tracksInfoWindowChanges attribute on the marker, and the showCallout() method to attempt to manually trigger a re-render. I am using android so the iOS only options such as redrawCallout() method are not available.
const MyMap = (props) => {
const [marker, setMarker] = useState({});
return (
<MapView
showsUserLocation
zoomControlEnabled={true}
zoomEnabled={true}
region={{
latitude: 0,
longitude: 0,
latitudeDelta: 0.03,
longitudeDelta: 0.03,
}}>
<Marker
coordinate={{latitude: 0, longitude: 0}}
onPress={(e) => {
setMarker({ title: 'Test'});
}}>
<View>
<Icon name='key' size={40} color='#384963'</Icon>
</View>
<Callout>
<Text>{marker.title}</Text>
</Callout>
</Marker>
</MapView>
);
}
Is it possible to trigger a re-render of the content after the Callout has been rendered once? From debugging I can see that the component renders after I update my marker state, however the content is not visible until I manually close the Callout and re-opened it.
I am having the exact same problem. The suggestion for android, as per this issue, is to use showCallout(). Below is my work-around for this issue using showCallout():
const MyMap = (props) => {
const [marker, setMarker] = useState({});
let markerRef = useRef(null);
useEffect(() => {
if (markerRef.current) {
markerRef.current.showCallout();
}
})
return (
<MapView
showsUserLocation
zoomControlEnabled={true}
zoomEnabled={true}
region={{
latitude: 0,
longitude: 0,
latitudeDelta: 0.03,
longitudeDelta: 0.03,
}}>
<Marker
ref={markerRef}
coordinate={{latitude: 0, longitude: 0}}
onPress={(e) => {
setMarker({title: `Test ${Math.floor(Math.random() * 10)}`});
}}>
<View>
<Icon name='key' size={40} color='#384963' />
</View>
<Callout>
<Text>{marker.title}</Text>
</Callout>
</Marker>
</MapView>
);
}
Briefly, I added a markerRef and call markerRef.current.showCallout() in useEffect(). What this achieves, if I am not mistaken, is that each time MyMap gets re-rendered, the callout will be forced to show again. I also changed the onPress such that the effect of callout re-rendering is more apparent.
Note 1: Each time the marker is pressed, the callout shall have its text updated.
Note 2: If you have multiple markers and callouts, and you want to update only the callout that is currently in view, you will have to add another check in useEffect(). Something like: if (markerRef.current && selectedMarker === 'markerID') to make sure that only the desired callout is re-rendered continuously.
I Have a map marker that is centered on the current user location.
This marker has a draggable property so it it possible to drag
the marker around on the map. My questions is how can i set the current region prop
of the map onDragEnd of the maker. I basically want the user to be able to
drag the marker around and update the map region.
region={{ latitude: this.state.region.latitude, longitude: this.state.region.longitude, latitudeDelta: 0.0922, longitudeDelta: 0.0421 }}
showsUserLocation={true} loadingEnabled={true} followsUserLocation={false}>
<MapView.Marker
onDragEnd={this.onRegionChange(this.state.region)}
//On region change method that gets fires ondragEnd of the marker
onRegionChange=(region) => {
this.setState({region
});
console.log(this.state.region)
}
I have a static map with one marker and want to always display the marker callout.
Some folks advise to use MapView's onLayout, but this doesn't work for some time now.
I'm attempting to solve it with onRegionChangeComplete which sometimes works (it gets called once or twice), but in about 10% cases it gets stuck in a loop and gets called again and again until the app crashes. This happens only on iOS.
This is my code
setMarkerRef = (ref) => {
this.marker = ref
}
showCallout = () => {
this.marker.showCallout()
}
render() {
...
<MapView
style={{...StyleSheet.absoluteFillObject}}
region={{
latitude: latitude,
longitude: longitude,
latitudeDelta: 0.00922,
longitudeDelta: 0.00421,
}}
showsMyLocationButton={false}
showsCompass={false}
showsTraffic={false}
zoomEnabled={false}
rotateEnabled={false}
scrollEnabled={false}
pitchEnabled={false}
onRegionChangeComplete={this.showCallout}
onPress={this.openMap}
>
<MapView.Marker
coordinate={{
latitude: latitude,
longitude: longitude,
}}
title={address}
ref={this.setMarkerRef}
/>
</MapView>
Any idea what's wrong with my approach? Does anyone have a better workaround?
Edit: I want the component to look like this, without the need for user interaction. Simply display a map, marker and callout.
Since you're rendering a fixed map with no intended region change, there's no need for you to listen to regionChangeComplete. It's really not a fun event to work with as its behaviour isn't that consistent.
initialRegion would work for showing your map. Or if you would like more fancy animation, trying using MapView animateToRegion.
Anyway, all you need to listen to is componentDidMount.
I suspect your refs code might be wrong, but anyway this works for me.
componentDidMount () {
this.refs['mymarker'].showCallout()
}
render () {
return (
<MapView>
<MapView.Marker
coordinate={YOUR_COORDINATES}
title='my callout'
ref='mymarker'>
</MapView.Marker>
</MapView>
)
}
If you still have problems, it could be that your title prop in MapView.Marker isn't receiving the address you are passing in when showCallout was called. In such a case, the call out doesn't render.
Updated Answer: (seemed like the above wasn't working for Apple Maps)
render () {
return (
<MapView
onLayout={() => this.refs["mymarker"].showCallout()}>
<MapView.Marker
coordinate={YOUR_COORDINATES}
title='my callout'
ref='mymarker'>
</MapView.Marker>
</MapView>
)
}