From 37eece479e4a4c38aa8d09564cf86de6fc7ba3e8 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Sun, 2 Feb 2025 11:20:01 +0900 Subject: [PATCH 1/4] Enable layer insertion below symbols --- .../Style Layers/Style Layer.swift | 16 +++++++--------- Sources/MapLibreSwiftUI/Examples/Polyline.swift | 2 ++ Sources/MapLibreSwiftUI/MapViewCoordinator.swift | 10 ++++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift b/Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift index 435d562..01b8039 100644 --- a/Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift +++ b/Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift @@ -15,6 +15,10 @@ public enum LayerInsertionPosition: Equatable { case aboveOthers /// The layer should be inserted below other existing layers. case belowOthers + /// The layer should be inserted below the first symbol layer (used for labels and iconography). + /// + /// If there are no symbol layers in the style, the layer is inserted above other existing layers. + case belowSymbols } /// Internal style enum that wraps a source reference. @@ -132,7 +136,7 @@ public extension StyleLayer { } } -public extension StyleLayer { +public extension StyleLayerDefinition { // MARK: - Common modifiers func visible(_ value: Bool) -> Self { @@ -162,14 +166,8 @@ public extension StyleLayer { func renderBelowOthers() -> Self { modified(self) { $0.insertionPosition = .belowOthers } } -} -public extension StyleLayerDefinition { - func minimumZoomLevel(_ value: Float) -> Self { - modified(self) { $0.minimumZoomLevel = value } - } - - func maximumZoomLevel(_ value: Float) -> Self { - modified(self) { $0.maximumZoomLevel = value } + func renderBelowSymbols() -> Self { + modified(self) { $0.insertionPosition = .belowSymbols } } } diff --git a/Sources/MapLibreSwiftUI/Examples/Polyline.swift b/Sources/MapLibreSwiftUI/Examples/Polyline.swift index 3b5b6fa..f4b40c2 100644 --- a/Sources/MapLibreSwiftUI/Examples/Polyline.swift +++ b/Sources/MapLibreSwiftUI/Examples/Polyline.swift @@ -26,6 +26,7 @@ struct PolylineMapView: View { curveType: .exponential, parameters: NSExpression(forConstantValue: 1.5), stops: NSExpression(forConstantValue: [14: 6, 18: 24])) + .renderBelowSymbols() // Add an inner (blue) polyline LineStyleLayer(identifier: "polyline-inner", source: polylineSource) @@ -36,6 +37,7 @@ struct PolylineMapView: View { curveType: .exponential, parameters: NSExpression(forConstantValue: 1.5), stops: NSExpression(forConstantValue: [14: 3, 18: 16])) + .renderBelowSymbols() } } } diff --git a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift index e629871..8a53c61 100644 --- a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift +++ b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift @@ -268,6 +268,10 @@ public class MapViewCoordinator: NSObject, MLNMapV } func addLayers(to mglStyle: MLNStyle) { + let firstSymbolLayer = mglStyle.layers.first { layer in + layer is MLNSymbolStyleLayer + } + for layerSpec in parent.userLayers { // DISCUSS: What preventions should we try to put in place against the user accidentally adding the same layer twice? let newLayer = layerSpec.makeStyleLayer(style: mglStyle).makeMLNStyleLayer() @@ -302,6 +306,12 @@ public class MapViewCoordinator: NSObject, MLNMapV mglStyle.addLayer(newLayer) case .belowOthers: mglStyle.insertLayer(newLayer, at: 0) + case .belowSymbols: + if let firstSymbolLayer = firstSymbolLayer { + mglStyle.insertLayer(newLayer, below: firstSymbolLayer) + } else { + mglStyle.addLayer(newLayer) + } } } } From 8f636203daa1ce9c0444a34eaba26f45be057347 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Sun, 2 Feb 2025 13:00:15 +0900 Subject: [PATCH 2/4] Cleanup; add CHANGELOG --- CHANGELOG.md | 11 +++++++++++ Sources/MapLibreSwiftDSL/Style Layers/Line.swift | 2 +- Sources/MapLibreSwiftUI/Examples/Polyline.swift | 2 ++ Sources/MapLibreSwiftUI/MapViewCoordinator.swift | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49186fb..58deb7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Version 0.7.0 - 2025-02-02 + +### Added + +- Adds support for a `belowSymbols` layer order. The `renderBelowSymbols` modifier on layers will insert the new layer below the first symbol layer in the style. This allows for rendering below labels and icons. +- Potentially BREAKING: `belowSymbols` is now the default on the `LineStyleLayer`. This is probbaly what most users want. + +### Fixed + +- Moved modifiers on `StyleLayer` to `StyleLayerDefinition`. The previous extension of `StyleLayer` was a mistake, since `StyleLayerDefinition` is the supertype, and none of the behavior was specific to `StyleLayer`. + ## Version 0.6.0 - 2025-01-14 - Potentially BREAKING: Upgrades Mockable to 0.2.0. If you're using mockable in your project, this may require you to upgrade there as well. diff --git a/Sources/MapLibreSwiftDSL/Style Layers/Line.swift b/Sources/MapLibreSwiftDSL/Style Layers/Line.swift index 1c10508..4551056 100644 --- a/Sources/MapLibreSwiftDSL/Style Layers/Line.swift +++ b/Sources/MapLibreSwiftDSL/Style Layers/Line.swift @@ -11,7 +11,7 @@ import MapLibreSwiftMacros public struct LineStyleLayer: SourceBoundVectorStyleLayerDefinition { public let identifier: String public let sourceLayerIdentifier: String? - public var insertionPosition: LayerInsertionPosition = .aboveOthers + public var insertionPosition: LayerInsertionPosition = .belowSymbols public var isVisible: Bool = true public var maximumZoomLevel: Float? = nil public var minimumZoomLevel: Float? = nil diff --git a/Sources/MapLibreSwiftUI/Examples/Polyline.swift b/Sources/MapLibreSwiftUI/Examples/Polyline.swift index f4b40c2..144611d 100644 --- a/Sources/MapLibreSwiftUI/Examples/Polyline.swift +++ b/Sources/MapLibreSwiftUI/Examples/Polyline.swift @@ -26,6 +26,7 @@ struct PolylineMapView: View { curveType: .exponential, parameters: NSExpression(forConstantValue: 1.5), stops: NSExpression(forConstantValue: [14: 6, 18: 24])) + // Not required as this is the default; demonstration to be explicit .renderBelowSymbols() // Add an inner (blue) polyline @@ -37,6 +38,7 @@ struct PolylineMapView: View { curveType: .exponential, parameters: NSExpression(forConstantValue: 1.5), stops: NSExpression(forConstantValue: [14: 3, 18: 16])) + // Not required as this is the default; demonstration to be explicit .renderBelowSymbols() } } diff --git a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift index 8a53c61..ba8b2fc 100644 --- a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift +++ b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift @@ -307,7 +307,7 @@ public class MapViewCoordinator: NSObject, MLNMapV case .belowOthers: mglStyle.insertLayer(newLayer, at: 0) case .belowSymbols: - if let firstSymbolLayer = firstSymbolLayer { + if let firstSymbolLayer { mglStyle.insertLayer(newLayer, below: firstSymbolLayer) } else { mglStyle.addLayer(newLayer) From b7bd040dbd696ac373eda885232c2c84d65a0307 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Tue, 4 Feb 2025 12:11:23 +0900 Subject: [PATCH 3/4] Revert the default back to below all; rework above/below refs into enum --- CHANGELOG.md | 2 +- .../Style Layers/Background.swift | 2 +- .../Style Layers/Circle.swift | 2 +- .../Style Layers/FillStyleLayer.swift | 2 +- .../MapLibreSwiftDSL/Style Layers/Line.swift | 2 +- .../Style Layers/Style Layer.swift | 50 +++++++++---------- .../Style Layers/Symbol.swift | 2 +- Sources/MapLibreSwiftUI/Examples/Layers.swift | 2 +- .../MapLibreSwiftUI/Examples/Polyline.swift | 6 +-- .../MapLibreSwiftUI/MapViewCoordinator.swift | 13 ++--- .../Examples/LayerPreviewTests.swift | 2 +- 11 files changed, 41 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58deb7f..dc38807 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Adds support for a `belowSymbols` layer order. The `renderBelowSymbols` modifier on layers will insert the new layer below the first symbol layer in the style. This allows for rendering below labels and icons. -- Potentially BREAKING: `belowSymbols` is now the default on the `LineStyleLayer`. This is probbaly what most users want. +- BREAKING: Refactored the layer position modifiers to accept enum variants to enable better extensibility. ### Fixed diff --git a/Sources/MapLibreSwiftDSL/Style Layers/Background.swift b/Sources/MapLibreSwiftDSL/Style Layers/Background.swift index e472fa3..0f9e104 100644 --- a/Sources/MapLibreSwiftDSL/Style Layers/Background.swift +++ b/Sources/MapLibreSwiftDSL/Style Layers/Background.swift @@ -7,7 +7,7 @@ import MapLibreSwiftMacros @MLNStyleProperty("backgroundOpacity", supportsInterpolation: true) public struct BackgroundLayer: StyleLayer { public let identifier: String - public var insertionPosition: LayerInsertionPosition = .belowOthers + public var insertionPosition: LayerInsertionPosition = .below(.all) public var isVisible: Bool = true public var maximumZoomLevel: Float? = nil public var minimumZoomLevel: Float? = nil diff --git a/Sources/MapLibreSwiftDSL/Style Layers/Circle.swift b/Sources/MapLibreSwiftDSL/Style Layers/Circle.swift index 040065e..6026212 100644 --- a/Sources/MapLibreSwiftDSL/Style Layers/Circle.swift +++ b/Sources/MapLibreSwiftDSL/Style Layers/Circle.swift @@ -10,7 +10,7 @@ import MapLibreSwiftMacros public struct CircleStyleLayer: SourceBoundVectorStyleLayerDefinition { public let identifier: String public let sourceLayerIdentifier: String? - public var insertionPosition: LayerInsertionPosition = .aboveOthers + public var insertionPosition: LayerInsertionPosition = .above(.all) public var isVisible: Bool = true public var maximumZoomLevel: Float? = nil public var minimumZoomLevel: Float? = nil diff --git a/Sources/MapLibreSwiftDSL/Style Layers/FillStyleLayer.swift b/Sources/MapLibreSwiftDSL/Style Layers/FillStyleLayer.swift index 018723f..f610c27 100644 --- a/Sources/MapLibreSwiftDSL/Style Layers/FillStyleLayer.swift +++ b/Sources/MapLibreSwiftDSL/Style Layers/FillStyleLayer.swift @@ -10,7 +10,7 @@ import MapLibreSwiftMacros public struct FillStyleLayer: SourceBoundVectorStyleLayerDefinition { public let identifier: String public let sourceLayerIdentifier: String? - public var insertionPosition: LayerInsertionPosition = .aboveOthers + public var insertionPosition: LayerInsertionPosition = .above(.all) public var isVisible: Bool = true public var maximumZoomLevel: Float? = nil public var minimumZoomLevel: Float? = nil diff --git a/Sources/MapLibreSwiftDSL/Style Layers/Line.swift b/Sources/MapLibreSwiftDSL/Style Layers/Line.swift index 4551056..768523d 100644 --- a/Sources/MapLibreSwiftDSL/Style Layers/Line.swift +++ b/Sources/MapLibreSwiftDSL/Style Layers/Line.swift @@ -11,7 +11,7 @@ import MapLibreSwiftMacros public struct LineStyleLayer: SourceBoundVectorStyleLayerDefinition { public let identifier: String public let sourceLayerIdentifier: String? - public var insertionPosition: LayerInsertionPosition = .belowSymbols + public var insertionPosition: LayerInsertionPosition = .above(.all) public var isVisible: Bool = true public var maximumZoomLevel: Float? = nil public var minimumZoomLevel: Float? = nil diff --git a/Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift b/Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift index 01b8039..7a72f62 100644 --- a/Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift +++ b/Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift @@ -1,24 +1,34 @@ import InternalUtils import MapLibre +/// A layer reference specifying which layer we should insert a new layer above. +public enum LayerReferenceAbove: Equatable { + /// A specific layer, referenced by ID. + case layer(layerId: String) + /// The group of all layers currently in the style. + case all +} + +/// A layer reference specifying which layer we should insert a new layer below. +public enum LayerReferenceBelow: Equatable { + /// A specific layer, referenced by ID. + case layer(layerId: String) + /// The group of all layers currently in the style. + case all + /// The group of symbol layers currently in the style. + case symbols +} + /// Specifies a preference for where the layer should be inserted in the hierarchy. public enum LayerInsertionPosition: Equatable { /// The layer should be inserted above the layer with ID ``layerID``. /// /// If no such layer exists, the layer will be added above others and an error will be logged. - case above(layerID: String) + case above(LayerReferenceAbove) /// The layer should be inserted below the layer with ID ``layerID``. /// /// If no such layer exists, the layer will be added above others and an error will be logged. - case below(layerID: String) - /// The layer should be inserted above other existing layers. - case aboveOthers - /// The layer should be inserted below other existing layers. - case belowOthers - /// The layer should be inserted below the first symbol layer (used for labels and iconography). - /// - /// If there are no symbol layers in the style, the layer is inserted above other existing layers. - case belowSymbols + case below(LayerReferenceBelow) } /// Internal style enum that wraps a source reference. @@ -151,23 +161,11 @@ public extension StyleLayerDefinition { modified(self) { $0.maximumZoomLevel = value } } - func renderAbove(_ layerID: String) -> Self { - modified(self) { $0.insertionPosition = .above(layerID: layerID) } - } - - func renderBelow(_ layerID: String) -> Self { - modified(self) { $0.insertionPosition = .below(layerID: layerID) } - } - - func renderAboveOthers() -> Self { - modified(self) { $0.insertionPosition = .aboveOthers } - } - - func renderBelowOthers() -> Self { - modified(self) { $0.insertionPosition = .belowOthers } + func renderAbove(_ layerReference: LayerReferenceAbove) -> Self { + modified(self) { $0.insertionPosition = .above(layerReference) } } - func renderBelowSymbols() -> Self { - modified(self) { $0.insertionPosition = .belowSymbols } + func renderBelow(_ layerReference: LayerReferenceBelow) -> Self { + modified(self) { $0.insertionPosition = .below(layerReference) } } } diff --git a/Sources/MapLibreSwiftDSL/Style Layers/Symbol.swift b/Sources/MapLibreSwiftDSL/Style Layers/Symbol.swift index 24b17a7..a3e322c 100644 --- a/Sources/MapLibreSwiftDSL/Style Layers/Symbol.swift +++ b/Sources/MapLibreSwiftDSL/Style Layers/Symbol.swift @@ -23,7 +23,7 @@ import MapLibreSwiftMacros public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition { public let identifier: String public let sourceLayerIdentifier: String? - public var insertionPosition: LayerInsertionPosition = .aboveOthers + public var insertionPosition: LayerInsertionPosition = .above(.all) public var isVisible: Bool = true public var maximumZoomLevel: Float? = nil public var minimumZoomLevel: Float? = nil diff --git a/Sources/MapLibreSwiftUI/Examples/Layers.swift b/Sources/MapLibreSwiftUI/Examples/Layers.swift index 281670c..f57205e 100644 --- a/Sources/MapLibreSwiftUI/Examples/Layers.swift +++ b/Sources/MapLibreSwiftUI/Examples/Layers.swift @@ -38,7 +38,7 @@ let clustered = ShapeSource(identifier: "points", options: [.clustered: true, .c // Silly example: a background layer on top of everything to create a tint effect BackgroundLayer(identifier: "rose-colored-glasses") .backgroundColor(.systemPink.withAlphaComponent(0.3)) - .renderAboveOthers() + .renderAbove(.all) } .ignoresSafeArea(.all) } diff --git a/Sources/MapLibreSwiftUI/Examples/Polyline.swift b/Sources/MapLibreSwiftUI/Examples/Polyline.swift index 144611d..f80e840 100644 --- a/Sources/MapLibreSwiftUI/Examples/Polyline.swift +++ b/Sources/MapLibreSwiftUI/Examples/Polyline.swift @@ -26,8 +26,7 @@ struct PolylineMapView: View { curveType: .exponential, parameters: NSExpression(forConstantValue: 1.5), stops: NSExpression(forConstantValue: [14: 6, 18: 24])) - // Not required as this is the default; demonstration to be explicit - .renderBelowSymbols() + .renderBelow(.symbols) // Add an inner (blue) polyline LineStyleLayer(identifier: "polyline-inner", source: polylineSource) @@ -38,8 +37,7 @@ struct PolylineMapView: View { curveType: .exponential, parameters: NSExpression(forConstantValue: 1.5), stops: NSExpression(forConstantValue: [14: 3, 18: 16])) - // Not required as this is the default; demonstration to be explicit - .renderBelowSymbols() + .renderBelow(.symbols) } } } diff --git a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift index ba8b2fc..55a313e 100644 --- a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift +++ b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift @@ -288,25 +288,26 @@ public class MapViewCoordinator: NSObject, MLNMapV } switch layerSpec.insertionPosition { - case let .above(layerID: id): + case let .above(.layer(layerId: id)): if let layer = mglStyle.layer(withIdentifier: id) { mglStyle.insertLayer(newLayer, above: layer) } else { NSLog("Failed to find layer with ID \(id). Adding layer on top.") mglStyle.addLayer(newLayer) } - case let .below(layerID: id): + case .above(.all): + mglStyle.addLayer(newLayer) + + case let .below(.layer(layerId: id)): if let layer = mglStyle.layer(withIdentifier: id) { mglStyle.insertLayer(newLayer, below: layer) } else { NSLog("Failed to find layer with ID \(id). Adding layer on top.") mglStyle.addLayer(newLayer) } - case .aboveOthers: - mglStyle.addLayer(newLayer) - case .belowOthers: + case .below(.all): mglStyle.insertLayer(newLayer, at: 0) - case .belowSymbols: + case .below(.symbols): if let firstSymbolLayer { mglStyle.insertLayer(newLayer, below: firstSymbolLayer) } else { diff --git a/Tests/MapLibreSwiftUITests/Examples/LayerPreviewTests.swift b/Tests/MapLibreSwiftUITests/Examples/LayerPreviewTests.swift index b1e98cb..6e0c816 100644 --- a/Tests/MapLibreSwiftUITests/Examples/LayerPreviewTests.swift +++ b/Tests/MapLibreSwiftUITests/Examples/LayerPreviewTests.swift @@ -30,7 +30,7 @@ final class LayerPreviewTests: XCTestCase { // Silly example: a background layer on top of everything to create a tint effect BackgroundLayer(identifier: "rose-colored-glasses") .backgroundColor(.systemPink.withAlphaComponent(0.3)) - .renderAboveOthers() + .renderAbove(.all) } } } From ce0385ef7bd68011889a8c063072b290e438c6f7 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Tue, 4 Feb 2025 12:13:44 +0900 Subject: [PATCH 4/4] swiftformat --- Sources/MapLibreSwiftUI/MapViewCoordinator.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift index 55a313e..0157bc0 100644 --- a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift +++ b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift @@ -297,7 +297,6 @@ public class MapViewCoordinator: NSObject, MLNMapV } case .above(.all): mglStyle.addLayer(newLayer) - case let .below(.layer(layerId: id)): if let layer = mglStyle.layer(withIdentifier: id) { mglStyle.insertLayer(newLayer, below: layer)