You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 09:51:54