Skip to content

Commit 5642a79

Browse files
pjleonard37github-actions[bot]
authored andcommitted
Fix Display the User's Location Example Menu [MAPSIOS-2026] (#7997)
Specifically, this PR updates the Display the User's Location example menu location as it was blocked in the recent redesign. More broadly, this PR adds a new shared `SettingsSheet` Swift UI component system, which will eventually be used by all examples as part of our new redesign. GitOrigin-RevId: 624e743409d92344b3cf1b3fa0fb3224cc088eb8
1 parent 7ce9fdb commit 5642a79

File tree

3 files changed

+203
-95
lines changed

3 files changed

+203
-95
lines changed

Examples.xcodeproj/project.pbxproj

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
classes = {
55
};
66
objectVersion = 63;
7-
objectVersion = 54;
8-
objectVersion = 77;
97
objects = {
108

119
/* Begin PBXBuildFile section */
@@ -38,8 +36,7 @@
3836
2997D21A7DB20098C6D03D3B /* StandardStyleImportExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640198169EEDFC7CBEFCFCCF /* StandardStyleImportExample.swift */; };
3937
2B44F3E8EF3A50D9AE6B825F /* route.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 450C8D5E4B84428FE51BCA97 /* route.geojson */; };
4038
2C03342240D5487880316518 /* AddOneMarkerSymbolExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCBE814694CF08A9C2E4A42 /* AddOneMarkerSymbolExample.swift */; platformFilter = ios; };
41-
2C03342240D5487880316518 /* AddOneMarkerSymbolExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCBE814694CF08A9C2E4A42 /* AddOneMarkerSymbolExample.swift */; platformFilters = (ios, ); };
42-
2D3DF43CEE3D4C2B0B012E0E /* IndoorExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1109CA9B9EDD4DEB302501A /* IndoorExample.swift */; platformFilters = (ios, ); };
39+
2D3DF43CEE3D4C2B0B012E0E /* IndoorExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1109CA9B9EDD4DEB302501A /* IndoorExample.swift */; platformFilter = ios; };
4340
2EC3AAE5D54ACFB90E98CD43 /* ElevatedLineMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BBD2991186A6B98F730454 /* ElevatedLineMapView.swift */; };
4441
30589E5AB307FC934E466332 /* radar2.gif in Resources */ = {isa = PBXBuildFile; fileRef = D8730F8FB259A4F889609108 /* radar2.gif */; };
4542
312CE7CED726F0A572301622 /* PinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC3D7884D057238010CB6E4 /* PinView.swift */; };
@@ -95,6 +92,7 @@
9592
7036A19FCD2CCE85BDDF4E00 /* TrackingModeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F5E598A16FA446F583344CB /* TrackingModeExample.swift */; platformFilter = ios; };
9693
732DD7412EA8308000CCDD65 /* AppearancesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 732DD7402EA8308000CCDD65 /* AppearancesExample.swift */; };
9794
7365170E39A459EB4DFA198B /* ExamplesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE18E37A8652B4807D2459F1 /* ExamplesUITests.swift */; platformFilter = ios; };
95+
73C81A0F2EC3D14D00387773 /* SettingsSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73C81A0E2EC3D14D00387773 /* SettingsSheet.swift */; };
9896
754B87ED0F8761A02C8C09C2 /* StyleOverridesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF257763AFD6E4964B28C9EB /* StyleOverridesModel.swift */; platformFilter = ios; };
9997
759D42AA5FE93B6FA9DFADF5 /* LocationOverrideExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45B39AE24486FED5ED30392D /* LocationOverrideExample.swift */; };
10098
7686448F8648BECC75A912B6 /* DashboardCarPlaySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 913B4773A82AD6357D6AAEA1 /* DashboardCarPlaySceneDelegate.swift */; platformFilter = ios; };
@@ -300,6 +298,7 @@
300298
70922E748D003176C4A3C60A /* HeatmapLayerGlobeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeatmapLayerGlobeExample.swift; sourceTree = "<group>"; };
301299
7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateGeoJSONLineExample.swift; sourceTree = "<group>"; };
302300
732DD7402EA8308000CCDD65 /* AppearancesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancesExample.swift; sourceTree = "<group>"; };
301+
73C81A0E2EC3D14D00387773 /* SettingsSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SettingsSheet.swift; path = Sources/Examples/Controllers/SettingsSheet.swift; sourceTree = "<group>"; };
303302
73E23CA02EB25A510035A052 /* AppearancesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppearancesExample.swift; path = "Sources/Examples/SwiftUI Examples/AppearancesExample.swift"; sourceTree = "<group>"; };
304303
75D03F5A3A0E879717BFE421 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
305304
75F4874FE9742DF10F0D0483 /* ColorScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorScheme.swift; sourceTree = "<group>"; };
@@ -721,6 +720,7 @@
721720
AFDB1EA82615CFDF02CE1D4D = {
722721
isa = PBXGroup;
723722
children = (
723+
73C81A0E2EC3D14D00387773 /* SettingsSheet.swift */,
724724
73E23CA02EB25A510035A052 /* AppearancesExample.swift */,
725725
52909911727F239150D4FE30 /* Examples */,
726726
DA93D15473B052576C7D2965 /* ExamplesTests */,
@@ -886,8 +886,6 @@
886886
AC4B0DE570C38B503D03669A /* XCRemoteSwiftPackageReference "Fingertips" */,
887887
4F0A03F138FCA51E80A1893D /* XCLocalSwiftPackageReference "." */,
888888
);
889-
preferredProjectObjectVersion = 54;
890-
preferredProjectObjectVersion = 77;
891889
projectDirPath = "";
892890
projectRoot = "";
893891
targets = (
@@ -1104,6 +1102,7 @@
11041102
4791CACAC0846107E4B0955B /* SceneKitExample.swift in Sources */,
11051103
345AF31082BC5A7A64CB8622 /* SearchView.swift in Sources */,
11061104
1FCA58860B3F23CAC57B32CA /* SegmentedToggleView.swift in Sources */,
1105+
73C81A0F2EC3D14D00387773 /* SettingsSheet.swift in Sources */,
11071106
F613749DCDDDDC6F041032A0 /* SimpleMapExample.swift in Sources */,
11081107
1F860D5B445E75772C4C3B6C /* SkyLayerExample.swift in Sources */,
11091108
79843B780E7C5DC68433B745 /* SnapshotMapExample.swift in Sources */,
Lines changed: 80 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
import UIKit
2+
import SwiftUI
23
import MapboxMaps
34

45
final class TrackingModeExample: UIViewController, ExampleProtocol {
5-
private var cancelables = Set<AnyCancelable>()
66
private var locationTrackingCancellation: AnyCancelable?
77

88
private var mapView: MapView!
9-
private lazy var toggleBearingImageButton = UIButton(frame: .zero)
10-
private lazy var trackingButton = UIButton(frame: .zero)
11-
private lazy var styleToggle = UISegmentedControl(items: Style.allCases.map(\.name))
129
private var style: Style = .standard {
1310
didSet {
1411
mapView.mapboxMap.styleURI = style.uri
1512
}
1613
}
1714
private var showsBearingImage: Bool = false {
1815
didSet {
19-
syncPuckAndButton()
16+
let configuration = Puck2DConfiguration.makeDefault(showBearing: showsBearingImage)
17+
mapView.location.options.puckType = .puck2D(configuration)
2018
}
2119
}
2220

@@ -31,11 +29,7 @@ final class TrackingModeExample: UIViewController, ExampleProtocol {
3129
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
3230
view.addSubview(mapView)
3331

34-
addStyleToggle()
35-
36-
// Setup and create button for toggling show bearing image
37-
setupToggleShowBearingImageButton()
38-
setupLocationButton()
32+
setupSettingsButton()
3933

4034
// Add user position icon to the map with location indicator layer
4135
mapView.location.options.puckType = .puck2D()
@@ -53,74 +47,72 @@ final class TrackingModeExample: UIViewController, ExampleProtocol {
5347
finish()
5448
}
5549

56-
@objc func showHideBearingImage() {
57-
showsBearingImage.toggle()
58-
}
59-
60-
func syncPuckAndButton() {
61-
// Update puck config
62-
let configuration = Puck2DConfiguration.makeDefault(showBearing: showsBearingImage)
50+
private func setupSettingsButton() {
51+
let buttonView = SettingsButtonView(onTap: openSettings)
52+
let hostingController = UIHostingController(rootView: buttonView)
53+
hostingController.view.backgroundColor = .clear
54+
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
6355

64-
mapView.location.options.puckType = .puck2D(configuration)
65-
66-
// Update button title
67-
let title: String = showsBearingImage ? "Hide bearing image" : "Show bearing image"
68-
toggleBearingImageButton.setTitle(title, for: .normal)
69-
}
70-
71-
private func setupToggleShowBearingImageButton() {
72-
// Styling
73-
toggleBearingImageButton.backgroundColor = .systemBlue
74-
toggleBearingImageButton.addTarget(self, action: #selector(showHideBearingImage), for: .touchUpInside)
75-
toggleBearingImageButton.setTitleColor(.white, for: .normal)
76-
toggleBearingImageButton.layer.cornerRadius = 4
77-
toggleBearingImageButton.clipsToBounds = true
78-
toggleBearingImageButton.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
79-
80-
toggleBearingImageButton.translatesAutoresizingMaskIntoConstraints = false
81-
view.addSubview(toggleBearingImageButton)
82-
83-
// Constraints
84-
toggleBearingImageButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0).isActive = true
85-
toggleBearingImageButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0).isActive = true
86-
toggleBearingImageButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -70.0).isActive = true
87-
88-
syncPuckAndButton()
89-
}
90-
91-
private func setupLocationButton() {
92-
trackingButton.addTarget(self, action: #selector(switchTracking), for: .touchUpInside)
93-
94-
trackingButton.setImage(UIImage(systemName: "location.fill"), for: .normal)
95-
96-
let buttonWidth = 44.0
97-
trackingButton.translatesAutoresizingMaskIntoConstraints = false
98-
trackingButton.backgroundColor = UIColor(white: 0.97, alpha: 1)
99-
trackingButton.layer.cornerRadius = buttonWidth/2
100-
trackingButton.layer.shadowOffset = CGSize(width: -1, height: 1)
101-
trackingButton.layer.shadowColor = UIColor.black.cgColor
102-
trackingButton.layer.shadowOpacity = 0.5
103-
view.addSubview(trackingButton)
56+
addChild(hostingController)
57+
view.addSubview(hostingController.view)
58+
hostingController.didMove(toParent: self)
10459

10560
NSLayoutConstraint.activate([
106-
trackingButton.trailingAnchor.constraint(equalTo: toggleBearingImageButton.trailingAnchor),
107-
trackingButton.bottomAnchor.constraint(equalTo: toggleBearingImageButton.topAnchor, constant: -20),
108-
trackingButton.widthAnchor.constraint(equalTo: trackingButton.heightAnchor),
109-
trackingButton.widthAnchor.constraint(equalToConstant: buttonWidth)
61+
hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
62+
hostingController.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -120)
11063
])
11164
}
11265

113-
@objc func switchStyle(sender: UISegmentedControl) {
114-
style = Style(rawValue: sender.selectedSegmentIndex) ?? . satelliteStreets
115-
}
116-
117-
@objc func switchTracking() {
118-
let isTrackingNow = locationTrackingCancellation != nil
119-
if isTrackingNow {
120-
stopTracking()
121-
} else {
122-
startTracking()
66+
private func openSettings() {
67+
let controlsSection = SettingsSection(
68+
title: "Controls",
69+
controls: [
70+
.toggle(
71+
title: "Track user location",
72+
isOn: Binding(
73+
get: { [weak self] in self?.locationTrackingCancellation != nil },
74+
set: { [weak self] in $0 ? self?.startTracking() : self?.stopTracking() }
75+
)
76+
),
77+
.toggle(
78+
title: "Show bearing image",
79+
isOn: Binding(
80+
get: { [weak self] in self?.showsBearingImage ?? false },
81+
set: { [weak self] in self?.showsBearingImage = $0 }
82+
)
83+
),
84+
.segmentedPicker(
85+
title: "Map style",
86+
options: Style.allCases.map(\.name),
87+
selection: Binding(
88+
get: { [weak self] in self?.style.rawValue ?? 0 },
89+
set: { [weak self] newValue in
90+
self?.style = Style(rawValue: newValue) ?? .standard
91+
}
92+
)
93+
)
94+
]
95+
)
96+
97+
let docsSection = SettingsSection(
98+
title: "Docs",
99+
controls: [
100+
.link(
101+
title: "Tracking mode example",
102+
url: URL(string: "https://docs.mapbox.com/ios/maps/examples/tracking-mode/")!
103+
)
104+
]
105+
)
106+
107+
let settingsView = SettingsSheet(sections: [controlsSection, docsSection])
108+
let hostingController = UIHostingController(rootView: settingsView)
109+
110+
if let sheet = hostingController.sheetPresentationController {
111+
sheet.detents = [.medium()]
112+
sheet.prefersGrabberVisible = true
123113
}
114+
115+
present(hostingController, animated: true)
124116
}
125117

126118
private func startTracking() {
@@ -130,24 +122,11 @@ final class TrackingModeExample: UIViewController, ExampleProtocol {
130122
to: CameraOptions(center: location.coordinate, zoom: 15),
131123
duration: 1.3)
132124
}
133-
134-
trackingButton.setImage(UIImage(systemName: "location.fill"), for: .normal)
135125
}
136126

137-
func stopTracking() {
138-
trackingButton.setImage(UIImage(systemName: "location"), for: .normal)
127+
private func stopTracking() {
139128
locationTrackingCancellation = nil
140129
}
141-
142-
func addStyleToggle() {
143-
// Create a UISegmentedControl to toggle between map styles
144-
styleToggle.selectedSegmentIndex = style.rawValue
145-
styleToggle.addTarget(self, action: #selector(switchStyle(sender:)), for: .valueChanged)
146-
styleToggle.translatesAutoresizingMaskIntoConstraints = false
147-
148-
// set the segmented control as the title view
149-
navigationItem.titleView = styleToggle
150-
}
151130
}
152131

153132
extension TrackingModeExample: GestureManagerDelegate {
@@ -162,6 +141,11 @@ extension TrackingModeExample: GestureManagerDelegate {
162141

163142
extension TrackingModeExample {
164143
private enum Style: Int, CaseIterable {
144+
case standard
145+
case light
146+
case satelliteStreets
147+
case customUri
148+
165149
var name: String {
166150
switch self {
167151
case .standard:
@@ -188,10 +172,17 @@ extension TrackingModeExample {
188172
return .init(url: localStyleURL)!
189173
}
190174
}
175+
}
176+
}
191177

192-
case standard
193-
case light
194-
case satelliteStreets
195-
case customUri
178+
// MARK: - SwiftUI Settings Button
179+
private struct SettingsButtonView: View {
180+
let onTap: () -> Void
181+
182+
var body: some View {
183+
Button(action: onTap) {
184+
Image(systemName: "slider.horizontal.3")
185+
}
186+
.buttonStyle(MapFloatingButtonStyle())
196187
}
197188
}

0 commit comments

Comments
 (0)