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

Replacing a style image glitches when using SymbolLayer's `iconSize`

Open jimmya opened this issue 2 years ago • 0 comments

Environment

  • Xcode version: 14.3.1
  • iOS version: 16.4
  • Devices affected: All
  • Maps SDK Version: 10.15.1 (also tested with 11 beta)

Observed behavior and steps to reproduce

When loading style images asynchronously and replacing a synchronously returned image there is a visual glitch, if the symbol layer contains an iconSize (either static value or expression). Once the asynchronously loaded image replaces the existing one it sort of blends with the previous one. This can be observed when double tapping the map (after loading), during the zoom in animation the old image is blended through the new image (see attached video). Once zooming in far enough it works properly.

Note: this behaviour is only present on a UIImage with a scale > 1 (so retina and super retina).

So to summarise: I've got a SymbolLayer that has an .iconSize expression:

layer.iconSize = .expression(
    Expression(.interpolate) {
        Expression(.linear)
        Expression(.zoom)
        3
        0.6
        4
        0.8
        6
        1.0
    }
)

I load images asynchronously on the .styleImageMissing event:

mapView.mapboxMap.onEvery(event: .styleImageMissing) { [weak self] event in
    // Provide initial image synchronously
    let view = UIView(frame: .init(origin: .zero, size: .init(width: 50, height: 50)))
    view.backgroundColor = .green
    let image = view.convertToImage()
    try! self?.mapView.mapboxMap.style.addImage(image, id: event.payload.id)

    // Provide loaded image after 2s delay
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
        let view = UIView(frame: .init(origin: .zero, size: .init(width: 50, height: 50)))
        view.backgroundColor = .blue
        view.layer.cornerRadius = 25
        view.clipsToBounds = true
        let image = view.convertToImage()
        try! self?.mapView.mapboxMap.style.addImage(image, id: event.payload.id)
    }
}

When double tapping to zoom in the original image is blended through the new image:

https://github.com/mapbox/mapbox-maps-ios/assets/3206952/668c3aa3-5910-401c-bf59-167577a30fec

When animating the camera manually in code the original image is also blended through the new image:

https://github.com/mapbox/mapbox-maps-ios/assets/3206952/a1233dc9-d158-4f2b-9847-f6fe6384b9ed

Expected behavior

Only the last loaded image should be shown, they should not blend through each other

Notes / preliminary analysis

I think it's all outlined above. The main points are:

  • Loading a style image asynchronously
  • Having the .iconSize property set on the SymbolLayer
  • Having a scale factor of the image thats higher than 1

Additional links and references

Sample project that reproduces this issue: IconBlending.zip

jimmya avatar Aug 10 '23 09:08 jimmya