Skip to content

Commit

Permalink
Typed Interactions and Featuresets
Browse files Browse the repository at this point in the history
  • Loading branch information
persidskiy committed Oct 11, 2024
1 parent 1c5bff3 commit ffb3105
Show file tree
Hide file tree
Showing 36 changed files with 2,076 additions and 375 deletions.
12 changes: 12 additions & 0 deletions Apps/Examples/Examples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
14799547EFD5C4757FBAD6E4 /* ViewportExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0C19E67A2E87A3D18B7B511 /* ViewportExample.swift */; platformFilters = (ios, ); };
1687412AC1637D7EA697C7A4 /* View+OnShake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E9078B1B13E54C2FFC5FFC /* View+OnShake.swift */; };
1820AE40702C7875656BA2D7 /* radar0.gif in Resources */ = {isa = PBXBuildFile; fileRef = 1BCE444AC33D2F1F88F4CCF0 /* radar0.gif */; };
18919387995A155D6F64DADF /* StandardInteractiveBuildingsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97E057D9FFB1DE3E16F1F0B /* StandardInteractiveBuildingsExample.swift */; };
18F76FE745B049D1F0CAF6CA /* GeoJSONSourceExample.geojson in Resources */ = {isa = PBXBuildFile; fileRef = F033C8EFB89A90D6705B047D /* GeoJSONSourceExample.geojson */; };
191391C51FC69A6D36EB67F0 /* ResizableImageExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A0818B5BC601707E3235A9 /* ResizableImageExample.swift */; platformFilters = (ios, ); };
1B5230204B5659B1F05C303D /* DataDrivenSymbolsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99A72B5DBF68C4729A5DC65D /* DataDrivenSymbolsExample.swift */; platformFilters = (ios, ); };
Expand All @@ -32,6 +33,7 @@
2B44F3E8EF3A50D9AE6B825F /* route.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 450C8D5E4B84428FE51BCA97 /* route.geojson */; };
2C03342240D5487880316518 /* AddOneMarkerSymbolExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCBE814694CF08A9C2E4A42 /* AddOneMarkerSymbolExample.swift */; platformFilters = (ios, ); };
30589E5AB307FC934E466332 /* radar2.gif in Resources */ = {isa = PBXBuildFile; fileRef = D8730F8FB259A4F889609108 /* radar2.gif */; };
312CE7CED726F0A572301622 /* PinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC3D7884D057238010CB6E4 /* PinView.swift */; };
32FA2A4133B0464494212B34 /* Array+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BB15B17EDE597D37CFF3FCA /* Array+Split.swift */; platformFilters = (ios, ); };
33B816803AF5330796686AA1 /* CameraForExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF35650C6319088CAAF95F84 /* CameraForExample.swift */; platformFilters = (ios, ); };
373BD1EE35B76E43534E23F6 /* Fingertips in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, ); productRef = FD9311FF1C736B80A26F4258 /* Fingertips */; };
Expand Down Expand Up @@ -144,6 +146,7 @@
F5E96E5798947CA56FD77CF9 /* Fire_Hydrants.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 02DA2CC04980F807255D646B /* Fire_Hydrants.geojson */; };
F613749DCDDDDC6F041032A0 /* SimpleMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78811E5A3185D2D32495870A /* SimpleMapExample.swift */; };
F6E3EF9BE4F1D2F58DE1BED2 /* radar1.gif in Resources */ = {isa = PBXBuildFile; fileRef = 8BD8BADE1108B0D380D9BEF8 /* radar1.gif */; };
FA53EEA88DB29D4D5AC58514 /* StandardInteractiveFeaturesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FBAFDF929532EDB964786F4 /* StandardInteractiveFeaturesExample.swift */; };
FDA4B57BE32D92BB57A5B7E6 /* FeaturesAtPointExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6239A5CDA61892902765B843 /* FeaturesAtPointExample.swift */; platformFilters = (ios, ); };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -171,6 +174,7 @@
083C452C7342E73F6F16A83C /* OfflineManagerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineManagerExample.swift; sourceTree = "<group>"; };
0A5C0A3C44715B96D646ACB7 /* PitchAndDistanceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PitchAndDistanceExample.swift; sourceTree = "<group>"; };
0AD7661708AEADD25AB726B4 /* InstrumentClusterCarPlaySceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstrumentClusterCarPlaySceneDelegate.swift; sourceTree = "<group>"; };
0FBAFDF929532EDB964786F4 /* StandardInteractiveFeaturesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardInteractiveFeaturesExample.swift; sourceTree = "<group>"; };
10C7CEE3F343DF482D428211 /* ExampleProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleProtocol.swift; sourceTree = "<group>"; };
1229327C13654C370B5641FC /* OfflineRegionManagerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineRegionManagerExample.swift; sourceTree = "<group>"; };
12A0818B5BC601707E3235A9 /* ResizableImageExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizableImageExample.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -230,6 +234,7 @@
6BB52F9D3A810B1A9CEC832C /* Example.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Example.swift; sourceTree = "<group>"; };
6CD7ADCCB774239AA0090C46 /* RasterParticleExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RasterParticleExample.swift; sourceTree = "<group>"; };
6D5B23007CF347976470A069 /* Examples-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Examples-Bridging-Header.h"; sourceTree = "<group>"; };
6DC3D7884D057238010CB6E4 /* PinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinView.swift; sourceTree = "<group>"; };
6E54D3F5943238258AA0A9BE /* ResizeMapViewExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeMapViewExample.swift; sourceTree = "<group>"; };
70922E748D003176C4A3C60A /* HeatmapLayerGlobeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeatmapLayerGlobeExample.swift; sourceTree = "<group>"; };
7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateGeoJSONLineExample.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -300,6 +305,7 @@
E0C19E67A2E87A3D18B7B511 /* ViewportExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewportExample.swift; sourceTree = "<group>"; };
E612275E3042D0D0AF8B583E /* MapRecorderExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRecorderExample.swift; sourceTree = "<group>"; };
E6E875420B5C674C8CCAB9B1 /* MapSettingsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSettingsExample.swift; sourceTree = "<group>"; };
E97E057D9FFB1DE3E16F1F0B /* StandardInteractiveBuildingsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardInteractiveBuildingsExample.swift; sourceTree = "<group>"; };
EC1155178B21E2E8075454A8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
F000C4D3B6FC70FA9607E3A3 /* UIColor+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Random.swift"; sourceTree = "<group>"; };
F033C8EFB89A90D6705B047D /* GeoJSONSourceExample.geojson */ = {isa = PBXFileReference; path = GeoJSONSourceExample.geojson; sourceTree = "<group>"; };
Expand Down Expand Up @@ -387,6 +393,8 @@
45B39AE24486FED5ED30392D /* LocationOverrideExample.swift */,
6CD7ADCCB774239AA0090C46 /* RasterParticleExample.swift */,
AC5A8729C9AEA4711B56B5F0 /* SnapshotMapExample.swift */,
E97E057D9FFB1DE3E16F1F0B /* StandardInteractiveBuildingsExample.swift */,
0FBAFDF929532EDB964786F4 /* StandardInteractiveFeaturesExample.swift */,
640198169EEDFC7CBEFCFCCF /* StandardStyleImportExample.swift */,
DD6F1212BB2453DBFECE12F2 /* StandardStyleLocationsExample.swift */,
90CEF3209781923E53974F20 /* SwiftUIRoot.swift */,
Expand All @@ -404,6 +412,7 @@
858990E6795D3162A941E82C /* ButtonStyle.swift */,
CBD8F852552CA316EFFCDD64 /* CalloutView.swift */,
75D03F5A3A0E879717BFE421 /* Constants.swift */,
6DC3D7884D057238010CB6E4 /* PinView.swift */,
26964B05A794CC808C594AB5 /* SwiftExtensions.swift */,
83E9078B1B13E54C2FFC5FFC /* View+OnShake.swift */,
F393DF039D7AD2F35C8DE4CE /* ViewExtensions.swift */,
Expand Down Expand Up @@ -874,6 +883,7 @@
902FD51EC410A1E8BD88941D /* NavigationSimulatorExample.swift in Sources */,
E6B722A64C15CE701287B464 /* OfflineManagerExample.swift in Sources */,
5FF3E34B523C39A404154BF7 /* OfflineRegionManagerExample.swift in Sources */,
312CE7CED726F0A572301622 /* PinView.swift in Sources */,
A6389C28B8AAC39878591AD0 /* PitchAndDistanceExample.swift in Sources */,
F48BF087BB56B0A44D8B16F3 /* PointAnnotationClusteringExample.swift in Sources */,
4417BB8A356335BC8421A19B /* PointClusteringExample.swift in Sources */,
Expand All @@ -892,6 +902,8 @@
F2B385831A78B3EE16BFEA69 /* SnapshotterCoreGraphicsExample.swift in Sources */,
68FD9E1F4606B2729BA1E6DC /* SnapshotterExample.swift in Sources */,
442DB919BE75CE7B0A537757 /* SpinningGlobeExample.swift in Sources */,
18919387995A155D6F64DADF /* StandardInteractiveBuildingsExample.swift in Sources */,
FA53EEA88DB29D4D5AC58514 /* StandardInteractiveFeaturesExample.swift in Sources */,
1372F3B8047B6B4EE70933D9 /* StandardStyleExample.swift in Sources */,
2997D21A7DB20098C6D03D3B /* StandardStyleImportExample.swift in Sources */,
8F0BEA796867B64E48A1B328 /* StandardStyleLocationsExample.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import SwiftUI
@_spi(Experimental) import MapboxMaps

@available(iOS 14, *)
struct StandardInteractiveBuildingsExample: View {
@State var selectedBuildings = [StandardBuildingsFeature]()
@State var lightPreset = StandardLightPreset.day
@State var theme = StandardTheme.default
@State var buildingSelectColor = StyleColor("hsl(214, 94%, 59%)") // default color

var body: some View {
let cameraCenter = CLLocationCoordinate2D(latitude: 60.1718, longitude: 24.9453)
Map(initialViewport: .camera(center: cameraCenter, zoom: 16.35, bearing: 49.92, pitch: 40)) {
/// Each selected building is colored using the `selected` state.
ForEvery(selectedBuildings, id: \.id) { building in
FeatureState(building, .init(select: true))
}

/// When the user taps the building, it is added to the list of selected buildings.
TapInteraction(.standardBuildings) { building, _ in
self.selectedBuildings.append(building)
return true
}

/// Tapping anywhere away from a 3D building will deselect previously selected buildings.
TapInteraction { _ in
selectedBuildings.removeAll()
return true
}
}
/// DON'T USE Standard Experimental style in production, it will break over time.
/// Currently this feature is in preview.
.mapStyle(.standardExperimental(theme: theme, lightPreset: lightPreset, buildingSelectColor: buildingSelectColor))
.ignoresSafeArea()
/// Debug panel
.safeOverlay(alignment: .bottom) {
VStack {
HStack {
Text("Building Select")
Picker("Building Select", selection: $buildingSelectColor) {
Text("Default").tag(StyleColor("hsl(214, 94%, 59%)"))
Text("Yellow").tag(StyleColor("yellow"))
Text("Red").tag(StyleColor(.red))
}.pickerStyle(.segmented)
}
HStack {
Text("Light")
Picker("Light", selection: $lightPreset) {
Text("Dawn").tag(StandardLightPreset.dawn)
Text("Day").tag(StandardLightPreset.day)
Text("Dusk").tag(StandardLightPreset.dusk)
Text("Night").tag(StandardLightPreset.night)
}.pickerStyle(.segmented)
}
HStack {
Text("Theme")
Picker("Theme", selection: $theme) {
Text("Default").tag(StandardTheme.default)
Text("Faded").tag(StandardTheme.faded)
Text("Monochrome").tag(StandardTheme.monochrome)
}.pickerStyle(.segmented)
}
}
.floating()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import SwiftUI
@_spi(Experimental) import MapboxMaps

@available(iOS 14, *)
struct StandardInteractiveFeaturesExample: View {
@State var selectedPoi: StandardPoiFeature?
@State var selectedBuildings = [StandardBuildingsFeature]()
@State var selectedPlace: StandardPlaceLabelsFeature?
@State var lightPreset = StandardLightPreset.day
@State var theme = StandardTheme.default
@State var buildingSelectColor = StyleColor("hsl(214, 94%, 59%)") // default color

private struct PropView: View {
var key: String
var prop: String?
var body: some View {
if let prop {
Text("\(key): \(prop)")
}
}
}

var body: some View {
let cameraCenter = CLLocationCoordinate2D(latitude: 60.1718, longitude: 24.9453)
MapReader { proxy in
Map(initialViewport: .camera(center: cameraCenter, zoom: 16.35, bearing: 49.92, pitch: 40)) {
if let selectedPoi {
/// When there is a currently selected poi: (1) display the PinView as a ViewAnnotation.
MapViewAnnotation(coordinate: selectedPoi.coordinate) {
PinView(text: selectedPoi.name ?? "", type: selectedPoi.class)
.id(selectedPoi.id)
}
.allowZElevate(true)
.variableAnchors([
ViewAnnotationAnchorConfig(anchor: .top, offsetX: 0, offsetY: 50)
])

/// And (2): Hide the selected POI via feature state.
FeatureState(selectedPoi, .init(hide: true))
}

/// Each selected building is colored using the `selected` state.
ForEvery(selectedBuildings, id: \.id) { building in
FeatureState(building, .init(select: true))
}

if let selectedPlace {
FeatureState(selectedPlace, .init(select: true))
}

/// When the POI featureset is tapped, set that feature as selected.
TapInteraction(.standardPoi) { poi, _ in
selectedPoi = poi

/// Query all buildings in the viewport that are below the selected POI coordinate (distance between poi and building footprint <= 0).
proxy.map?.queryRenderedFeatures(
featureset: .standardBuildings,
filter: Exp(.lte) {
Exp(.distance) { poi.geometry.geoJSONObject }
0
}) { result in
switch result {
case .success(let features):
// TODO: MAPSIOS-1596 Fix crash in ForEvery for buildings with the same id.
self.selectedBuildings = features
case .failure(let failure):
print("error: \(failure)")
}
}
return true
}

TapInteraction(.standardPlaceLabels) { placeLabel, _ in
selectedPlace = placeLabel
return true
}

/// When the map is tapped, besides the feature, reset the selection.
TapInteraction { _ in
selectedBuildings = []
selectedPoi = nil
selectedPlace = nil

return true
}
}
/// DON'T USE Standard Experimental style in production, it will break over time.
/// Currently this feature is in preview.
.mapStyle(.standardExperimental(theme: theme, lightPreset: lightPreset, buildingSelectColor: buildingSelectColor))
}
.ignoresSafeArea()
/// Debug panel
.safeOverlay(alignment: .bottom) {
VStack(alignment: .leading) {
if let selectedPoi {
VStack(alignment: .leading) {
PropView(key: "name", prop: selectedPoi.name)
PropView(key: "group", prop: selectedPoi.group)
PropView(key: "class", prop: selectedPoi.class)
PropView(key: "maki", prop: selectedPoi.maki)
PropView(key: "transitMode", prop: selectedPoi.transitMode)
PropView(key: "transitStopType", prop: selectedPoi.transitStopType)
PropView(key: "transitNetwork", prop: selectedPoi.transitNetwork)
PropView(key: "airportRef", prop: selectedPoi.airportRef)
}
.floating()
.font(.safeMonospaced)
}
if let selectedPlace {
VStack(alignment: .leading) {
PropView(key: "name", prop: selectedPlace.name)
PropView(key: "class", prop: selectedPlace.class)
}
.floating()
.font(.safeMonospaced)
}
VStack {
HStack {
Text("Building Select")
Picker("Building Select", selection: $buildingSelectColor) {
Text("Default").tag(StyleColor("hsl(214, 94%, 59%)"))
Text("Yellow").tag(StyleColor("yellow"))
Text("Red").tag(StyleColor(.red))
}.pickerStyle(.segmented)
}
HStack {
Text("Light")
Picker("Light", selection: $lightPreset) {
Text("Dawn").tag(StandardLightPreset.dawn)
Text("Day").tag(StandardLightPreset.day)
Text("Dusk").tag(StandardLightPreset.dusk)
Text("Night").tag(StandardLightPreset.night)
}.pickerStyle(.segmented)
}
HStack {
Text("Theme")
Picker("Theme", selection: $theme) {
Text("Default").tag(StandardTheme.default)
Text("Faded").tag(StandardTheme.faded)
Text("Monochrome").tag(StandardTheme.monochrome)
}.pickerStyle(.segmented)
}
}
.floating()
}
}
}
}
Loading

0 comments on commit ffb3105

Please sign in to comment.