Skip to content

fix(ios): Image refresh#4249

Open
patrik-majercik wants to merge 1 commit into
rnmapbox:mainfrom
patrik-majercik:fix/ios-image-refresh
Open

fix(ios): Image refresh#4249
patrik-majercik wants to merge 1 commit into
rnmapbox:mainfrom
patrik-majercik:fix/ios-image-refresh

Conversation

@patrik-majercik

Copy link
Copy Markdown

Description

Implements iOS Image.refresh, which was left as a TODO in RNMBXImageModule while Android and PointAnnotation already support it.

On iOS, <Image> registers map icons by snapshotting its React child view via layer.render(). When the child contains asynchronously loaded content (e.g. a remote Image), the first snapshot may be incomplete. Calling refresh() re-snapshots the child and updates the Mapbox style image - matching the existing PointAnnotation.refresh() behavior.

Related: #3674 (async images under <Image> not updating on iOS)

Checklist

  • I've read CONTRIBUTING.md
  • I updated the doc/other generated code with running yarn generate in the root folder
  • I have tested the new feature on /example app.
    • In V11 mode/ios
    • In New Architecture mode/ios
    • In V11 mode/android
    • In New Architecture mode/android
  • I added/updated a sample - if a new feature was implemented (/example)

Note: This is a native bug fix with no JS API / codegen changes — yarn generate and /example sample updates are likely not required. Android behavior is unchanged.

Component to reproduce the issue you're fixing

import { useCallback, useRef } from 'react';
import { StyleSheet, View } from 'react-native';
import { Image as RNImage } from 'react-native';
import Mapbox, { Image, Images, MapView, ShapeSource, SymbolLayer } from '@rnmapbox/maps';

const LOGO_URL = 'https://reactnative.dev/img/tiny_logo.png';

const Marker = ({ logo, onLoad }) => (
  <View style={styles.marker} collapsable={false}>
    <View style={styles.pin} />
    <RNImage source={{ uri: logo }} style={styles.logo} onLoad={onLoad} />
  </View>
);

const MarkerLogo = ({ url }) => {
  const imageRef = useRef(null);

  const handleLoad = useCallback(() => {
    imageRef.current?.refresh();
  }, []);

  return (
    <Image ref={imageRef} name={url}>
      <Marker logo={url} onLoad={handleLoad} />
    </Image>
  );
};

export default function ImageRefreshRepro() {
  const shape = {
    type: 'FeatureCollection',
    features: [
      {
        type: 'Feature',
        properties: { logo: LOGO_URL },
        geometry: { type: 'Point', coordinates: [-74.006, 40.7128] },
      },
    ],
  };

  return (
    <MapView style={styles.map}>
      <Images>
        <Image name="point">
          <View style={styles.marker}>
            <View style={styles.pin} />
          </View>
        </Image>
        <MarkerLogo url={LOGO_URL} />
      </Images>
      <ShapeSource id="shape" shape={shape}>
        <SymbolLayer
          id="singlePoint"
          style={{
            iconImage: ['coalesce', ['get', 'logo'], 'point'],
            iconAllowOverlap: true,
          }}
        />
      </ShapeSource>
    </MapView>
  );
}

const styles = StyleSheet.create({
  map: { flex: 1 },
  marker: { width: 72, height: 91 },
  pin: { width: 72, height: 91, backgroundColor: '#F41919', borderRadius: 36 },
  logo: {
    position: 'absolute',
    left: 23,
    top: 23,
    width: 25,
    height: 25,
    borderRadius: 12.5,
  },
});

@patrik-majercik patrik-majercik requested a deployment to CI with Mapbox Tokens July 8, 2026 13:33 — with GitHub Actions Waiting
@patrik-majercik patrik-majercik requested a deployment to CI with Mapbox Tokens July 8, 2026 13:33 — with GitHub Actions Waiting
@patrik-majercik patrik-majercik requested a deployment to CI with Mapbox Tokens July 8, 2026 13:33 — with GitHub Actions Waiting
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant