MapKit top MKTileOverlay blendMode overrides lower MKTileOverlay blendMode

I need multiple MKTileOverlays with multiple blendModes. Apparently using an overlay with a different blend causes the layer under to use the same blend mode.

For the example below, using a normal blend mode on top of a soft light blend mode causes a normal blend mode to be used instead of soft light. The soft light layer is rendered as expected until the normal layer is displayed starting at zoom level 15.

First, I've subclassed MKTileOverlay to add an overlay type so that the correct renderer is provided per overlay. (I know there is a title, but I prefer this)

enum OverlayType {
    case softLight,
         normal
}

class TileOverlay: MKTileOverlay {
    var type: OverlayType = .normal
}

Then setup layers and renderers in the typical fashion:

var softLightRenderer: MKTileOverlayRenderer!
var normalRenderer: MKTileOverlayRenderer!

private func setupSoftlightRenderer() {
    let overlay = TileOverlay(urlTemplate: "http://localhost/softlight/{z}/{x}/{y}")
    overlay.type = .softLight
    overlay.canReplaceMapContent = false
    overlay.minimumZ = 9
    overlay.maximumZ = 20
    mapView.addOverlay(overlay, level: .aboveLabels)
    softLightRenderer = MKTileOverlayRenderer(tileOverlay: overlay)
    tileRenderer.blendMode = .softLight
}

private func setupNormalRenderer() {
    let overlay = TileOverlay(urlTemplate: "http://localhost/normal/{z}/{x}/{y}")
    overlay.type = .normal
    overlay.canReplaceMapContent = false
    overlay.minimumZ = 15
    overlay.maximumZ = 20
    mapView.addOverlay(overlay, level: .aboveLabels)
    normalRenderer = MKTileOverlayRenderer(tileOverlay: overlay)
    normalRenderer.blendMode = .normal
}

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
  
    if let overlay = overlay as? TileOverlay {
        switch overlay.type {
        case .softLight:
            return softLightRenderer
        case .normal:
            return normalRenderer
        }      
    }
  
    print("Warning: using unhandled overlay renderer")
    return blankRenderer
}

override func viewDidLoad() {
    ...
    setupSoftlightRenderer()
    setupNormalRenderer()
}

Interestingly, if I put the overlays at two different levels, one .aboveLabels and another .aboveRoads it works as expected. The problem is that limits me to two different overlay blend modes. I really could use more than two. I tried every possible variation of inserting the layers at different indexes and methods, but the only two that seem to work are the .aboveLabels and .aboveRoads.

How can I use more than two different blend modes?

Replies

When using any vector overlays (MKPolygonRenderer, MKPolylineRenderer, etc), my hack of using .aboveLabels and .aboveRoads doesn't work. When a raster layer uses .aboveRoads and the vector uses .aboveLabels, the vectors are not drawn properly. Since I need at least one vector layer with normal blending, it means I effectively have zero blend modes other than normal for raster overlays.

I created a minimal reproducible project. Clicking the button will add a MKPolygonRenderer with normal blending that breaks the raster overlay that uses .softLight blending.

Note that I have tried overriding the draw functions for the overlays and setting the blend modes there - even tried with transparency layers, but I have not found a work-around.