React Native Expo自定义地图标记Android显示正常iOS不显示问题
React Native Expo 地图Marker图标iOS不显示问题
我正在使用React Native Expo开发跨平台应用,采用react-native-maps渲染带有自定义标记的地图。使用Marker组件的icon属性渲染PNG图片,同时Marker包含一个子Text元素,代码如下:
<Marker key={i} coordinate={{ latitude: gig.location.latitude, longitude: gig.location.longitude, }} icon={require('../assets/map-pin-50pc.png')} onPress={() => { navigation.navigate("GigDetails", { venue: gig.venue, gigName: gig.gigName, image: gig.image, blurb: gig.blurb, isFree: gig.isFree, genre: gig.genre, dateAndTime: { ...gig.dateAndTime }, tickets: gig.tickets, }); }} > <Text style={styles.gigInfo_text}>{gig.genre}</Text> </Marker>
在Android设备上,自定义标记图片和文本均正常显示,但在iOS设备上仅能看到文本,icon图片无法显示。
已尝试的解决方式
- 提前导入图片再传递给icon属性,无效:
import mapPinImage from '../assets/map-pin-50pc.png' // ... icon={mapPinImage} - 检查图片尺寸为41x62像素,大小2.41kb,符合苹果的最大尺寸要求。
完整组件代码
import { FC } from "react"; import { useState, useMemo } from "react"; import { StyleSheet, Text, View, Image, TouchableOpacity, Platform, Dimensions } from "react-native"; import MapView from "react-native-maps"; import { Marker } from "react-native-maps"; import { mapStyle } from "../util/mapStyle"; import { useGigs } from "../hooks/useGigs"; import { AntDesign } from "@expo/vector-icons"; import { format } from "date-fns"; import { mapProps } from "../routes/homeStack"; import { Switch } from 'react-native-paper' import mapPinImage from '../assets/map-pin-50pc.png' type MapScreenNavgationProp = mapProps['navigation'] interface Props { navigation: MapScreenNavgationProp } const GigMap:FC<Props> = ({ navigation }):JSX.Element => { const [selectedDateMs, setSelectedDateMs] = useState<number>(Date.now()); const [isSwitchOn, setIsSwitchOn] = useState(false); const gigs = useGigs(); //生成格式为DD/MM/YYYY的当前日期 const selectedDateString:string = useMemo(() => { const formattedDate = format(new Date(selectedDateMs),'EEE LLL do Y') return formattedDate }, [selectedDateMs]); const currentDay:string = useMemo(() => { const formattedDay = format(new Date(selectedDateMs),'EEEE') return formattedDay },[selectedDateMs]) const currentWeek:string = useMemo(() => { const formattedDay = format(new Date(selectedDateMs),'LLLL do Y') return formattedDay },[selectedDateMs]) //筛选出当日演出 const gigsToday = gigs.filter((gig) => { const formattedGigDate = format(new Date(gig.dateAndTime.seconds * 1000), 'EEE LLL do Y') return formattedGigDate === selectedDateString; }); const freeGigsToday = gigsToday.filter((gig) => { return gig.isFree === true }) const gigsToDisplay = isSwitchOn ? freeGigsToday : gigsToday //增减日期 const addDays = (amount:number):void => { setSelectedDateMs((curr) => curr + 1000 * 60 * 60 * 24 * amount); }; const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn); return ( <View style={styles.container}> <View testID="gigMapHeader" style={styles.headerText}> <Text style={styles.headerText_main}>{currentDay}</Text> <Text style={styles.headerText_sub}>{currentWeek}</Text> </View> <View style={styles.imageText}> <Text style={styles.subHeader}>点击地图上的</Text> <Image style={styles.image} source={require("../assets/map-pin-new.png")} /> <Text style={styles.subHeader}> 图标查看演出详情 </Text> </View> <View style={styles.mapContainer}> <MapView initialRegion={{ latitude: -41.29416, longitude: 174.77782, latitudeDelta: 0.03, longitudeDelta: 0.03, }} style={styles.map} customMapStyle={mapStyle} > {gigsToDisplay.map((gig, i) => { return ( <Marker key={i} coordinate={{ latitude: gig.location.latitude, longitude: gig.location.longitude, }} icon={require('../assets/map-pin-50pc.png')} onPress={() => { navigation.navigate("GigDetails", { venue: gig.venue, gigName: gig.gigName, image: gig.image, blurb: gig.blurb, isFree: gig.isFree, genre: gig.genre, dateAndTime: { ...gig.dateAndTime }, tickets: gig.tickets, }); }} > <Text style={styles.gigInfo_text}>{gig.genre}</Text> </Marker> ); })} </MapView> </View> <View style={styles.buttonOptions}> <TouchableOpacity onPress={() => addDays(-1)} style={styles.touchable}> <AntDesign name="caretleft" size={36} color="#000000" /> <Text style={{ fontFamily: "NunitoSans", color: "#000000",marginLeft:'8%' }}> 前一天 </Text> </TouchableOpacity> <TouchableOpacity onPress={() => addDays(1)} style={styles.touchable}> <AntDesign name="caretright" size={36} color="#000000" /> <Text style={{ fontFamily: "NunitoSans", color: "#000000",marginRight:'8%' }}> 后一天 </Text> </TouchableOpacity> </View> <View style = {styles.buttonAndSwitch}> <TouchableOpacity onPress={() => navigation.navigate("List")} style={styles.button} > <Text style={styles.buttonText}>列表视图</Text> </TouchableOpacity> <View style = {styles.switch}> <Switch value={isSwitchOn} onValueChange={onToggleSwitch} color = '#377D8A' /> <Text style = {styles.switch_text}>免费演出</Text> </View> </View> </View> ); }; const {width:screenWidth, height:screenHeight} = Dimensions.get('window') const mapWidth = screenWidth * 0.9 //设置宽度为屏幕的90% const mapHeight = mapWidth /0.91 //根据Figma地图0.91的宽高比设置高度 const styles = StyleSheet.create({ container: { flex:1 }, map: { height: '100%', width: '100%' }, mapContainer:{ marginTop: '5%', marginHorizontal: 20, width: mapWidth, height: mapHeight, flex:1, ...Platform.select({ ios: { borderRadius:26, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.3, shadowRadius: 2, }, android: { overflow: 'hidden', borderRadius:26, elevation: 4, } }) }, gigInfo_text: { fontSize:10, color:'black', fontFamily:'NunitoSans', paddingTop: 25, textAlign:'center', fontWeight:'bold' }, gigInfo_text_genre: { color: "white", fontFamily: "Helvetica-Neue", transform: [{ translateY: -5 }], }, headerText: { color: "black", fontSize: 25, marginTop: '0%', marginLeft: '7%', fontFamily: "NunitoSans", marginBottom: 10, }, headerText_main: { fontFamily: "NunitoSans", fontSize:25, lineHeight:34.1 }, headerText_sub: { fontFamily:'LatoRegular', size:14, lineHeight:16.8 }, callout: { width: "auto", height: "auto", backgroundColor: "azure", }, buttonOptions: { flexDirection: "row", justifyContent: "space-between", marginTop:'4%', width:'100%' }, buttonOptionsText: { margin: 5, }, image: { height:20, width:14, marginHorizontal:3 }, imageText: { flexDirection: "row", marginLeft:'7%', marginTop:27 }, touchable: { flexDirection: "column", alignItems: "center", }, subHeader: { fontFamily: "LatoRegular", color: "#747474", size: 12, lineHeight: 17.04 }, button:{ flexDirection:'column', width:115, height:37, marginLeft:'7%', backgroundColor:'#377D8A', borderRadius:8, justifyContent:'center', marginTop:'6%' }, buttonText: { color:'#FFFFFF', textAlign:'center', fontFamily: 'NunitoSans', fontSize:16, lineHeight:22 }, buttonAndSwitch:{ flexDirection:'row', alignItems:'center', justifyContent:'space-between' }, switch:{ marginRight:'6%', transform: [{translateY:7}] }, switch_text: { fontFamily: 'LatoRegular', fontSize:10, transform:[{translateY:-10}] } }); export default GigMap;
可能的原因及解决方案
- iOS子元素优先级覆盖icon属性:react-native-maps在iOS平台上,当Marker包含子元素时,会优先渲染子组件,忽略
icon属性。解决方式是将图标和文本合并到同一个自定义View中,作为Marker的子元素:<Marker key={i} coordinate={{ latitude: gig.location.latitude, longitude: gig.location.longitude, }} onPress={() => { navigation.navigate("GigDetails", { venue: gig.venue, gigName: gig.gigName, image: gig.image, blurb: gig.blurb, isFree: gig.isFree, genre: gig.genre, dateAndTime: { ...gig.dateAndTime }, tickets: gig.tickets, }); }} anchor={{ x: 0.5, y: 1 }} // 确保标记底部中心对准坐标点 > <View style={{ alignItems: 'center' }}> <Image source={require('../assets/map-pin-50pc.png')} style={{ width: 41, height: 62 }} /> <Text style={styles.gigInfo_text}>{gig.genre}</Text> </View> </Marker> - 资源缓存或打包问题:Expo的资源缓存可能导致iOS无法正确加载图片,尝试清除缓存重启项目:
expo r -c - 图片格式兼容性:部分PNG图片的透明通道或编码可能在iOS上存在兼容性问题,尝试将图片转换为JPG格式测试,或重新导出PNG(确保使用标准编码)。
内容的提问来源于stack exchange,提问作者Josh Simon
相关产品推荐
相关产品推荐

