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
18 changes: 18 additions & 0 deletions Sources/MapConductorForMapKit/MapKitMapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public struct MapKitMapView: View {
@ObservedObject private var state: MapKitViewState
private let handlers: MapViewHandlers<MapKitViewState>
private let cameraRestriction: CameraRestriction?
private let style: MapViewStyle?
private let onStyleDiagnostics: (([String]) -> Void)?
private let content: () -> MapViewContent

public init(
Expand All @@ -21,10 +23,18 @@ public struct MapKitMapView: View {
onCameraMove: OnCameraMoveHandler? = nil,
onCameraMoveEnd: OnCameraMoveHandler? = nil,
sdkInitialize: (() -> Void)? = nil,
/// How the map looks, when the app states it rather than naming a
/// design. `MapConductorVectorStyle` builds one; what happens
/// underneath depends on this backend and the app does not have to
/// know.
style: MapViewStyle? = nil,
onStyleDiagnostics: (([String]) -> Void)? = nil,
@MapViewContentBuilder content: @escaping () -> MapViewContent = { MapViewContent() }
) {
self.state = state
self.cameraRestriction = cameraRestriction
self.style = style
self.onStyleDiagnostics = onStyleDiagnostics
self.handlers = MapViewHandlers(
onMapLoaded: onMapLoaded,
onMapClick: onMapClick,
Expand Down Expand Up @@ -54,6 +64,8 @@ public struct MapKitMapView: View {
state: state,
cameraRestriction: cameraRestriction,
handlers: handlers,
style: style,
onStyleDiagnostics: onStyleDiagnostics,
content: mapContent
)
}
Expand All @@ -64,6 +76,8 @@ private struct MapKitMapViewRepresentable: UIViewRepresentable {
@ObservedObject var state: MapKitViewState
let cameraRestriction: CameraRestriction?
let handlers: MapViewHandlers<MapKitViewState>
let style: MapViewStyle?
let onStyleDiagnostics: (([String]) -> Void)?
let content: MapViewContent

func makeCoordinator() -> Coordinator {
Expand Down Expand Up @@ -109,6 +123,9 @@ private struct MapKitMapViewRepresentable: UIViewRepresentable {
}

func updateUIView(_ uiView: MKMapView, context: Context) {
// Every evaluation of the app's `body` lands here; most calls do
// nothing. See `MapViewStyleHost.apply`.
context.coordinator.applyStyle(style, onDiagnostics: onStyleDiagnostics)
// 制限値が変わったときだけ再適用する。
context.coordinator.applyCameraRestriction(cameraRestriction)
uiView.mapType = state.mapDesignType.getValue()
Expand All @@ -128,6 +145,7 @@ private struct MapKitMapViewRepresentable: UIViewRepresentable {
}

static func dismantleUIView(_ uiView: MKMapView, coordinator: Coordinator) {
coordinator.disposeStyle()
coordinator.unbind()
uiView.delegate = nil
}
Expand Down
17 changes: 15 additions & 2 deletions Sources/MapConductorForMapKit/marker/MapKitMarkerController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,18 @@ final class MapKitMarkerController: AbstractMarkerController<MKPointAnnotation,
super.init(markerManager: markerManager, renderer: renderer)
}

/// 同一一覧の再送を見抜く門番。詳細は型のコメントに。
private var syncIdentity = MarkerListIdentity()

func syncMarkers(_ markers: [Marker]) {
MCLog.marker("MapKitMarkerController.syncMarkers count=\(markers.count)")
// 同じ一覧の再送は入口で帰す。SwiftUI はカメラが動くたびに body を
// 再評価し、そのたびに全マーカーがここへ来る。なぜそれが実害か
// (144k 件で操作の 89% が凍った)は core の MarkerListIdentity に。
guard syncIdentity.shouldProcess(markers) else {
refreshTileLayerIfNeeded()
return
}
let newIds = Set(markers.map { $0.id })
let oldIds = Set(markerStatesById.keys)

Expand Down Expand Up @@ -73,7 +83,6 @@ final class MapKitMarkerController: AbstractMarkerController<MKPointAnnotation,

private func subscribeToMarker(_ state: MarkerState) {
guard markerSubscriptions[state.id] == nil else { return }
MCLog.marker("MapKitMarkerController.subscribe id=\(state.id)")
markerSubscriptions[state.id] = state.asFlow()
.dropFirst() // Skip initial value to avoid triggering update on subscription
.receive(on: DispatchQueue.main)
Expand Down Expand Up @@ -128,7 +137,11 @@ final class MapKitMarkerController: AbstractMarkerController<MKPointAnnotation,
tileSize: Self.retinaAwareTileSize,
cacheSizeBytes: tilingOptions.cacheSize,
debugTileOverlay: tilingOptions.debugTileOverlay,
iconScaleCallback: scaledCallback
iconScaleCallback: scaledCallback,
// MapLibre と同じく tilingOptions から。渡し忘れると 14px の間引きが
// 黙って無効になり、密なデータで描画も突き合わせも重くなる(実際に
// ここが 0 のままだった)。
declutterPx: tilingOptions.declutterPx
)
TileServerRegistry.get().register(routeId: routeId, provider: renderer)
tileRenderer = renderer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ final class MapKitRasterLayerOverlayRenderer: AbstractRasterLayerOverlayRenderer

override func createLayer(state: RasterLayerState) async -> MKTileOverlay? {
guard let mapView else { return nil }
guard let overlay = await makeTileOverlay(from: state) else { return nil }
guard let overlay = await makeTileOverlay(from: state, displayScale: mapView.traitCollection.displayScale) else { return nil }
overlayStates[overlay] = state

let renderer = MKTileOverlayRenderer(tileOverlay: overlay)
Expand Down Expand Up @@ -97,14 +97,18 @@ final class MapKitRasterLayerOverlayRenderer: AbstractRasterLayerOverlayRenderer
mapView = nil
}

private func makeTileOverlay(from state: RasterLayerState) async -> MKTileOverlay? {
private func makeTileOverlay(from state: RasterLayerState, displayScale: CGFloat) async -> MKTileOverlay? {
let source = await resolveSource(state: state)

switch source {
case let .urlTemplate(template, tileSize, minZoom, maxZoom, _, scheme):
// RasterLayerSource expresses display size in points; MKTileOverlay
// expects pixels. Without the screen scale, Retina maps select a
// finer tile level and shrink the style's labels and road widths.
let pixelSize = max(1, Int((CGFloat(tileSize) * max(1, displayScale)).rounded()))
let overlay = CustomURLTileOverlay(
urlTemplate: template,
tileSize: tileSize,
tileSize: pixelSize,
minZoom: minZoom,
maxZoom: maxZoom,
scheme: scheme,
Expand Down
92 changes: 46 additions & 46 deletions api/MapConductorForMapKit.api.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,70 +12,70 @@ import _Concurrency
import _MapKit_SwiftUI
import _StringProcessing
import _SwiftConcurrencyShims
extension MapConductorCore.MapCameraPosition {
final public func toMKMapCamera(on mapView: MapKit.MKMapView? = nil) -> MapKit.MKMapCamera
extension MapConductorCore::MapCameraPosition {
final public func toMKMapCamera(on mapView: MapKit::MKMapView? = nil) -> MapKit::MKMapCamera
}
extension MapKit.MKMapView {
@_Concurrency.MainActor @preconcurrency public func toMapCameraPosition(logicalTiltHint: Swift.Double? = nil, visibleRegion: MapConductorCore.VisibleRegion? = nil) -> MapConductorCore.MapCameraPosition
extension MapKit::MKMapView {
@_Concurrency::MainActor @preconcurrency public func toMapCameraPosition(logicalTiltHint: Swift::Double? = nil, visibleRegion: MapConductorCore::VisibleRegion? = nil) -> MapConductorCore::MapCameraPosition
}
public protocol MapKitMapDesignTypeProtocol : MapConductorCore.MapDesignTypeProtocol where Self.Identifier == MapKit.MKMapType {
public protocol MapKitMapDesignTypeProtocol : MapConductorCore::MapDesignTypeProtocol where Self.Identifier == MapKit::MKMapType {
}
public typealias MapKitMapDesignType = any MapConductorForMapKit.MapKitMapDesignTypeProtocol
public struct MapKitMapDesign : MapConductorForMapKit.MapKitMapDesignTypeProtocol, Swift.Hashable {
public let id: MapKit.MKMapType
public let attributionRules: [MapConductorCore.AttributionRule]
public init(id: MapKit.MKMapType, attributionRules: [MapConductorCore.AttributionRule] = [])
public func getValue() -> MapKit.MKMapType
public static let Standard: MapConductorForMapKit.MapKitMapDesign
public static let Satellite: MapConductorForMapKit.MapKitMapDesign
public static let Hybrid: MapConductorForMapKit.MapKitMapDesign
public static let SatelliteFlyover: MapConductorForMapKit.MapKitMapDesign
public static let HybridFlyover: MapConductorForMapKit.MapKitMapDesign
public static let MutedStandard: MapConductorForMapKit.MapKitMapDesign
public static func Create(id: MapKit.MKMapType) -> MapConductorForMapKit.MapKitMapDesign
public static func toMapDesignType(id: MapKit.MKMapType) -> MapConductorForMapKit.MapKitMapDesignType
public static func == (a: MapConductorForMapKit.MapKitMapDesign, b: MapConductorForMapKit.MapKitMapDesign) -> Swift.Bool
public typealias Identifier = MapKit.MKMapType
public func hash(into hasher: inout Swift.Hasher)
public var hashValue: Swift.Int {
public typealias MapKitMapDesignType = any MapConductorForMapKit::MapKitMapDesignTypeProtocol
public struct MapKitMapDesign : MapConductorForMapKit::MapKitMapDesignTypeProtocol, Swift::Hashable {
public let id: MapKit::MKMapType
public let attributionRules: [MapConductorCore::AttributionRule]
public init(id: MapKit::MKMapType, attributionRules: [MapConductorCore::AttributionRule] = [])
public func getValue() -> MapKit::MKMapType
public static let Standard: MapConductorForMapKit::MapKitMapDesign
public static let Satellite: MapConductorForMapKit::MapKitMapDesign
public static let Hybrid: MapConductorForMapKit::MapKitMapDesign
public static let SatelliteFlyover: MapConductorForMapKit::MapKitMapDesign
public static let HybridFlyover: MapConductorForMapKit::MapKitMapDesign
public static let MutedStandard: MapConductorForMapKit::MapKitMapDesign
public static func Create(id: MapKit::MKMapType) -> MapConductorForMapKit::MapKitMapDesign
public static func toMapDesignType(id: MapKit::MKMapType) -> MapConductorForMapKit::MapKitMapDesignType
public static func == (a: MapConductorForMapKit::MapKitMapDesign, b: MapConductorForMapKit::MapKitMapDesign) -> Swift::Bool
public typealias Identifier = MapKit::MKMapType
public func hash(into hasher: inout Swift::Hasher)
public var hashValue: Swift::Int {
get
}
}
@_Concurrency.MainActor @preconcurrency public struct MapKitMapView : SwiftUICore.View {
@_Concurrency.MainActor @preconcurrency public init(state: MapConductorForMapKit.MapKitViewState, cameraRestriction: MapConductorCore.CameraRestriction? = nil, onMapLoaded: MapConductorCore.OnMapLoadedHandler<MapConductorForMapKit.MapKitViewState>? = nil, onMapClick: MapConductorCore.OnMapEventHandler? = nil, onMapLongClick: MapConductorCore.OnMapEventHandler? = nil, onCameraMoveStart: MapConductorCore.OnCameraMoveHandler? = nil, onCameraMove: MapConductorCore.OnCameraMoveHandler? = nil, onCameraMoveEnd: MapConductorCore.OnCameraMoveHandler? = nil, sdkInitialize: (() -> Swift.Void)? = nil, @MapConductorCore.MapViewContentBuilder content: @escaping () -> MapConductorCore.MapViewContent = { MapViewContent() })
@_Concurrency.MainActor @preconcurrency public var body: some SwiftUICore.View {
@_Concurrency::MainActor @preconcurrency public struct MapKitMapView : SwiftUICore::View {
@_Concurrency::MainActor @preconcurrency public init(state: MapConductorForMapKit::MapKitViewState, cameraRestriction: MapConductorCore::CameraRestriction? = nil, onMapLoaded: MapConductorCore::OnMapLoadedHandler<MapConductorForMapKit::MapKitViewState>? = nil, onMapClick: MapConductorCore::OnMapEventHandler? = nil, onMapLongClick: MapConductorCore::OnMapEventHandler? = nil, onCameraMoveStart: MapConductorCore::OnCameraMoveHandler? = nil, onCameraMove: MapConductorCore::OnCameraMoveHandler? = nil, onCameraMoveEnd: MapConductorCore::OnCameraMoveHandler? = nil, sdkInitialize: (() -> Swift::Void)? = nil, style: (any MapConductorCore::MapViewStyle)? = nil, onStyleDiagnostics: (([Swift::String]) -> Swift::Void)? = nil, @MapConductorCore::MapViewContentBuilder content: @escaping () -> MapConductorCore::MapViewContent = { MapViewContent() })
@_Concurrency::MainActor @preconcurrency public var body: some SwiftUICore::View {
get
}
public typealias Body = @_opaqueReturnTypeOf("$s015MapConductorForA3Kit0adA4ViewV4bodyQrvp", 0) __
}
public typealias MapKitActualMarker = MapKit.MKPointAnnotation
public typealias MapKitActualPolyline = MapKit.MKPolyline
public typealias MapKitActualCircle = MapKit.MKCircle
public typealias MapKitActualPolygon = MapKit.MKPolygon
final public class MapKitViewState : MapConductorCore.MapViewState<MapConductorForMapKit.MapKitMapDesignType> {
final public var mapViewHolder: MapConductorForMapKit.MapKitViewHolder? {
public typealias MapKitActualMarker = MapKit::MKPointAnnotation
public typealias MapKitActualPolyline = MapKit::MKPolyline
public typealias MapKitActualCircle = MapKit::MKCircle
public typealias MapKitActualPolygon = MapKit::MKPolygon
final public class MapKitViewState : MapConductorCore::MapViewState<MapConductorForMapKit::MapKitMapDesignType> {
final public var mapViewHolder: MapConductorForMapKit::MapKitViewHolder? {
get
}
override final public var mapDesignType: MapConductorForMapKit.MapKitMapDesignType {
override final public var mapDesignType: MapConductorForMapKit::MapKitMapDesignType {
get
set
}
public init(id: Swift.String, mapDesignType: MapConductorForMapKit.MapKitMapDesignType = MapKitMapDesign.Standard, cameraPosition: MapConductorCore.MapCameraPosition = .Default, uiSettings: MapConductorCore.MapUISettings = MapUISettings())
convenience public init(mapDesignType: MapConductorForMapKit.MapKitMapDesignType = MapKitMapDesign.Standard, cameraPosition: MapConductorCore.MapCameraPosition = .Default, uiSettings: MapConductorCore.MapUISettings = MapUISettings())
override final public func getMapViewHolder() -> MapConductorCore.AnyMapViewHolder?
public init(id: Swift::String, mapDesignType: MapConductorForMapKit::MapKitMapDesignType = MapKitMapDesign.Standard, cameraPosition: MapConductorCore::MapCameraPosition = .Default, uiSettings: MapConductorCore::MapUISettings = MapUISettings())
convenience public init(mapDesignType: MapConductorForMapKit::MapKitMapDesignType = MapKitMapDesign.Standard, cameraPosition: MapConductorCore::MapCameraPosition = .Default, uiSettings: MapConductorCore::MapUISettings = MapUISettings())
override final public func getMapViewHolder() -> MapConductorCore::AnyMapViewHolder?
@objc deinit
}
public class MapKitZoomAltitudeConverter : MapConductorCore.WebMercatorZoomAltitudeConverter {
public init(zoom0Altitude: Swift.Double = AbstractZoomAltitudeConverter.defaultZoom0Altitude)
public class MapKitZoomAltitudeConverter : MapConductorCore::WebMercatorZoomAltitudeConverter {
public init(zoom0Altitude: Swift::Double = AbstractZoomAltitudeConverter.defaultZoom0Altitude)
@objc deinit
}
@_hasMissingDesignatedInitializers final public class MapKitViewHolder : MapConductorCore.MapViewHolderProtocol {
final public let mapView: MapKit.MKMapView
final public let map: MapKit.MKMapView
final public func toScreenOffset(position: any MapConductorCore.GeoPointProtocol) -> CoreFoundation.CGPoint?
final public func fromScreenOffsetSync(offset: CoreFoundation.CGPoint) -> MapConductorCore.GeoPoint?
public typealias ActualMap = MapKit.MKMapView
public typealias ActualMapView = MapKit.MKMapView
@_hasMissingDesignatedInitializers final public class MapKitViewHolder : MapConductorCore::MapViewHolderProtocol {
final public let mapView: MapKit::MKMapView
final public let map: MapKit::MKMapView
final public func toScreenOffset(position: any MapConductorCore::GeoPointProtocol) -> CoreFoundation::CGPoint?
final public func fromScreenOffsetSync(offset: CoreFoundation::CGPoint) -> MapConductorCore::GeoPoint?
public typealias ActualMap = MapKit::MKMapView
public typealias ActualMapView = MapKit::MKMapView
@objc deinit
}
extension MapConductorForMapKit.MapKitMapView : Swift.Sendable {}
extension MapConductorForMapKit::MapKitMapView : Swift::Sendable {}
Loading