react-native-maps callout content not updated on change - react-native

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.

Related

How to display overlay when React Native Map Marker On Pressing

I want to display overlay component when pressing the map marker. When pressing the button, overlay is showing fine. But when pressing the marker, it is not showing. Can someone let me know about how to display overlay when pressing the map marker.
Here is the code.
const toggleOverlay = () => {
setVisible(!visible);
};
return(
<View style={styles.container}>
<MapView style={styles.map}
showsUserLocation={true}
followUserLocation={true}
zoomEnabled={true}
showsMyLocationButton={true}
initialRegion={ mapRegion}
provider = {PROVIDER_GOOGLE}
showsTraffic={true}
>
{
locationPoints? locationPoints.map((point) => (
<Marker
coordinate={{
longitude: parseFloat(point.PointLongitude),
latitude: parseFloat(point.PointLatitude)
}}
title= {point.name}
pinColor={'blue'}
**onPress={() => (toggleOverlay)}**
>
</Marker>
))
: null}
</MapView>
<Button title="Confirm Address"
**onPress={toggleOverlay}** />
<Overlay isVisible={visible} onBackdropPress={toggleOverlay}>
<Text>Your Address</Text>
<View>
<Text>Address</Text>
</View>
</Overlay>
</View>
);
try, maybe it works)
{
locationPoints? locationPoints.map((point) => (
<Marker
coordinate={{
longitude: parseFloat(point.PointLongitude),
latitude: parseFloat(point.PointLatitude)
}}
title= {point.name}
pinColor={'blue'}
onPress={toggleOverlay}
/>
))
: null
}

userLocationButton padding - move button only

