How to Overlay selected custom marker in react-native-maps - react-native

I use react-native-maps to display an array of positions using custom marker. I want to overlay the selected maker, but did not found the right solution. Using position: 'absolute' for the selected marker changes its position in the map.
Here is the code of the MapView:
<MapView
style={styles.map}
region={region} // initial region is user location if myPosition or coordinates of search
// onRegionChange={onRegionChange}
>
<View style={{position: 'absolute'}}>
{data.map((item) => (
<Marker
onPress={() => handleMarkerPress(item)}
coordinate={{
latitude: item.latitude,
longitude: item.longitude,
}}
// image={require('Resources/geoloc.png')}
// pinColor={item.id === visibleItemId ? 'red' : 'blue'}
key={item.id}>
<MosqueMarquer
mosqueImage={item.images}
itemId={item.id}
visibleItemId={visibleItemId}
distance={item.distance_result}
/>
</Marker>
))}
{isUserGeoLoc ? (
<Marker
coordinate={{
latitude: userLocation.latitude,
longitude: userLocation.longitude,
}}
description={'Your are here'}>
<Image
resizeMode="contain"
source={require('Resources/user_location.png')}
style={{tintColor: '#4280ee', height: 25}}
/>
</Marker>
) : null}
</View>
</MapView>
And here is the custom marker code:
const MosqueMarquer = (props) => {
const relativeStyle =
props.itemId == props.visibleItemId
? {position: 'absolute', tintColor: '#428947', color: '#fff', zIndex: 1}
: {position: null, tintColor: '#fff', color: '#3c423d', zIndex: 0};
return (
<ImageBackground
resizeMode="contain"
source={require('Resources/square_marker.png')}
style={{
...styles.imageBackground,
position: relativeStyle.position,
zIndex: relativeStyle.zIndex,
}}
imageStyle={{tintColor: relativeStyle.tintColor}}>
<Text style={{...styles.text, color: relativeStyle.color}}>
{props.distance}m
</Text>
</ImageBackground>
);
};
export default MosqueMarquer;
const styles = StyleSheet.create({
imageBackground: {
width: 70,
height: 70,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: common.FONT_SIZE_H38,
},
});
the selected marker in green is under the none selected one:
THANKS

<XMarksTheSpot coordinates={coordinatesOfYOurMarker} center={center} />
XMarksTheSpot ->
import React from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';
import { Polygon, Polyline, Marker } from 'react-native-maps';
class XMarksTheSpot extends React.Component {
render() {
return (
<View>
<Polygon
coordinates={this.props.coordinates}
strokeColor="rgba(0, 0, 0, 1)"
strokeWidth={3}
/>
<Polyline
coordinates={[this.props.coordinates[0], this.props.coordinates[2]]}
/>
<Polyline
coordinates={[this.props.coordinates[1], this.props.coordinates[3]]}
/>
<Marker coordinate={this.props.center} />
</View>
);
}
}
XMarksTheSpot.propTypes = {
coordinates: PropTypes.array,
center: PropTypes.object,
zIndex: PropTypes.number,
};
export default XMarksTheSpot;

Related

React Native Maps - takeSnapshot not capturing markers

