Skip to content

Commit 7aee112

Browse files
pjleonard37github-actions[bot]
authored andcommitted
Add custom vector icons examples for iOS and Android (#8285)
Ports the custom colorized vector icons from GL JS to iOS (SwiftUI) and Android (Jetpack Compose). Additionally, moves the functionality from the Icon Size Change examples into these new examples and removes the old examples. GitOrigin-RevId: b92c8c82cc24c5029b212e91db70518342cc260d
1 parent 404d798 commit 7aee112

File tree

5 files changed

+97
-146
lines changed

5 files changed

+97
-146
lines changed

Examples.xcodeproj/project.pbxproj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
6B040F65241ABF600D70D14D /* Custom3DPuckExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C957F9CA07061B793C2DD4A /* Custom3DPuckExample.swift */; platformFilter = ios; };
9292
7036A19FCD2CCE85BDDF4E00 /* TrackingModeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F5E598A16FA446F583344CB /* TrackingModeExample.swift */; platformFilter = ios; };
9393
732DD7412EA8308000CCDD65 /* AppearancesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 732DD7402EA8308000CCDD65 /* AppearancesExample.swift */; };
94+
7363B8EE2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7363B8ED2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift */; };
9495
7365170E39A459EB4DFA198B /* ExamplesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE18E37A8652B4807D2459F1 /* ExamplesUITests.swift */; platformFilter = ios; };
9596
73C81A0F2EC3D14D00387773 /* SettingsSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73C81A0E2EC3D14D00387773 /* SettingsSheet.swift */; };
9697
754B87ED0F8761A02C8C09C2 /* StyleOverridesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF257763AFD6E4964B28C9EB /* StyleOverridesModel.swift */; platformFilter = ios; };
@@ -132,7 +133,6 @@
132133
B304BACFCD08802A740E8919 /* PuckPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0CC67084BA1191D0B179A94 /* PuckPlayground.swift */; };
133134
B53EA441C54E2B680A7E99F0 /* BuildingExtrusionsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFF80A86D7DEF54E0A7256DF /* BuildingExtrusionsExample.swift */; platformFilter = ios; };
134135
B6AA94E58F828EDE740BD047 /* RadarExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2980F63F3B84D7031779978 /* RadarExtensions.swift */; platformFilter = ios; };
135-
B9B1EE72E6203358F2785916 /* IconSizeChangeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */; platformFilter = ios; };
136136
B9D4B9C3042383738AB5B667 /* DebugMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59EF8798357CF55109A4E56 /* DebugMapExample.swift */; platformFilter = ios; };
137137
BD99E89F050E7D93846147FF /* ViewAnnotationBasicExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C6CF222C8EE9AE69563E39 /* ViewAnnotationBasicExample.swift */; platformFilter = ios; };
138138
BDABAAC8727AF67A0DEE2020 /* AnimateGeoJSONLineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */; platformFilter = ios; };
@@ -257,7 +257,6 @@
257257
3DCA3CFE97357EF7D190E2A2 /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; };
258258
3E2F68B22AFF73A71F86CABC /* ExamplesUITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = ExamplesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
259259
3E3A0CCD53B02FF2BAD830A2 /* CameraAnimationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAnimationExample.swift; sourceTree = "<group>"; };
260-
3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconSizeChangeExample.swift; sourceTree = "<group>"; };
261260
3FB717239BBFE5103C1EB1A4 /* AdvancedViewportGesturesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedViewportGesturesExample.swift; sourceTree = "<group>"; };
262261
3FC5980DD30479F30127BA71 /* NavigationSimulator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSimulator.swift; sourceTree = "<group>"; };
263262
450C8D5E4B84428FE51BCA97 /* route.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = route.geojson; sourceTree = "<group>"; };
@@ -299,6 +298,8 @@
299298
7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateGeoJSONLineExample.swift; sourceTree = "<group>"; };
300299
732DD7402EA8308000CCDD65 /* AppearancesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancesExample.swift; sourceTree = "<group>"; };
301300
73C81A0E2EC3D14D00387773 /* SettingsSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SettingsSheet.swift; path = Sources/Examples/Controllers/SettingsSheet.swift; sourceTree = "<group>"; };
301+
7363B8ED2ED0D7E4002D00B3 /* CustomColorizedVectorIconsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CustomColorizedVectorIconsExample.swift; path = "Sources/Examples/SwiftUI Examples/CustomColorizedVectorIconsExample.swift"; sourceTree = "<group>"; };
302+
7363B8ED2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CustomVectorIconsExample.swift; path = "Sources/Examples/SwiftUI Examples/CustomVectorIconsExample.swift"; sourceTree = "<group>"; };
302303
73E23CA02EB25A510035A052 /* AppearancesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppearancesExample.swift; path = "Sources/Examples/SwiftUI Examples/AppearancesExample.swift"; sourceTree = "<group>"; };
303304
75D03F5A3A0E879717BFE421 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
304305
75F4874FE9742DF10F0D0483 /* ColorScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorScheme.swift; sourceTree = "<group>"; };
@@ -577,7 +578,6 @@
577578
4D5DB9BD5E97D3C0080EC5D3 /* CustomPointAnnotationExample.swift */,
578579
3333EF3E0F1C789809F385AF /* DynamicViewAnnotationExample.swift */,
579580
5FC0F09FF1EF1A88BC1C6545 /* FrameViewAnnotationsExample.swift */,
580-
3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */,
581581
3BDE7738CA55957F3FAC3ECE /* LineAnnotationExample.swift */,
582582
B7A7586D05B960928AB17A0D /* MultipleGeometriesExample.swift */,
583583
A8359ECB5024BEF3722C3CA1 /* PointAnnotationClusteringExample.swift */,
@@ -721,6 +721,8 @@
721721
isa = PBXGroup;
722722
children = (
723723
73C81A0E2EC3D14D00387773 /* SettingsSheet.swift */,
724+
7363B8ED2ED0D7E4002D00B3 /* CustomColorizedVectorIconsExample.swift */,
725+
7363B8ED2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift */,
724726
73E23CA02EB25A510035A052 /* AppearancesExample.swift */,
725727
52909911727F239150D4FE30 /* Examples */,
726728
DA93D15473B052576C7D2965 /* ExamplesTests */,
@@ -1022,6 +1024,7 @@
10221024
A1FABB64B1C3EEC9E5AF06A0 /* ContentView.swift in Sources */,
10231025
60A1572CCF5763FA3C946B89 /* Custom2DPuckExample.swift in Sources */,
10241026
6B040F65241ABF600D70D14D /* Custom3DPuckExample.swift in Sources */,
1027+
7363B8EE2ED0D7E4002D00B3 /* CustomVectorIconsExample.swift in Sources */,
10251028
4E64A70408A69F2BC9F70610 /* CustomGeometrySourceExample.swift in Sources */,
10261029
EE4064D753E360A6A6AC5BAC /* CustomLayerExample.swift in Sources */,
10271030
08DD7D352E50C412B667D6F6 /* CustomLayerExampleShaders.metal in Sources */,
@@ -1056,7 +1059,6 @@
10561059
5F2AD73C8104089C9291574E /* GeofencingUserLocation.swift in Sources */,
10571060
0414AD72988F405F5BA1D843 /* GlobeFlyToExample.swift in Sources */,
10581061
49F6209402BF34C06C90107A /* HeatmapLayerGlobeExample.swift in Sources */,
1059-
B9B1EE72E6203358F2785916 /* IconSizeChangeExample.swift in Sources */,
10601062
2D3DF43CEE3D4C2B0B012E0E /* IndoorExample.swift in Sources */,
10611063
423A42B555DD0B3AD4856FCF /* InsetMapExample.swift in Sources */,
10621064
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)