Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions Sources/MapConductorForMapKit/MapKitOverlayStacking.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Foundation
import MapKit
@_spi(MapConductorDriver) import MapConductorCore

/// オーバーレイを、重なり順の帯(``OverlayBand``)の位置へ差し込む。
///
/// 下から ラスター < グラウンドイメージ < ポリゴン < サークル < ポリライン < マーカーのタイル。
/// MapKit は同じ level の中では足した順に重ねるので、以前は後から足したもの
/// (clearOverlays の後に送り直したグラウンドイメージなど)が上に来ていた。
/// ここでは level の中の並びで帯の位置を計算して `insertOverlay(_:at:level:)` する。
///
/// ## level とラベル
///
/// `addOverlay(_:)` の既定の level は `.aboveRoads`(ラベルの下)で、ポリゴン・サークル・
/// ポリラインは以前からラベルの下にあった。一方ラスター・グラウンドイメージ・マーカーのタイルは
/// `.aboveLabels` で足していたので、**ラスターがポリゴン等より上**に描かれていた。
/// 帯の順を守るには、ラスターとグラウンドイメージをポリゴン等と同じ level に置く必要がある。
/// ここではポリゴン等のラベルとの関係を変えない方を選び、マーカーの帯以外はすべて
/// `.aboveRoads`、マーカーのタイルだけ `.aboveLabels` に置く。
/// その結果ラスター(GL 系と同じくラベルの下になる)とグラウンドイメージ(以前はラベルの上)が
/// ラベルの下へ移る。
///
/// - マーカー(`MKAnnotation`)は MapKit が常にオーバーレイより上に描くので、帯の条件を満たす。
/// - 帯の中の zIndex は足した時点の値で並べる(MapKit の描画担当は zIndex の変更を受けて
/// 並べ直さない。従来から未対応)。
@MainActor
enum MapKitOverlayStacking {
static func level(for key: OverlayStackKey) -> MKOverlayLevel {
key.band == .marker ? .aboveLabels : .aboveRoads
}

static func add(_ overlay: MKOverlay, _ key: OverlayStackKey, to mapView: MKMapView) {
let level = level(for: key)
let registry = keys(of: mapView)
registry.setObject(KeyBox(key), forKey: overlay)
let existing = mapView.overlays(in: level)
let keys = existing.map { (registry.object(forKey: $0) as KeyBox?)?.key }
let index = OverlayStacking.insertionIndex(for: key, among: keys)
if index >= existing.count {
mapView.addOverlay(overlay, level: level)
} else {
mapView.insertOverlay(overlay, at: index, level: level)
}
}

private final class KeyBox: NSObject {
let key: OverlayStackKey
init(_ key: OverlayStackKey) { self.key = key }
}

private static var registryKey: UInt8 = 0

/// 地図ごとの「オーバーレイ → 帯のキー」。オーバーレイは弱参照で持つので、外したものは自然に消える。
private static func keys(of mapView: MKMapView) -> NSMapTable<AnyObject, KeyBox> {
if let table = objc_getAssociatedObject(mapView, &registryKey) as? NSMapTable<AnyObject, KeyBox> {
return table
}
let table = NSMapTable<AnyObject, KeyBox>.weakToStrongObjects()
objc_setAssociatedObject(mapView, &registryKey, table, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return table
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import CoreLocation
import MapKit
import MapConductorCore
@_spi(MapConductorDriver) import MapConductorCore
import UIKit

@MainActor
Expand Down Expand Up @@ -36,7 +36,7 @@ final class MapKitCircleOverlayRenderer: AbstractCircleOverlayRenderer<MKPolygon
renderer.fillColor = state.fillColor

renderersByPolygon[ObjectIdentifier(polygon)] = renderer
mapView.addOverlay(polygon)
MapKitOverlayStacking.add(polygon, OverlayStackKey(.circle, zIndex: state.zIndex ?? 0), to: mapView)
polygons.append(polygon)
}
fragmentsByCircleId[state.id] = polygons
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import CoreLocation
import MapKit
import MapConductorCore
@_spi(MapConductorDriver) import MapConductorCore
import UIKit

// Custom MKOverlay for ground images
Expand Down Expand Up @@ -98,7 +98,7 @@ final class MapKitGroundImageOverlayRenderer: AbstractGroundImageOverlayRenderer
renderersByStateId[state.id] = renderer
overlaysByStateId[state.id] = overlay

mapView.addOverlay(overlay, level: .aboveLabels)
MapKitOverlayStacking.add(overlay, OverlayStackKey(.groundImage), to: mapView)

return overlay
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ final class MapKitMarkerController: AbstractMarkerController<MKPointAnnotation,
// MKTileOverlay.tileSize=256 ensures MapKit requests tiles at the correct zoom level.
// The server returns retinaAwareTileSize images for Retina sharpness.
let overlay = MarkerTileOverlay(urlTemplate: urlTemplate, tileSize: 256)
mapView.addOverlay(overlay, level: .aboveLabels)
// マーカーのタイルはマーカーと同じ帯(オーバーレイの中では一番上)。
MapKitOverlayStacking.add(overlay, OverlayStackKey(.marker), to: mapView)
tileOverlay = overlay
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MapKit
import MapConductorCore
@_spi(MapConductorDriver) import MapConductorCore
import UIKit

/// MapKit は穴(inner rings)をネイティブに描画できるため、ラスタタイルマスクは使わない
Expand Down Expand Up @@ -48,7 +48,7 @@ final class MapKitPolygonOverlayRenderer: AbstractPolygonOverlayRenderer<MKPolyg
renderer.fillColor = resolved.fillColor
renderer.update(rings: rings)
renderersByPolygonId[resolved.id] = renderer
mapView.addOverlay(polygon)
MapKitOverlayStacking.add(polygon, OverlayStackKey(.polygon, zIndex: resolved.zIndex), to: mapView)
return polygon
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MapKit
import MapConductorCore
@_spi(MapConductorDriver) import MapConductorCore
import UIKit

@MainActor
Expand Down Expand Up @@ -27,7 +27,7 @@ final class MapKitPolylineOverlayRenderer: AbstractPolylineOverlayRenderer<MKPol
renderer.lineWidth = CGFloat(state.strokeWidth)

renderersByPolylineId[state.id] = renderer
mapView.addOverlay(polyline)
MapKitOverlayStacking.add(polyline, OverlayStackKey(.polyline, zIndex: state.zIndex), to: mapView)

return polyline
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MapKit
import MapConductorCore
@_spi(MapConductorDriver) import MapConductorCore

@MainActor
final class MapKitRasterLayerOverlayRenderer: AbstractRasterLayerOverlayRenderer<MKTileOverlay> {
Expand All @@ -24,7 +24,7 @@ final class MapKitRasterLayerOverlayRenderer: AbstractRasterLayerOverlayRenderer
// Important: MapKit may request a renderer immediately as part of addOverlay().
// Ensure we have the renderer registered before adding the overlay to the map.
if state.visible {
mapView.addOverlay(overlay, level: .aboveLabels)
MapKitOverlayStacking.add(overlay, OverlayStacking.rasterKey(stateId: state.id, zIndex: state.zIndex), to: mapView)
}

return overlay
Expand Down Expand Up @@ -56,7 +56,7 @@ final class MapKitRasterLayerOverlayRenderer: AbstractRasterLayerOverlayRenderer
if finger.visible != prevFinger.visible {
if current.state.visible {
if layer.isKind(of: MKTileOverlay.self) && !mapView.overlays.contains(where: { $0 === layer }) {
mapView.addOverlay(layer, level: .aboveLabels)
MapKitOverlayStacking.add(layer, OverlayStacking.rasterKey(stateId: current.state.id, zIndex: current.state.zIndex), to: mapView)
}
} else {
mapView.removeOverlay(layer)
Expand Down
Loading