From 7e080cb2c6ee533922b515e379443fbc15dbb700 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 23 Apr 2023 22:38:02 -0700 Subject: [PATCH] Remove tracking mode setting, add button to mesh map --- Meshtastic.xcodeproj/project.pbxproj | 4 ++ Meshtastic/Enums/AppSettingsEnums.swift | 7 ++ Meshtastic/Helpers/Extensions.swift | 1 - Meshtastic/Model/UserSettings.swift | 7 -- Meshtastic/Views/Map/Custom/MapButtons.swift | 66 +++++++++++++++++++ .../Views/Map/Custom/MapViewSwiftUI.swift | 59 +++++++---------- Meshtastic/Views/Nodes/NodeMap.swift | 20 ++++-- Meshtastic/Views/Settings/AppSettings.swift | 18 +---- 8 files changed, 121 insertions(+), 61 deletions(-) create mode 100644 Meshtastic/Views/Map/Custom/MapButtons.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 7acbca17..c213f398 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -117,6 +117,7 @@ DDD6EEAF29BC024700383354 /* Firmware.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD6EEAE29BC024700383354 /* Firmware.swift */; }; DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */; }; DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */; }; + DDDB443629F6287000EE2349 /* MapButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB443529F6287000EE2349 /* MapButtons.swift */; }; DDDE59F529AF163D00490C6C /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41A61C29AE7E8E003C5A37 /* WidgetKit.framework */; }; DDDE59F629AF163D00490C6C /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41A61E29AE7E8F003C5A37 /* SwiftUI.framework */; }; DDDE59F929AF163D00490C6C /* WidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDE59F829AF163D00490C6C /* WidgetsBundle.swift */; }; @@ -297,6 +298,7 @@ DDD6EEAE29BC024700383354 /* Firmware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Firmware.swift; sourceTree = ""; }; DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = ""; }; DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = ""; }; + DDDB443529F6287000EE2349 /* MapButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapButtons.swift; sourceTree = ""; }; DDDD527729B5B83F0045BC3C /* MeshtasticDataModelV9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV9.xcdatamodel; sourceTree = ""; }; DDDE59F429AF163D00490C6C /* WidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; DDDE59F829AF163D00490C6C /* WidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetsBundle.swift; sourceTree = ""; }; @@ -365,6 +367,7 @@ C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */, DD964FC32974767D007C176F /* MapViewFitExtension.swift */, DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */, + DDDB443529F6287000EE2349 /* MapButtons.swift */, ); path = Custom; sourceTree = ""; @@ -904,6 +907,7 @@ DD5E523F298F5A9E00D21B61 /* AirQualityIndexCompact.swift in Sources */, DD964FBF296E76EF007C176F /* WaypointFormView.swift in Sources */, DD3501892852FC3B000FC853 /* Settings.swift in Sources */, + DDDB443629F6287000EE2349 /* MapButtons.swift in Sources */, DD5D0A9C2931B9F200F7EA61 /* EthernetModes.swift in Sources */, DD5E5203298EE33B00D21B61 /* config.pb.swift in Sources */, DD798B072915928D005217CD /* ChannelMessageList.swift in Sources */, diff --git a/Meshtastic/Enums/AppSettingsEnums.swift b/Meshtastic/Enums/AppSettingsEnums.swift index 35d75707..606d803d 100644 --- a/Meshtastic/Enums/AppSettingsEnums.swift +++ b/Meshtastic/Enums/AppSettingsEnums.swift @@ -97,6 +97,13 @@ enum UserTrackingModes: Int, CaseIterable, Identifiable { return NSLocalizedString("map.usertrackingmode.followwithheading", comment: "Follow with Heading") } } + var icon: String { + switch self { + case .none: return "location" + case .follow: return "location.fill" + case .followWithHeading: return "location.north.line.fill" + } + } func MKUserTrackingModeValue() -> MKUserTrackingMode { switch self { diff --git a/Meshtastic/Helpers/Extensions.swift b/Meshtastic/Helpers/Extensions.swift index 2e5d7794..4fb36497 100644 --- a/Meshtastic/Helpers/Extensions.swift +++ b/Meshtastic/Helpers/Extensions.swift @@ -193,7 +193,6 @@ extension UserDefaults { case meshMapCenteringMode case meshMapRecentering case meshMapCustomTileServer - case meshMapUserTrackingMode case meshMapShowNodeHistory case meshMapShowRouteLines } diff --git a/Meshtastic/Model/UserSettings.swift b/Meshtastic/Model/UserSettings.swift index 1c44c965..be44007d 100644 --- a/Meshtastic/Model/UserSettings.swift +++ b/Meshtastic/Model/UserSettings.swift @@ -61,12 +61,6 @@ class UserSettings: ObservableObject { UserDefaults.standard.synchronize() } } - @Published var meshMapUserTrackingMode: Int { - didSet { - UserDefaults.standard.set(meshMapUserTrackingMode, forKey: "meshMapUserTrackingMode") - UserDefaults.standard.synchronize() - } - } @Published var meshMapShowNodeHistory: Bool { didSet { UserDefaults.standard.set(meshMapShowNodeHistory, forKey: "meshMapShowNodeHistory") @@ -91,7 +85,6 @@ class UserSettings: ObservableObject { self.meshMapCenteringMode = UserDefaults.standard.object(forKey: "meshMapCenteringMode") as? Int ?? 0 self.meshMapRecentering = UserDefaults.standard.object(forKey: "meshMapRecentering") as? Bool ?? false self.meshMapCustomTileServer = UserDefaults.standard.string(forKey: "meshMapCustomTileServer") ?? "" - self.meshMapUserTrackingMode = UserDefaults.standard.object(forKey: "meshMapUserTrackingMode") as? Int ?? 0 self.meshMapShowNodeHistory = UserDefaults.standard.object(forKey: "meshMapShowNodeHistory") as? Bool ?? true self.meshMapShowRouteLines = UserDefaults.standard.object(forKey: "meshMapShowRouteLines") as? Bool ?? false } diff --git a/Meshtastic/Views/Map/Custom/MapButtons.swift b/Meshtastic/Views/Map/Custom/MapButtons.swift new file mode 100644 index 00000000..913cfb71 --- /dev/null +++ b/Meshtastic/Views/Map/Custom/MapButtons.swift @@ -0,0 +1,66 @@ +// +// MapButtons.swift +// Meshtastic +// +// Copyright © Garth Vander Houwen 4/23/23. +// + +import SwiftUI + +struct MapButtons: View { + let buttonWidth: CGFloat = 22 + let width: CGFloat = 45 + @Binding var tracking: UserTrackingModes + @Binding var isPresentingInfoSheet: Bool + + var body: some View { + VStack() { + let impactLight = UIImpactFeedbackGenerator(style: .light) + Button(action: { + self.isPresentingInfoSheet.toggle() + }) { + Image(systemName: isPresentingInfoSheet ? "info.circle.fill" : "info.circle") + .resizable() + .frame(width: buttonWidth, height: buttonWidth, alignment: .center) + .offset(y: -2) + } + Divider() + Button(action: { + switch self.tracking { + case .none: + self.tracking = .follow + case .follow: + self.tracking = .followWithHeading + case .followWithHeading: + self.tracking = .none + } + impactLight.impactOccurred() + }) { + Image(systemName: tracking.icon) + .frame(width: buttonWidth, height: buttonWidth, alignment: .center) + .offset(y: 3) + } + } + .frame(width: width, height: width*2, alignment: .center) + .background(Color(UIColor.systemBackground)) + .cornerRadius(8) + .shadow(radius: 1) + .offset(y: 25) + } +} + +// MARK: Previews +struct MapControl_Previews: PreviewProvider { + @State static var tracking: UserTrackingModes = .none + @State static var isPresentingInfoSheet = false + static var previews: some View { + Group { + MapButtons(tracking: $tracking, isPresentingInfoSheet: $isPresentingInfoSheet) + .environment(\.colorScheme, .light) + MapButtons(tracking: $tracking, isPresentingInfoSheet: $isPresentingInfoSheet) + .environment(\.colorScheme, .dark) + } + + .previewLayout(.fixed(width: 60, height: 100)) + } +} diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 28fadd95..708d82da 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -29,7 +29,6 @@ struct MapViewSwiftUI: UIViewRepresentable { @State private var loadedLastUpdatedLocalMapFile = 0 var customMapOverlay: CustomMapOverlay? @State private var presentCustomMapOverlayHash: CustomMapOverlay? - // Custom Tile Server @AppStorage("meshMapCustomTileServer") private var tileServerUrl = "" var tileRenderer: MKTileOverlayRenderer? @@ -70,10 +69,6 @@ struct MapViewSwiftUI: UIViewRepresentable { mapView.showsScale = true mapView.showsTraffic = true - let overlay = TileServerOverlay() // Offline-Map-Tiles - overlay.canReplaceMapContent = true - mapView.addOverlay(overlay, level: .aboveLabels) - #if targetEnvironment(macCatalyst) // Show the default always visible compass and the mac only controls mapView.showsCompass = true @@ -99,10 +94,29 @@ struct MapViewSwiftUI: UIViewRepresentable { func updateUIView(_ mapView: MKMapView, context: Context) { - mapView.mapType = mapViewType - - if self.customMapOverlay != self.presentCustomMapOverlayHash || self.loadedLastUpdatedLocalMapFile != self.lastUpdatedLocalMapFile { + if tileServerUrl.count > 0 { + tileRenderer?.alpha = 0.0 + let overlays = mapView.overlays + if mapView.mapType == .standard { + let overlay = MKTileOverlay(urlTemplate: tileServerUrl) + if overlays.contains(where: {$0 is MKPolyline}) { + mapView.addOverlay(overlay, level: .aboveLabels) + if let poly_overlay = overlays.filter({$0 is MKPolyline}).first { + mapView.addOverlay(poly_overlay, level: .aboveLabels) + } + } else { + mapView.addOverlay(overlay, level: .aboveLabels) + + } + } else { + for overlay in overlays { + if let ove = overlay as? MKTileOverlay { + mapView.removeOverlay(ove) + } + } + } + } else if self.customMapOverlay != self.presentCustomMapOverlayHash || self.loadedLastUpdatedLocalMapFile != self.lastUpdatedLocalMapFile { mapView.removeOverlays(mapView.overlays) if self.customMapOverlay != nil { @@ -131,7 +145,7 @@ struct MapViewSwiftUI: UIViewRepresentable { .sorted { $0.nodePosition?.num ?? 0 > $1.nodePosition?.num ?? -1 } let annotationCount = waypoints.count + (showNodeHistory ? positions.count : latest.count) - if annotationCount != mapView.annotations.count { + // if annotationCount != mapView.annotations.count { print("Annotation Count: \(annotationCount) Map Annotations: \(mapView.annotations.count)") mapView.removeAnnotations(mapView.annotations) mapView.addAnnotations(waypoints) @@ -152,7 +166,7 @@ struct MapViewSwiftUI: UIViewRepresentable { }) let polyline = MKPolyline(coordinates: lineCoords, count: nodePositions.count) polyline.title = "\(String(position.nodePosition?.num ?? 0))" - mapView.addOverlay(polyline) + mapView.addOverlay(polyline, level: .aboveLabels) lineIndex += 1 // There are 18 colors for lines, start over if we are at index 17 if lineIndex > 17 { @@ -176,30 +190,7 @@ struct MapViewSwiftUI: UIViewRepresentable { mapView.showsUserLocation = true } mapView.setUserTrackingMode(userTrackingMode, animated: true) - - if tileServerUrl.count > 0 { - tileRenderer?.alpha = 0.0 - let overlays = mapView.overlays - if mapView.mapType == .standard { - let overlay = MKTileOverlay(urlTemplate: tileServerUrl) - if overlays.contains(where: {$0 is MKPolyline}) { - mapView.addOverlay(overlay, level: .aboveLabels) - if let poly_overlay = overlays.filter({$0 is MKPolyline}).first { - mapView.addOverlay(poly_overlay, level: .aboveRoads) - } - } else { - mapView.addOverlay(overlay, level: .aboveRoads) - - } - } else { - for overlay in overlays { - if let ove = overlay as? MKTileOverlay { - mapView.removeOverlay(ove) - } - } - } - } - } + //} } } diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 509c61ef..0098fbf4 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -17,7 +17,6 @@ struct NodeMap: View { @EnvironmentObject var userSettings: UserSettings @AppStorage("meshMapType") private var meshMapType = "hybridFlyover" - @AppStorage("meshMapUserTrackingMode") private var meshMapUserTrackingMode = 0 @AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false @AppStorage("meshMapShowRouteLines") private var meshMapShowRouteLines = false @@ -32,7 +31,9 @@ struct NodeMap: View { private var waypoints: FetchedResults @State private var mapType: MKMapType = .standard - @State private var userTrackingMode: MKUserTrackingMode = .none + @State var selectedTracking: UserTrackingModes = .none + @State var isPresentingInfoSheet: Bool = false + @State var waypointCoordinate: WaypointCoordinate? @State private var customMapOverlay: MapViewSwiftUI.CustomMapOverlay? = MapViewSwiftUI.CustomMapOverlay( mapName: "offlinemap", @@ -56,11 +57,23 @@ struct NodeMap: View { positions: Array(positions), waypoints: Array(waypoints), mapViewType: mapType, - userTrackingMode: userTrackingMode, + userTrackingMode: selectedTracking.MKUserTrackingModeValue(), showNodeHistory: meshMapShowNodeHistory, showRouteLines: meshMapShowRouteLines, customMapOverlay: self.customMapOverlay ) + VStack(alignment: .trailing) { + + HStack(alignment: .top) { + Spacer() + MapButtons(tracking: $selectedTracking, isPresentingInfoSheet: $isPresentingInfoSheet) + .padding(.trailing, 8) + .padding(.top, 16) + } + + Spacer() + + } VStack { Spacer() Picker("Map Type", selection: $mapType) { @@ -94,7 +107,6 @@ struct NodeMap: View { UIApplication.shared.isIdleTimerDisabled = true self.bleManager.context = context self.bleManager.userSettings = userSettings - userTrackingMode = UserTrackingModes(rawValue: meshMapUserTrackingMode)?.MKUserTrackingModeValue() ?? MKUserTrackingMode.none switch meshMapType { case "standard": mapType = .standard diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index ee308564..b6f6d95a 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -82,15 +82,6 @@ struct AppSettings: View { .font(.caption) .foregroundColor(.gray) } - Picker("map.usertrackingmode", selection: $userSettings.meshMapUserTrackingMode) { - ForEach(UserTrackingModes.allCases) { utm in - Text(utm.description) - } - } - .pickerStyle(DefaultPickerStyle()) - Text("When follow or follow with heading are selected maps will automatically center on the location of the GPS on the connected phone.") - .font(.caption) - .foregroundColor(.gray) } Section(header: Text("map options")) { @@ -102,14 +93,11 @@ struct AppSettings: View { } .pickerStyle(DefaultPickerStyle()) - if userSettings.meshMapUserTrackingMode == 0 { + Toggle(isOn: $userSettings.meshMapRecentering) { - Toggle(isOn: $userSettings.meshMapRecentering) { - - Label("map.recentering", systemImage: "camera.metering.center.weighted") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Label("map.recentering", systemImage: "camera.metering.center.weighted") } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) Toggle(isOn: $userSettings.meshMapShowNodeHistory) { Label("Show Node History", systemImage: "building.columns.fill")