Using react-native-maps, in my mapView, I have enabled my userLocationButton. You can see this rendered in the bottom right hand corner by default. I would like to be able to position this button specifically without it having an impact on the entire mapView.
I have tried mapPadding which you can see in my example but that seems to move the entire view which you can see by watching the 'google' text in the bottom left by default.
How can I position the userLocation button individually? Also, if I wanted to change the look of the button, how could I render a custom button? Worst case scenario I can just make my own.
I have the code below as well as a snack example here. Please run on IOS.
Thank you for any insight at all!
export default class Map extends React.Component {
render() {
return (
<View style={{...StyleSheet.absoluteFillObject}}>
<MapView
mapPadding={{bottom: 100}}
style={styles.map}
provider={PROVIDER_GOOGLE}
showsUserLocation={true}
showsMyLocationButton={true}>
</MapView>
</View>
);
}
}
const styles = ScaledSheet.create({
map: {
...StyleSheet.absoluteFillObject
},
I have searched before and I couldn't find the answer.
So I custom a button. I create a TouchableOpacity in an absolute view.
<TouchableOpacity
style={styles.circleView}
onPress={() => {
setFollowUser(true);
}}>
<Image
source={Target}
style={{
height: 20,
width: 20,
}}
tintColor={MediumBlue}
/>
</TouchableOpacity>
and in <MapView> I use
onUserLocationChange={userLocation => {
followUser === true &&
mapRef.current.animateToRegion(
{
latitude: userLocation.nativeEvent.coordinate.latitude,
longitude: userLocation.nativeEvent.coordinate.longitude,
latitudeDelta: initLatDelta,
longitudeDelta: initLongDelta,
},
1000,
);
}}
It works for me on Android.
If you dont want to follow user's location everytime, you can call it only once or use react-native-geolocation-service

React Native Maps Go to Marker

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

react native maps marker custom image cannot change from default

I've spent about 5 hours trying to get this to work with many different permutations of code, and then rebuilding. I cannot for the life of me change the default "red pointer" marker as the default marker image in react native maps.
import MapView, { PROVIDER_GOOGLE } from 'react-native-maps';
...
<MapView
provider={PROVIDER_GOOGLE}
style={styles.map}
ref={ref => {this.map = ref;}}
minZoomLevel={4} // default => 0
maxZoomLevel={10} // default => 20
enableZoomControl={true}
showsUserLocation = {true}
showsMyLocationButton = {true}
zoomEnabled = {true}
initialRegion={{
latitude: 37.600425,
longitude: -122.385861,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
>
<MapView.Marker
coordinate={marker.location}
image={require('./images/test.png')} <------ HERE
width={48}
height={48}
/>
</MapView>
The images definitely exist in the right folder, I've tried different image formats png/gif/jpg/svg, I've tried using {{uri:...}} and icon/image, adding and removing width/height attributes. Nothing seems to work. I'm always getting the default red pointer.
Have I missed something obvious?
The project packager/compiler fails when I require an image that doesn't exist, or an unsupported type. It definitely can see the image, but just doesn't do anything with it. Same results on the emulator and on actual device.
image={require('./images/test.png')}
This line just does nothing, as if it's being ignored somehow.
<MapView
provider={PROVIDER_GOOGLE}
style={styles.container}
region={{
latitude: this.state.latitude,
longitude: this.state.longitude,
}}
>
<Marker
coordinate={{
latitude: this.state.latitude,
longitude: this.state.longitude,
}}
description={"This is a marker in React Natve"}
>
<Image source={require('./man_marker.png')} style={{height: 35, width:35 }} />
</Marker>
</MapView>
There are two solutions:
The first solution (recommended)
Resize your marker image with image editor(such as Photoshop,....) and use as icon in marker
To do this, you can make three photos of different sizes (YOUR_MARKER.png , YOUR_MARKER#2x.png , YOUR_MARKER#3x.png) (React Native automatically displays the appropriate item).
This is a good solution if you have a large number of markers.(You can refer here to clarify this)
<Marker
coordinate={ ... }
tracksViewChanges={false}
icon={require('./YOUR_MARKER.png')}
/>
The second solution
As #shubham-raitka said you can use the Image inside the marker
<Marker
coordinate={ ... }
>
<Image source={require('./YOUR_MARKER.png')} style={{height: 35, width:35 }} />
</Marker>
In this case, if your number of markers is high (about 50 or more) the map performance will be very low.Therefore, it is not recommended to use this method
Here's an approach that worked for me in a similar situation: Use Image in place of Marker. Pop-ups work the same as with a marker. If you try this, Image is imported from react-native. The actual image is imported as:
var dotImage = require('./pathToImage.png')
<Marker
coordinate={meter.latlng}
title={"Parking Meter"}
key={idx}
>
<Image
source={dotImage}
style={{height: 6, width: 6}}
/>
</Marker>
The way you give the width and height is a bit strange, please try with this way.
import MapView, { Marker, PROVIDER_GOOGLE } from 'react-native-maps';
const markerImg = require('./images/test.png'); // <-- create a const with the path of the image
<------
------>
<MapView
provider={PROVIDER_GOOGLE}
style={styles.map}
ref={ref => {this.map = ref;}}
minZoomLevel={4} // default => 0
maxZoomLevel={10} // default => 20
enableZoomControl={true}
showsUserLocation = {true}
showsMyLocationButton = {true}
zoomEnabled = {true}
initialRegion={{
latitude: 37.600425,
longitude: -122.385861,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
>
<Marker
image={markerImg} // <----- add this the const with image
onPress={() => this.setState({ marker1: !this.state.marker1 })}
coordinate={{
latitude: 37.600425,
longitude: -122.385861,
}}
centerOffset={{ x: -18, y: -60 }}
anchor={{ x: 0.69, y: 1 }}
/>
</Marker>
</MapView>
I hope it works for you, works for me!
Not enough rep yet to just leave a comment, but the first solution works, I just had to add resizeMode or it cuts off the image if it's bigger.
<Marker
coordinate={ ... }
>
<Image source={require('./YOUR_MARKER.png')} style={{height: 35, width:35, resizeMode:"contain" }} />
</Marker>
<Marker
coordinate={d.driverLocation}
title={d.driverName}
description={d.autoNumber}
onPress={() => console.warn(d.mobaNumbers)}
image={require("../../../assets/bee.png")}
>
</Marker>
Try this it should work
import {Image} from 'react-native';
import MapView, {Marker} from 'react-native-maps';
<MapView
style={styles.mapStyle}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
customMapStyle={mapStyle}>
<Marker
draggable
coordinate={{
latitude: 37.78825,
longitude: -122.4324,
}}
onDragEnd={e => alert(JSON.stringify(e.nativeEvent.coordinate))}
title={'Test Marker'}
description={'This is a description of the marker'}>
<Image
source={require('./assests/custom_marker.png')}
style={{height: 35, width: 35}}
/>
</Marker>
</MapView>

react-native-maps inside Tab not working

I'm having trouble displaying my <MapView /> inside a Tab. I'm using react-native-scrollable-tab-view
The <MapView /> won't display in the map even after using : ...StyleSheet.absoluteFillObject. I can display the map only if I put a fix height. For example: height: 500.
Here is what my sample tab screen looks like.
styles = StyleSheet.create({
tabContent: {
flex: 1,
},
map: {
...StyleSheet.absoluteFillObject,
},
});
class HomeTemporary extends Component {
render() {
return (
<ScrollableTabView
renderTabBar={() => <ScrollableTabBar />}
>
<ScrollView tabLabel="List" style={styles.tabContent}>
<Text>Content Tab One</Text>
</ScrollView>
<ScrollView tabLabel="Map" style={styles.tabContent}>
<MapView
scrollEnabled
style={styles.map}
provider={MapView.PROVIDER_GOOGLE}
initialRegion={{
latitude: 25.2048493,
longitude: 55.2707828,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
/>
</ScrollView>
</ScrollableTabView>
);
}
}
Absolutely about what? If the map inside the view, the view component must also specify the absolute value of the width and height. In your case for tabContent.