Skip to content

Commit b7972d9

Browse files
mapbox-github-ci-writer-5[bot]pjleonard37
authored andcommitted
[Backport release/v0.17] Add custom vector icons examples for iOS and Android (#8507)
Backport b92c8c82cc24c5029b212e91db70518342cc260d from #8285. cc @mapbox/maps-ios cc @mapbox/maps-android Co-authored-by: Patrick Leonard <[email protected]> GitOrigin-RevId: f39812846df50b064bae04631c1b81911b0e9791
1 parent d14ab2d commit b7972d9

File tree

5 files changed

+99
-146
lines changed

5 files changed

+99
-146
lines changed

Examples.xcodeproj/project.pbxproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
6B040F65241ABF600D70D14D /* Custom3DPuckExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C957F9CA07061B793C2DD4A /* Custom3DPuckExample.swift */; platformFilter = ios; };
9595
7036A19FCD2CCE85BDDF4E00 /* TrackingModeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F5E598A16FA446F583344CB /* TrackingModeExample.swift */; platformFilter = ios; };
9696
732DD7412EA8308000CCDD65 /* AppearancesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 732DD7402EA8308000CCDD65 /* AppearancesExample.swift */; };
97+
7363B8EE2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7363B8ED2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift */; };
9798
7365170E39A459EB4DFA198B /* ExamplesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE18E37A8652B4807D2459F1 /* ExamplesUITests.swift */; platformFilter = ios; };
9899
754B87ED0F8761A02C8C09C2 /* StyleOverridesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF257763AFD6E4964B28C9EB /* StyleOverridesModel.swift */; platformFilter = ios; };
99100
759D42AA5FE93B6FA9DFADF5 /* LocationOverrideExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45B39AE24486FED5ED30392D /* LocationOverrideExample.swift */; };
@@ -134,7 +135,6 @@
134135
B304BACFCD08802A740E8919 /* PuckPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0CC67084BA1191D0B179A94 /* PuckPlayground.swift */; };
135136
B53EA441C54E2B680A7E99F0 /* BuildingExtrusionsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFF80A86D7DEF54E0A7256DF /* BuildingExtrusionsExample.swift */; platformFilter = ios; };
136137
B6AA94E58F828EDE740BD047 /* RadarExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2980F63F3B84D7031779978 /* RadarExtensions.swift */; platformFilter = ios; };
137-
B9B1EE72E6203358F2785916 /* IconSizeChangeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */; platformFilter = ios; };
138138
B9D4B9C3042383738AB5B667 /* DebugMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59EF8798357CF55109A4E56 /* DebugMapExample.swift */; platformFilter = ios; };
139139
BD99E89F050E7D93846147FF /* ViewAnnotationBasicExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C6CF222C8EE9AE69563E39 /* ViewAnnotationBasicExample.swift */; platformFilter = ios; };
140140
BDABAAC8727AF67A0DEE2020 /* AnimateGeoJSONLineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */; platformFilter = ios; };
@@ -259,7 +259,6 @@
259259
3DCA3CFE97357EF7D190E2A2 /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; };
260260
3E2F68B22AFF73A71F86CABC /* ExamplesUITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = ExamplesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
261261
3E3A0CCD53B02FF2BAD830A2 /* CameraAnimationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAnimationExample.swift; sourceTree = "<group>"; };
262-
3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconSizeChangeExample.swift; sourceTree = "<group>"; };
263262
3FB717239BBFE5103C1EB1A4 /* AdvancedViewportGesturesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedViewportGesturesExample.swift; sourceTree = "<group>"; };
264263
3FC5980DD30479F30127BA71 /* NavigationSimulator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSimulator.swift; sourceTree = "<group>"; };
265264
450C8D5E4B84428FE51BCA97 /* route.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = route.geojson; sourceTree = "<group>"; };
@@ -300,6 +299,9 @@
300299
70922E748D003176C4A3C60A /* HeatmapLayerGlobeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeatmapLayerGlobeExample.swift; sourceTree = "<group>"; };
301300
7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateGeoJSONLineExample.swift; sourceTree = "<group>"; };
302301
732DD7402EA8308000CCDD65 /* AppearancesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancesExample.swift; sourceTree = "<group>"; };
302+
73C81A0E2EC3D14D00387773 /* SettingsSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SettingsSheet.swift; path = Sources/Examples/Controllers/SettingsSheet.swift; sourceTree = "<group>"; };
303+
7363B8ED2ED0D7E4002D00B3 /* CustomColorizedVectorIconsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CustomColorizedVectorIconsExample.swift; path = "Sources/Examples/SwiftUI Examples/CustomColorizedVectorIconsExample.swift"; sourceTree = "<group>"; };
304+
7363B8ED2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CustomVectorIconsExample.swift; path = "Sources/Examples/SwiftUI Examples/CustomVectorIconsExample.swift"; sourceTree = "<group>"; };
303305
73E23CA02EB25A510035A052 /* AppearancesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppearancesExample.swift; path = "Sources/Examples/SwiftUI Examples/AppearancesExample.swift"; sourceTree = "<group>"; };
304306
75D03F5A3A0E879717BFE421 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
305307
75F4874FE9742DF10F0D0483 /* ColorScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorScheme.swift; sourceTree = "<group>"; };
@@ -578,7 +580,6 @@
578580
4D5DB9BD5E97D3C0080EC5D3 /* CustomPointAnnotationExample.swift */,
579581
3333EF3E0F1C789809F385AF /* DynamicViewAnnotationExample.swift */,
580582
5FC0F09FF1EF1A88BC1C6545 /* FrameViewAnnotationsExample.swift */,
581-
3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */,
582583
3BDE7738CA55957F3FAC3ECE /* LineAnnotationExample.swift */,
583584
B7A7586D05B960928AB17A0D /* MultipleGeometriesExample.swift */,
584585
A8359ECB5024BEF3722C3CA1 /* PointAnnotationClusteringExample.swift */,
@@ -721,6 +722,9 @@
721722
AFDB1EA82615CFDF02CE1D4D = {
722723
isa = PBXGroup;
723724
children = (
725+
73C81A0E2EC3D14D00387773 /* SettingsSheet.swift */,
726+
7363B8ED2ED0D7E4002D00B3 /* CustomColorizedVectorIconsExample.swift */,
727+
7363B8ED2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift */,
724728
73E23CA02EB25A510035A052 /* AppearancesExample.swift */,
725729
52909911727F239150D4FE30 /* Examples */,
726730
DA93D15473B052576C7D2965 /* ExamplesTests */,
@@ -1024,6 +1028,7 @@
10241028
A1FABB64B1C3EEC9E5AF06A0 /* ContentView.swift in Sources */,
10251029
60A1572CCF5763FA3C946B89 /* Custom2DPuckExample.swift in Sources */,
10261030
6B040F65241ABF600D70D14D /* Custom3DPuckExample.swift in Sources */,
1031+
7363B8EE2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift in Sources */,
10271032
4E64A70408A69F2BC9F70610 /* CustomGeometrySourceExample.swift in Sources */,
10281033
EE4064D753E360A6A6AC5BAC /* CustomLayerExample.swift in Sources */,
10291034
08DD7D352E50C412B667D6F6 /* CustomLayerExampleShaders.metal in Sources */,
@@ -1058,7 +1063,6 @@
10581063
5F2AD73C8104089C9291574E /* GeofencingUserLocation.swift in Sources */,
10591064
0414AD72988F405F5BA1D843 /* GlobeFlyToExample.swift in Sources */,
10601065
49F6209402BF34C06C90107A /* HeatmapLayerGlobeExample.swift in Sources */,
1061-
B9B1EE72E6203358F2785916 /* IconSizeChangeExample.swift in Sources */,
10621066
2D3DF43CEE3D4C2B0B012E0E /* IndoorExample.swift in Sources */,
10631067
423A42B555DD0B3AD4856FCF /* InsetMapExample.swift in Sources */,
10641068
94DB7E8C829041DC5F5B2300 /* InstrumentClusterCarPlaySceneDelegate.swift in Sources */,

Sources/Examples/All Examples/Annotations/IconSizeChangeExample.swift

Lines changed: 0 additions & 139 deletions
This file was deleted.

Sources/Examples/Models/Examples.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,6 @@ struct Examples {
7070
Example(title: "Animate Marker Position",
7171
description: "Animate updates to a marker/annotation's position.",
7272
type: AnimatedMarkerExample.self)
73-
Example(title: "Change icon size",
74-
description: "Change icon size with Symbol layer.",
75-
type: IconSizeChangeExample.self)
7673
Example(title: "Draw multiple geometries",
7774
description: "Draw multiple shapes on a map.",
7875
type: MultipleGeometriesExample.self)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import SwiftUI
2+
import MapboxMaps
3+
4+
/// Example demonstrating custom vector icons with dynamic styling and interaction.
5+
/// This example shows how to:
6+
/// - Dynamically colorize vector icons based on feature properties using the image expression
7+
/// - Interactively change icon size by tapping on icons
8+
///
9+
/// Vector icons are parameterized SVG images that can be styled at runtime. In this example,
10+
/// three flag icons are colored red, yellow, and purple using the 'flagColor' property.
11+
/// Tap any flag to toggle its size between 1x and 2x.
12+
///
13+
/// For this example to work, the SVGs must live inside the map style. The SVG file was uploaded
14+
/// to Mapbox Studio with the name `flag`, making it available for customization at runtime.
15+
/// You can add vector icons to your own style in Mapbox Studio.
16+
struct CustomVectorIconsExample: View {
17+
@State private var selectedFlagId: String?
18+
19+
var body: some View {
20+
Map(initialViewport: .camera(center: CLLocationCoordinate2D(latitude: 60.185755, longitude: 24.6881), zoom: 16)) {
21+
// Create GeoJSON source with three flag locations
22+
GeoJSONSource(id: "points")
23+
.data(.featureCollection(flagFeatures))
24+
25+
// Create symbol layer with parameterized icon
26+
SymbolLayer(id: "points", source: "points")
27+
.iconImage(
28+
Exp(.image) {
29+
"flag"
30+
["params": ["flag_color": Exp(.get) { "flagColor" }]]
31+
}
32+
)
33+
.iconSize(
34+
Exp(.switchCase) {
35+
Exp(.eq) {
36+
Exp(.get) { "id" }
37+
selectedFlagId ?? ""
38+
}
39+
2.0
40+
1.0
41+
}
42+
)
43+
.iconAllowOverlap(true)
44+
45+
// Add tap interaction for the symbol layer
46+
TapInteraction(.layer("points")) { feature, _ in
47+
if let id = feature.properties["id"]??.rawValue as? String {
48+
selectedFlagId = (selectedFlagId == id) ? nil : id
49+
}
50+
return true
51+
}
52+
}
53+
.mapStyle(MapStyle(uri: StyleURI(rawValue: "mapbox://styles/mapbox-map-design/cm4r19bcm00ao01qvhp3jc2gi")!))
54+
.ignoresSafeArea()
55+
}
56+
57+
/// GeoJSON features with flag locations and colors
58+
private var flagFeatures: FeatureCollection {
59+
FeatureCollection(features: [
60+
createFlagFeature(
61+
id: "flag-red",
62+
longitude: 24.68727,
63+
latitude: 60.185755,
64+
color: "red"
65+
),
66+
createFlagFeature(
67+
id: "flag-yellow",
68+
longitude: 24.68827,
69+
latitude: 60.186255,
70+
color: "yellow"
71+
),
72+
createFlagFeature(
73+
id: "flag-purple",
74+
longitude: 24.68927,
75+
latitude: 60.186055,
76+
color: "#800080"
77+
)
78+
])
79+
}
80+
81+
/// Creates a feature with a flag at the specified location and color
82+
private func createFlagFeature(id: String, longitude: Double, latitude: Double, color: String) -> Feature {
83+
var feature = Feature(geometry: .point(Point(CLLocationCoordinate2D(latitude: latitude, longitude: longitude))))
84+
feature.properties = [
85+
"id": .string(id),
86+
"flagColor": .string(color)
87+
]
88+
return feature
89+
}
90+
}

Sources/Examples/SwiftUI Examples/SwiftUIRoot.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct SwiftUIExamples {
2828
#endif
2929
Example("Clustering data", note: "Display GeoJSON data with clustering using custom layers and handle interactions with them.", destination: ClusteringExample())
3030
Example("Appearances", note: "Change icon images dynamically using the Appearances API with feature-state.", destination: AppearancesExample())
31+
Example("Custom Vector Icons", note: "Dynamically style vector icons with custom colors and interactively change their size on tap.", destination: CustomVectorIconsExample())
3132
Example("Geofencing User Location", note: "Set geofence on user initial location.", destination: GeofencingUserLocation())
3233
Example("Geofencing Playground", note: "Showcase isochrone API together with geofences.", destination: GeofencingPlayground())
3334
Example("Color Themes", note: "Showcase the Color Theme API", destination: ColorThemeExample())

0 commit comments

Comments
 (0)