react-native: https://github.com/expo/react-native/archive/sdk-42.0.0.tar.gz
react: 16.13.1
react-native-maps: 0.28.0
I want to get markers as a part of the snapshot. When we use takeSnapshot method all markers are ignored.
const snapshot = this.viewRefTest.takeSnapshot({
format: 'png', // image formats: 'png', 'jpg' (default: 'png')
quality: 0.5, // image quality: 0..1 (only relevant for jpg, default: 1)
result: 'file', // result types: 'file', 'base64' (default: 'file')
});
<MapView
ref={(viewRefTest) => {
this.viewRefTest = viewRefTest;
}}
showsUserLocation={true}
followUserLocation={true}>
<MapView.Marker coordinate={item.location}>
<Image
style={{ width: 30, height: 30 }}
source={require('../../assets/images/trophy.png')}
/>
<Callout style={{ width: 250, flexDirection: 'row', alignItems: 'center' }}>
<Text>$23</Text>
<View>
<Text style={{ fontSize: 12 }}>Custom Text!</Text>
</View>
</Callout>
</MapView.Marker>
</MapView>;
Please let me know the possibility of this.
After too many tries & combinations of adding delays, height/width, I was not able to capture custom markers using takeSnapshot method.
As a workaround, I have used captureRef method of react-native-view-shot
https://github.com/gre/react-native-view-shot
const uri = await captureRef(this.viewRefTest, {
format: "png",
quality: 1
})
<MapView
ref={(viewRefTest) => {
this.viewRefTest = viewRefTest;
}}
showsUserLocation={true}
followUserLocation={true}>
<MapView.Marker coordinate={item.location}>
<Image
style={{ width: 30, height: 30 }}
source={require('../../assets/images/trophy.png')}
/>
<Callout style={{ width: 250, flexDirection: 'row', alignItems: 'center' }}>
<Text>$23</Text>
<View>
<Text style={{ fontSize: 12 }}>Custom Text!</Text>
</View>
</Callout>
</MapView.Marker>
</MapView>
CaptureRef Returns a Promise of the image URI. It helps capture a React Native view to an image. We can mention height, width, quality & format for the captured image.
Could you try use width and height?
const snapshot = this.viewRefTest.takeSnapshot({
width: 500,
height: 500,
format: 'png',
quality: 0.5,
result: 'file',
});
snapshot.then((uri) => {
console.log(uri);
});
You can solve that by creating a Snapshot with "react-native-view-shot"
I think this bug depends on when you call this.viewRefTest.takeSnapshot()
You can check in my https://expo.dev/#duongtungls/expo-map-view-example
I think call takeSnapshot just after map mounted won't get marker or map.
If call after callback onMapReady still need wait some hundreds of milisecond to take fully snapshot for both map and marker.
I hope this example code can help you solve problem.
import { StatusBar } from 'expo-status-bar';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
import MapView, { Marker } from 'react-native-maps';
import { Ionicons } from '#expo/vector-icons';
export default function App() {
const mapRef = useRef(null);
const [uri, setUri] = useState(null);
const takeSnapshot = useCallback(() => {
if (!mapRef || !mapRef.current) {
return;
}
setTimeout(() => {
const snapshot = mapRef.current.takeSnapshot({
format: 'png', // image formats: 'png', 'jpg' (default: 'png')
quality: 0.5, // image quality: 0..1 (only relevant for jpg, default: 1)
result: 'file', // result types: 'file', 'base64' (default: 'file')
});
snapshot.then((uri) => {
setUri(uri);
});
}, 800); // I add some timeout delay because without delay snapnot won't have map or marker.
}, [mapRef]);
return (
<View style={styles.container}>
{!uri && (
<MapView
ref={mapRef}
style={{
width: '100%',
height: '100%',
}}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
onMapReady={takeSnapshot} // I think need wait for map ready to take snapshot but seem still need wait by setTimeout to get fully snapshot
>
<Marker
coordinate={{
latitude: 37.78825,
longitude: -122.4324,
}}
title={'Test'}
>
<Ionicons name="trophy" size={32} color="red" />
<Text>This is a marker</Text>
</Marker>
</MapView>
)}
{uri && (
<Image
style={{
width: '100%',
height: '100%',
resizeMode: 'contain',
borderColor: 'red',
borderWidth: 10,
}}
source={{
uri,
}}
/>
)}
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Snapshot take after map mounted
Snapshot take after onMapReady and 800ms delay
Best regards,

React Native - Image bigger than parent

Like in tinder, i have a screen with 3 Image Pickers. When I pick an image from the gallery, I want it to persist inside the TouchableOpacity container. However, the image overlaps the container.
I have been been trying various things mentioned here but the image still overlaps like in the image below:
What is the problem?
/* eslint-disable react-native/no-inline-styles */
import React, { useState } from 'react'
import {
Text,
View,
Image,
Button,
TouchableOpacity,
StyleSheet,
Dimensions,
} from 'react-native'
import ImagePicker from 'react-native-image-crop-picker'
import { FontAwesomeIcon } from '#fortawesome/react-native-fontawesome'
import { faPlusCircle } from '#fortawesome/free-solid-svg-icons'
import Colors from '../../../constants/Colors'
const { width: WIDTH } = Dimensions.get('window')
const ProfileEdit = () => {
const [photo, setPhoto] = useState(null)
const handleChoosePhoto = () => {
ImagePicker.openPicker({
width: 300,
height: 400,
cropping: true,
}).then((image) => {
setPhoto({
uri: image.path,
width: image.width,
height: image.height,
mime: image.mime,
})
console.log(image)
})
}
return (
<View style={styles.main}>
<View style={styles.button_row}>
<View style={styles.buttonWrapper}>
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
{photo ? (
<Image source={photo} style={styles.photo} />
) : (
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
)}
</TouchableOpacity>
</View>
<View style={styles.buttonWrapper}>
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
</TouchableOpacity>
</View>
<View style={styles.buttonWrapper}>
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
</TouchableOpacity>
</View>
</View>
</View>
)
}
const styles = StyleSheet.create({
main: {
flex: 1,
},
icon: {},
button: {
height: `${100}%`,
backgroundColor: Colors.defaultWhite,
padding: 2,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 3,
},
buttonWrapper: {
width: WIDTH / 3,
padding: 10,
},
button_row: {
flex: 0.3,
flexWrap: 'wrap',
flexDirection: 'row',
},
photo: {
width: WIDTH / 2,
height: WIDTH / 2,
// borderRadius: 3,
resizeMode: 'contain',
},
})
export default ProfileEdit
correct answer as described here
"However because images will try to set the width and height based on the actual size of the image, you need to override these style properties"
<Image
style={{
flex: 1,
alignSelf: 'stretch',
width: undefined,
height: undefined
}}
source={require('../../assets/images/onboarding-how-it-works.png')}
/>
Instead of calculating image width try this:
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
{photo ? (
<View style={styles.photoContainer}>
<Image source={photo} style={styles.photo} />
</View>
) : (
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
)}
</TouchableOpacity>
const styles = StyleSheet.create({
photoContainer: {
// flex: 1
},
photo: {
height: WIDTH / 2,
width: '100%',
resizeMode: 'contain'
}
});
I prefer ImageBackground instead. Below is the code to achieve you the desired View:
<ImageBackground
imageStyle={styles.image}
style={styles.imageContainer}
source={{uri: this.state.imageUri}}>
<TouchableOpacity onPress={this._pickImage} style={{
height: WIDTH/2,
width: WIDTH/2,
justifyContent: "center",
alignItems: "center"}}>
<Entypo name="camera" color={Colors.GREY} size={35}/>
</TouchableOpacity>
</ImageBackground>

React native elements header background image

I made a header using react native elements, and I want to add background image into it. Is there anyway to do it?
I am using react-native-elements: "^0.19.1"
Here is my header code
render() {
return (
<View style={{ paddingTop: Constants.statusBarHeight, backgroundColor: color.light_grey }}>
<Header
leftComponent={this.props.left ? this.props.left : <IconWrapper name='menu' color='black' size={28} style={styles.icon} onPress={() => Actions.drawerOpen()} />}
centerComponent={<Text style={styles.headerText}>{this.props.title}</Text>}
rightComponent={this.props.right ? this.props.right : <IconWrapper name='handbag' type='simple-line-icon' color='black' size={28} style={styles.icon} onPress={() => Actions.BagContents()} />}
outerContainerStyles={[styles.headerOuterContainer, { height: 0.08 * windowHeight() }]}
/>
</View>
)
}
}
You can always create your own <Header/> component, probably will take you more time but you will be able to understand it and edit it as you like. I created a simple Header component to show you how you can accomplish adding a background image to your header. See the snack #abranhe/stackoverflow-56729412
Header.js
import React, { Component } from 'react';
import { View, TouchableOpacity, StyleSheet, Dimensions, ImageBackground } from 'react-native';
export default class Header extends Component {
renderContent() {
return (
<View style={styles.content}>
<View style={styles.left}>{this.props.left}</View>
<View style={styles.center}>{this.props.center}</View>
<View style={styles.right}>{this.props.right}</View>
</View>
);
}
renderHeaderWithImage() {
return (
<ImageBackground style={styles.container} source={this.props.imageSource}>
{this.renderContent()}
</ImageBackground>
);
}
renderHeaderWithoutImage() {
return (
<View style={[{ backgroundColor: '#f8f8f8' }, styles.container]}>
{this.renderContent()}
</View>
);
}
render() {
return this.props.image
? this.renderHeaderWithImage()
: this.renderHeaderWithoutImage();
}
}
const styles = StyleSheet.create({
container: {
top: 0,
position: 'absolute',
width: Dimensions.get('window').width,
backgroundColor: '#f8f8f8',
borderBottom: 1,
borderColor: '#f8f8f8',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.5,
},
content: {
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: Dimensions.get('window').height * 0.03,
height: Dimensions.get('window').height * 0.045,
},
left: {
marginHorizontal: 5,
},
center: {
marginHorizontal: 5,
},
right: {
marginHorizontal: 5,
},
});
and then on when you want to use the Header component you can set the image prop to true, eg:
import React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
import Header from './components/Header';
export default () => {
return (
<View>
<Header
image
imageSource={{ uri: 'https://yourimage.png' }}
left={<Ionicons name="md-arrow-round-back" size={25} />}
center={<Text>Projects</Text>}
right={<Ionicons name="ios-camera" size={25} />}
/>
</View>
);
};
and then if you set the image prop to false you will remove the image from the background.
<Header
image={false}
imageSource={{ uri: 'https://yourimage.png' }}
left={<Ionicons name="md-arrow-round-back" size={25} />}
center={<Text>Projects</Text>}
right={<Ionicons name="ios-camera" size={25} />}
/>
There is ReactNative's component ImageBackground you can use it.
like this,
<ImageBackground source={...} style={{width: '100%', height: '100%'}}>
<Header
leftComponent={this.props.left ? this.props.left : <IconWrapper name='menu' color='black' size={28} style={styles.icon} onPress={() => Actions.drawerOpen()} />}
centerComponent={<Text style={styles.headerText}>{this.props.title}</Text>}
rightComponent={this.props.right ? this.props.right : <IconWrapper name='handbag' type='simple-line-icon' color='black' size={28} style={styles.icon} onPress={() => Actions.BagContents()} />}
outerContainerStyles={[styles.headerOuterContainer, { height: 0.08 * windowHeight() }]}
/>
</ImageBackground>
You can always style it your way.
assuming you are using react-navigation
You need to add a custon header component in navigationOptions,
import { Header } from 'react-navigation';
static navigationOptions = ({ navigation }) => {
return {
header: (props) => <View >
<LinearGradient
style={{ height: '100%', width: '100%', position: 'absolute' }}
start={{ x: 0, y: 1 }} end={{ x: 1, y: 0 }} colors={['#1A9EAE', '#4EAE7C']}
/>
<Header {...props} style={{ backgroundColor: Colors.transparent }} />
</View>,
}
}
This works for me.
<Header
backgroundImage={require("../../assets/images/btn-header-background.png")}
centerComponent={{
text: i18n.t("stats.title"),
style: { fontFamily: "FunctionLH", fontSize: 30, color: "#fff" }
}}
containerStyle={{
backgroundColor: "transparent",
justifyContent: "space-around"
}}
statusBarProps={{ barStyle: "light-content" }}
/>

How to slide down a View in react native

Hi I am new to react native
I have mapview and FlatList in my screen like shown in the below image.
Now I want Mapview to be expanded when i click on it , like the below image
Here is my code
import React, { Component } from 'react';
import MapView, { PROVIDER_GOOGLE, Marker } from 'react-native-maps';
import { View, StyleSheet, Text, TouchableOpacity, Animated, Image } from 'react-native';
import { Actions } from 'react-native-router-flux';
import DoctorsList from './DoctorsList';
import colors from '../styles/colors';
var isHidden = true;
export default class FindDoctorMaps extends Component {
constructor(props) {
super(props);
this.state = {
bounceValue: new Animated.Value(500), //This is the initial position of the subview
buttonText: 'LIST VIEW',
printText: false,
markers: [{
title: 'hello',
coordinates: {
latitude: 17.452,
longitude: 78.3721
},
},
{
title: 'hi',
coordinates: {
latitude: 17.458,
longitude: 78.3731
},
},
{
title: 'gyp',
coordinates: {
latitude: 17.468,
longitude: 78.3631
},
},
{
title: 'nig',
coordinates: {
latitude: 17.568,
longitude: 78.3831
},
},
{
title: 'Yoy',
coordinates: {
latitude: 17.588,
longitude: 78.4831
},
}]
};
}
render() {
return (
<View style={{ flex: 1, backgroundColor: 'white' }} >
<MapView
provider={PROVIDER_GOOGLE}
region={{
latitude: 17.452,
longitude: 78.3721,
latitudeDelta: 0.015,
longitudeDelta: 0.0121,
}}
style={styles.map}
showsUserLocation
followUserLocation
showsMyLocationButton
showsCompass
zoomEnabled
>
{this.state.markers.map(marker => (
<MapView.Marker
coordinate={marker.coordinates}
title={marker.title}
onPress={this.onMarkerPress}
/>
))}
</MapView>
<TouchableOpacity style={styles.filterContainer} onPress={Actions.morefilters}>
<View style={styles.filterStyle}>
<Image style={{ margin: 5 }} source={require('../assets/filters_icon.png')} />
<Text style={{ fontSize: 14, color: colors.darkBlue, fontWeight: 'bold', }}> Filters</Text>
</View>
</TouchableOpacity>
<View>
<TouchableOpacity >
<DoctorsList />
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
map: {
height: 200,
},
textViewStyle: {
fontSize: 14,
color: '#00539d',
fontWeight: 'bold',
margin: 20
},
subView: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: '#FFFFFF',
height: 500,
},
filterContainer: {
top: '20%',
right: 0,
alignSelf: 'flex-end',
position: 'absolute'
},
filterStyle: {
flexDirection: 'row',
backgroundColor: colors.white,
borderRadius: 8,
height: 30,
width: '60%',
alignItems: 'center',
justifyContent: 'center'
}
});
Can any one please help me how to expand Mapview by clicking on it.
or else please share some useful links so that i would be helpful for me
this.state = {
....
showMapFullScreen: false
....
}
render() {
....
<MapView
....
style={height: showMapFullScreen ? "100%" : 200 }
onPress={() => this.setState({ showMapFullScreen : true }) }
....
>
....
<TouchableOpacity
onPress={() => this.setState({ showMapFullScreen : false }) }>
<DoctorsList />
</TouchableOpacity>
.......
}
You can toggle the map height with a flag like shown above. When the user clicks on the map the height becomes full screen when he/she clicks on the list the map's height is reduced and gives space to the list to render.

react-native-maps Bug with children components keep moving location?

https://ufile.io/1bdoq
Bug:
Mapview wrapped children components may or may not leave the parent component when app loads.
Simulator:
iOS iPhone 6
xCode 10
Environment :
react-native-cli: 2.0.1
react-native: 0.57.0
"react-native-maps": "^0.22.1",
import React, { Component } from 'react';
import {View, Text, TouchableOpacity} from 'react-native'
import MapView from 'react-native-maps';
import { SearchBar } from 'react-native-elements'
import FontAwesome5 from 'react-native-vector-icons/dist/FontAwesome5'
export default class Search extends Component {
static navigationOptions = {
title: 'Search',
header: null
};
constructor(props){
super(props);
this.state = {
region: {
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
},
}
}
onRegionChange(region) {
this.setState({ region });
}
render(){
return(
<View style={{flex:1}}>
<MapView
style={{flex: 1}}
onRegionChange={(region) => this.onRegionChange(region)}
initialRegion={this.state.region}
>
<SearchBar
containerStyle={{backgroundColor: 'rgba(0,0,0,0)', borderTopWidth: 0, borderBottomWidth: 0, marginLeft: 10, marginRight: 10}}
inputStyle={{backgroundColor: 'white', marginTop: 30}}
icon={{type: 'material', color: '#86939e', name: 'search',style:{marginTop: 22, zIndex: 9999999}}}
lightTheme
/>
<TouchableOpacity style={{ height: 30, width: 150, backgroundColor:'white', justifyContent: 'center', paddingLeft: 10, paddingRight: 10}}>
<Text style={{textAlign: 'center'}}>Current Location</Text>
</TouchableOpacity>
<TouchableOpacity style={{ height: 30, width: 30, backgroundColor:'blue', justifyContent: 'center'}}>
<FontAwesome5 name="location-arrow" size={20} color="white"/>
</TouchableOpacity>
</MapView>
</View>
)
}
}
please close. READ THE DOCS do not wrap your components within the mapview, but put it underneath, and use absolute.