mapbox-maps-flutter icon indicating copy to clipboard operation
mapbox-maps-flutter copied to clipboard

Point Annotation image update not working on ios

Open thetranquila opened this issue 9 months ago • 9 comments

Even though it is working on Android. Updating point geometry in ios also works properly

thetranquila avatar May 04 '24 20:05 thetranquila

Hi @thetranquila, thank you for raising this bug, I have created an internal ticket to track the work https://mapbox.atlassian.net/browse/MAPSFLT-212

maios avatar May 07 '24 09:05 maios

Could be related that on real IOS 12 device the annotation in some case disappeared or not updating properly.

bakaemon avatar May 13 '24 08:05 bakaemon

Tested on Iphone 13 Ios 17 device. Also it never updates

thetranquila avatar May 13 '24 08:05 thetranquila

Hi all, while our team is working on this bug, what you can do to get the image update on iOS is to give a random iconImage each time you want to update image

pointAnnotation.iconImage = "some-other-id";

MapboxMaps iOS has a mechanism to manage images used by PointAnnotationsManager, when the annotations get updated, if there is already an image added to the renderer's style with same id, we will not do anything, so to work around it, you need to give different iconImage (this is gonna be the name/id of the image to add to style)

maios avatar May 13 '24 09:05 maios

Hi all, while our team is working on this bug, what you can do to get the image update on iOS is to give a random iconImage each time you want to update image

pointAnnotation.iconImage = "some-other-id";

MapboxMaps iOS has a mechanism to manage images used by PointAnnotationsManager, when the annotations get updated, if there is already an image added to the renderer's style with same id, we will not do anything, so to work around it, you need to give different iconImage (this is gonna be the name/id of the image to add to style)

I don't know what I did wrong, but I am getting issue in the following code, using your workaround:

  Future<void> _updateBusAnnotation(
    String busImage,
    Position position, [
    double iconSize = 1,
  ]) async {
    _tmpIcon ??= (await rootBundle.load(busImage)).buffer.asUint8List();
    if (busAnnotation == null) {
      busAnnotation = await pointAnnotationManager?.addAnnotation(
        point: position.toPoint(),
        dataImage: _tmpIcon,
        iconSize: iconSize,
      );
    } else {
      _tmpIcon = (await rootBundle.load(busImage)).buffer.asUint8List();
      if (Platform.isIOS) {
        final String randomString = () {
          const _chars =
              'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
          final _rnd = Random();
          const length = 10;

          return String.fromCharCodes(Iterable.generate(
            length,
            (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length)),
          ));
        }();
        busAnnotation = busAnnotation!.copyWith(
          iconImage: randomString,
        );
      }
      busAnnotation = busAnnotation!.copyWith(
        geometry: position.toPoint().toJson(),
        image: _tmpIcon,
      );
      
      await pointAnnotationManager?.update(busAnnotation!);
    }
  }

However, the icon is guaranteed to disappear if I update the annotation with iconImage assigned instead of randomly disappear. Did I do something wrong?

bakaemon avatar May 14 '24 10:05 bakaemon

Is this function copyWith something you added from your codebase? If so can we have some snippet code of what it does?

maios avatar May 14 '24 11:05 maios

@maios

