Skip to content

Commit

Permalink
Enable layer insertion below symbols (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianthetechie authored Feb 4, 2025
1 parent 61cffb6 commit ac5f240
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 41 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
- BREAKING: Refactored the layer position modifiers to accept enum variants to enable better extensibility.

### 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.
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/Background.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import MapLibreSwiftMacros
@MLNStyleProperty<Float>("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
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/Circle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/FillStyleLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/Line.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 = .above(.all)
public var isVisible: Bool = true
public var maximumZoomLevel: Float? = nil
public var minimumZoomLevel: Float? = nil
Expand Down
54 changes: 25 additions & 29 deletions Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift
Original file line number Diff line number Diff line change
@@ -1,20 +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
case below(LayerReferenceBelow)
}

/// Internal style enum that wraps a source reference.
Expand Down Expand Up @@ -132,7 +146,7 @@ public extension StyleLayer {
}
}

public extension StyleLayer {
public extension StyleLayerDefinition {
// MARK: - Common modifiers

func visible(_ value: Bool) -> Self {
Expand All @@ -147,29 +161,11 @@ public extension StyleLayer {
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 renderAbove(_ layerReference: LayerReferenceAbove) -> Self {
modified(self) { $0.insertionPosition = .above(layerReference) }
}

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 renderBelow(_ layerReference: LayerReferenceBelow) -> Self {
modified(self) { $0.insertionPosition = .below(layerReference) }
}
}
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/Symbol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftUI/Examples/Layers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/MapLibreSwiftUI/Examples/Polyline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct PolylineMapView: View {
curveType: .exponential,
parameters: NSExpression(forConstantValue: 1.5),
stops: NSExpression(forConstantValue: [14: 6, 18: 24]))
.renderBelow(.symbols)

// Add an inner (blue) polyline
LineStyleLayer(identifier: "polyline-inner", source: polylineSource)
Expand All @@ -36,6 +37,7 @@ struct PolylineMapView: View {
curveType: .exponential,
parameters: NSExpression(forConstantValue: 1.5),
stops: NSExpression(forConstantValue: [14: 3, 18: 16]))
.renderBelow(.symbols)
}
}
}
Expand Down
20 changes: 15 additions & 5 deletions Sources/MapLibreSwiftUI/MapViewCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ public class MapViewCoordinator<T: MapViewHostViewController>: NSObject, MLNMapV
}

func addLayers(to mglStyle: MLNStyle) {
let firstSymbolLayer = mglStyle.layers.first { layer in
layer is MLNSymbolStyleLayer
}

for layerSpec in parent.userLayers {

Check warning on line 275 in Sources/MapLibreSwiftUI/MapViewCoordinator.swift

View workflow job for this annotation

GitHub Actions / platform=iOS Simulator,name=iPhone 16,OS=18.1

main actor-isolated property 'userLayers' can not be referenced from a nonisolated context; this is an error in the Swift 6 language mode
// 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()
Expand All @@ -284,24 +288,30 @@ public class MapViewCoordinator<T: MapViewHostViewController>: 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 .below(.symbols):
if let firstSymbolLayer {
mglStyle.insertLayer(newLayer, below: firstSymbolLayer)
} else {
mglStyle.addLayer(newLayer)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down

0 comments on commit ac5f240

Please sign in to comment.