Sorry for the late reply, it was late when I posted the last comment. It was just an extension method I added. Here you go:

 PointAnnotation copyWith({
    Map<String?, Object?>? geometry,
    Uint8List? image,
    IconAnchor? iconAnchor,
    String? iconImage,
    List<double?>? iconOffset,
    double? iconRotate,
    double? iconSize,
    double? symbolSortKey,
    TextAnchor? textAnchor,
    String? textField,
    TextJustify? textJustify,
    double? textLetterSpacing,
    double? textMaxWidth,
    List<double?>? textOffset,
    double? textRadialOffset,
    double? textRotate,
    double? textSize,
    TextTransform? textTransform,
    int? iconColor,
    double? iconHaloBlur,
    int? iconHaloColor,
    double? iconHaloWidth,
    double? iconOpacity,
    int? textColor,
    double? textHaloBlur,
    int? textHaloColor,
    double? textHaloWidth,
    double? textOpacity,
  }) =>
      PointAnnotation(
        id: id,
        geometry: geometry ?? this.geometry,
        image: image ?? this.image,
        iconAnchor: iconAnchor ?? this.iconAnchor,
        iconImage: iconImage ?? this.iconImage,
        iconOffset: iconOffset ?? this.iconOffset,
        iconRotate: iconRotate ?? this.iconRotate,
        iconSize: iconSize ?? this.iconSize,
        symbolSortKey: symbolSortKey ?? this.symbolSortKey,
        textAnchor: textAnchor ?? this.textAnchor,
        textField: textField ?? this.textField,
        textJustify: textJustify ?? this.textJustify,
        textLetterSpacing: textLetterSpacing ?? this.textLetterSpacing,
        textMaxWidth: textMaxWidth ?? this.textMaxWidth,
        textOffset: textOffset ?? this.textOffset,
        textRadialOffset: textRadialOffset ?? this.textRadialOffset,
        textRotate: textRotate ?? this.textRotate,
        textSize: textSize ?? this.textSize,
        textTransform: textTransform ?? this.textTransform,
        iconColor: iconColor ?? this.iconColor,
        iconHaloBlur: iconHaloBlur ?? this.iconHaloBlur,
        iconHaloColor: iconHaloColor ?? this.iconHaloColor,
        iconHaloWidth: iconHaloWidth ?? this.iconHaloWidth,
        iconOpacity: iconOpacity ?? this.iconOpacity,
        textColor: textColor ?? this.textColor,
        textHaloBlur: textHaloBlur ?? this.textHaloBlur,
        textHaloColor: textHaloColor ?? this.textHaloColor,
        textHaloWidth: textHaloWidth ?? this.textHaloWidth,
        textOpacity: textOpacity ?? this.textOpacity,
      );

It worked well on android before. Alternatively, I also used this code, but the icon still disappears on update:

 busAnnotation?.iconImage = randomString;
 //...
 busAnnotation
        ?..geometry = position.toPoint().toJson()
        ..image = _tmpIcon;

bakaemon avatar May 15 '24 01:05 bakaemon

I get this bug here, not on iconImage:

var response = http.get(Uri.parse(currentDynamic?.icon?.src ?? ""));
dynamicAnnotation?.image = response.bodyBytes.buffer.asUint8List();
pointAnnotationManager?.update(dynamicAnnotation);

thetranquila avatar May 15 '24 23:05 thetranquila

I managed to find a fix for this, by adding onStyleMissingListener to MapWidget:

MapWidget(
                resourceOptions: ResourceOptions(
                  accessToken: mapboxToken,
                  tileStoreUsageMode: TileStoreUsageMode.READ_AND_UPDATE,
                ),
                onMapCreated: _onMapCreated,
                onScrollListener: _onScrollListener,
                onStyleImageMissingListener: _onStyleImageMissing,
//...
)

here is the method listener, for future reference:

/// This method is called when an image is missing in the style
///  to solve the issue of missing images on iOS.
  Future<void> _onStyleImageMissing(
    StyleImageMissingEventData eventData,
  ) async {
    if (!Platform.isIOS) {
      return;
    }
    final String missingImageName = eventData.id;
    Log.e('Image missing: $missingImageName');

    // Load the image from assets
    final ByteData bytes = await rootBundle.load(busImage);
    final Uint8List imageBytes = bytes.buffer.asUint8List();

    // Add the image to the map style
    await mapboxMap?.style.addStyleImage(
      missingImageName,
      3,
      MbxImage(width: 40, height: 40, data: imageBytes),
      false,
      [],
      [],
      null,
    );
  }

Still, I hope this problem is solved soon.

bakaemon avatar Jul 30 '24 01:07 bakaemon