From c09011f86bdb83ffc34024ce7508622c24f8969d Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 28 Feb 2023 12:25:44 -0800 Subject: [PATCH] Clean up map settings and recentering code --- Meshtastic.xcodeproj/project.pbxproj | 139 +++++++++++++++++- Meshtastic/Model/UserSettings.swift | 2 +- .../Views/Map/Custom/MapViewSwiftUI.swift | 55 +++---- Meshtastic/Views/Settings/AppSettings.swift | 37 +++-- 4 files changed, 183 insertions(+), 50 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index d6d5bc01..1048db4f 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -32,6 +32,8 @@ DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD415827285859C4009B0E59 /* TelemetryConfig.swift */; }; DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582928585C32009B0E59 /* RangeTestConfig.swift */; }; DD41A61529AB0035003C5A37 /* NodeWeatherForecast.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41A61429AB0035003C5A37 /* NodeWeatherForecast.swift */; }; + DD41A61D29AE7E8F003C5A37 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41A61C29AE7E8E003C5A37 /* WidgetKit.framework */; }; + DD41A61F29AE7E8F003C5A37 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41A61E29AE7E8F003C5A37 /* SwiftUI.framework */; }; DD457188293C7E63000C49FB /* SignalStrengthIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD457187293C7E63000C49FB /* SignalStrengthIndicator.swift */; }; DD47E3CE26F103C600029299 /* NodeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CD26F103C600029299 /* NodeList.swift */; }; DD47E3D626F17ED900029299 /* CircleText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D526F17ED900029299 /* CircleText.swift */; }; @@ -162,6 +164,9 @@ DD415827285859C4009B0E59 /* TelemetryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryConfig.swift; sourceTree = ""; }; DD41582928585C32009B0E59 /* RangeTestConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeTestConfig.swift; sourceTree = ""; }; DD41A61429AB0035003C5A37 /* NodeWeatherForecast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeWeatherForecast.swift; sourceTree = ""; }; + DD41A61B29AE7E8E003C5A37 /* WidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + DD41A61C29AE7E8E003C5A37 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + DD41A61E29AE7E8F003C5A37 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; DD457187293C7E63000C49FB /* SignalStrengthIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalStrengthIndicator.swift; sourceTree = ""; }; DD457BC4295D5E35004BCE4D /* MeshtasticDataModelV5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV5.xcdatamodel; sourceTree = ""; }; DD47E3CD26F103C600029299 /* NodeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeList.swift; sourceTree = ""; }; @@ -264,6 +269,15 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + DD41A61829AE7E8E003C5A37 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DD41A61F29AE7E8F003C5A37 /* SwiftUI.framework in Frameworks */, + DD41A61D29AE7E8F003C5A37 /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; DDC2E15126CE248E0042C5E4 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -311,6 +325,20 @@ path = Custom; sourceTree = ""; }; + DD41A61629AE7DDB003C5A37 /* Widget */ = { + isa = PBXGroup; + children = ( + ); + path = Widget; + sourceTree = ""; + }; + DD41A62029AE7E8F003C5A37 /* Widgets */ = { + isa = PBXGroup; + children = ( + ); + path = Widgets; + sourceTree = ""; + }; DD47E3CA26F0E50300029299 /* Nodes */ = { isa = PBXGroup; children = ( @@ -445,6 +473,8 @@ DD8EDE9226F97A2B00A5A10B /* Frameworks */ = { isa = PBXGroup; children = ( + DD41A61C29AE7E8E003C5A37 /* WidgetKit.framework */, + DD41A61E29AE7E8F003C5A37 /* SwiftUI.framework */, ); name = Frameworks; sourceTree = ""; @@ -463,8 +493,10 @@ DDCDC6CD29481FCC004C1DDA /* Localizable.strings */, DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */, DDC2E15626CE248E0042C5E4 /* Meshtastic */, + DD41A61629AE7DDB003C5A37 /* Widget */, DDC2E16D26CE248F0042C5E4 /* MeshtasticTests */, DDC2E17826CE248F0042C5E4 /* MeshtasticUITests */, + DD41A62029AE7E8F003C5A37 /* Widgets */, DDC2E15526CE248E0042C5E4 /* Products */, DD8EDE9226F97A2B00A5A10B /* Frameworks */, ); @@ -477,6 +509,7 @@ DDC2E15426CE248E0042C5E4 /* Meshtastic.app */, DDC2E16A26CE248F0042C5E4 /* MeshtasticTests.xctest */, DDC2E17526CE248F0042C5E4 /* MeshtasticUITests.xctest */, + DD41A61B29AE7E8E003C5A37 /* WidgetsExtension.appex */, ); name = Products; sourceTree = ""; @@ -484,8 +517,8 @@ DDC2E15626CE248E0042C5E4 /* Meshtastic */ = { isa = PBXGroup; children = ( - DD8ED9C6289CE4A100B3B0AB /* Enums */, DD90860A26F645B700DC5189 /* Meshtastic.entitlements */, + DD8ED9C6289CE4A100B3B0AB /* Enums */, DDC4D5662754996200A4208E /* Persistence */, DDAF8C5626ED07740058C060 /* Protobufs */, DD86D40D2881BDB300BAEB7A /* Export */, @@ -615,6 +648,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + DD41A61A29AE7E8E003C5A37 /* WidgetsExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = DD41A62A29AE7E91003C5A37 /* Build configuration list for PBXNativeTarget "WidgetsExtension" */; + buildPhases = ( + DD41A61729AE7E8E003C5A37 /* Sources */, + DD41A61829AE7E8E003C5A37 /* Frameworks */, + DD41A61929AE7E8E003C5A37 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WidgetsExtension; + productName = WidgetsExtension; + productReference = DD41A61B29AE7E8E003C5A37 /* WidgetsExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; DDC2E15326CE248E0042C5E4 /* Meshtastic */ = { isa = PBXNativeTarget; buildConfigurationList = DDC2E17E26CE248F0042C5E4 /* Build configuration list for PBXNativeTarget "Meshtastic" */; @@ -679,9 +729,12 @@ DDC2E14C26CE248E0042C5E4 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1250; + LastSwiftUpdateCheck = 1420; LastUpgradeCheck = 1250; TargetAttributes = { + DD41A61A29AE7E8E003C5A37 = { + CreatedOnToolsVersion = 14.2; + }; DDC2E15326CE248E0042C5E4 = { CreatedOnToolsVersion = 12.5.1; LastSwiftMigration = 1340; @@ -719,11 +772,19 @@ DDC2E15326CE248E0042C5E4 /* Meshtastic */, DDC2E16926CE248F0042C5E4 /* MeshtasticTests */, DDC2E17426CE248F0042C5E4 /* MeshtasticUITests */, + DD41A61A29AE7E8E003C5A37 /* WidgetsExtension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + DD41A61929AE7E8E003C5A37 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; DDC2E15226CE248E0042C5E4 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -771,6 +832,13 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + DD41A61729AE7E8E003C5A37 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; DDC2E15026CE248E0042C5E4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -926,6 +994,64 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + DD41A62B29AE7E91003C5A37 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GCH7VS5Y9R; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Widgets/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Widgets; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + DD41A62C29AE7E91003C5A37 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GCH7VS5Y9R; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Widgets/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Widgets; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; DDC2E17C26CE248F0042C5E4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1203,6 +1329,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + DD41A62A29AE7E91003C5A37 /* Build configuration list for PBXNativeTarget "WidgetsExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DD41A62B29AE7E91003C5A37 /* Debug */, + DD41A62C29AE7E91003C5A37 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; DDC2E14F26CE248E0042C5E4 /* Build configuration list for PBXProject "Meshtastic" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Meshtastic/Model/UserSettings.swift b/Meshtastic/Model/UserSettings.swift index a8f56efe..026306bc 100644 --- a/Meshtastic/Model/UserSettings.swift +++ b/Meshtastic/Model/UserSettings.swift @@ -79,7 +79,7 @@ class UserSettings: ObservableObject { self.keyboardType = UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0 self.meshMapType = UserDefaults.standard.string(forKey: "meshMapType") ?? "standard" self.meshMapCenteringMode = UserDefaults.standard.object(forKey: "meshMapCenteringMode") as? Int ?? 0 - self.meshMapRecentering = UserDefaults.standard.object(forKey: "meshMapRecentering") as? Bool ?? true + 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 } diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index b491559a..86a13c35 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -22,7 +22,7 @@ struct MapViewSwiftUI: UIViewRepresentable { let centeringMode: CenteringMode let centerOnPositionsOnly: Bool - @AppStorage("meshMapRecentering") private var recenter = false + @AppStorage("meshMapRecentering") private var recenter: Bool = false @AppStorage("meshMapUserTrackingMode") private var userTrackingModeId = 0 // Offline Maps @@ -35,21 +35,31 @@ struct MapViewSwiftUI: UIViewRepresentable { let dynamicRegion: Bool = true func makeUIView(context: Context) -> MKMapView { - // Parameters + // Map View Parameters mapView.mapType = mapViewType mapView.addAnnotations(waypoints) - // Logic to manage the map centering options + mapView.setUserTrackingMode(UserTrackingModes(rawValue: userTrackingModeId )?.MKUserTrackingModeValue() ?? MKUserTrackingMode.none, animated: true) + if UserTrackingModes(rawValue: userTrackingModeId) != UserTrackingModes.none { + mapView.showsUserLocation = true + } else { + mapView.showsUserLocation = false + } switch centeringMode { case .allAnnotations: mapView.addAnnotations(positions) - mapView.fitAllAnnotations() + if UserTrackingModes(rawValue: userTrackingModeId) == UserTrackingModes.none { + mapView.fitAllAnnotations() + } case .allPositions: - mapView.fit(annotations: positions, andShow: true) + if UserTrackingModes(rawValue: userTrackingModeId) == UserTrackingModes.none { + mapView.fit(annotations: positions, andShow: true) + } else { + mapView.addAnnotations(positions) + } } // Other MKMapView Settings - mapView.showsUserLocation = false - mapView.preferredConfiguration.elevationStyle = .realistic + mapView.preferredConfiguration.elevationStyle = .realistic// .flat mapView.isPitchEnabled = true mapView.isRotateEnabled = true mapView.isScrollEnabled = true @@ -114,19 +124,19 @@ struct MapViewSwiftUI: UIViewRepresentable { mapView.removeAnnotations(mapView.annotations) mapView.addAnnotations(waypoints) mapView.setUserTrackingMode(UserTrackingModes(rawValue: userTrackingModeId )?.MKUserTrackingModeValue() ?? MKUserTrackingMode.none, animated: true) - mapView.showsUserLocation = true - } - switch centeringMode { - case .allAnnotations: - if annotationCount != mapView.annotations.count { + if UserTrackingModes(rawValue: userTrackingModeId) != UserTrackingModes.none { + mapView.showsUserLocation = true + } else { + mapView.showsUserLocation = false + } + switch centeringMode { + case .allAnnotations: mapView.addAnnotations(positions) - if recenter { + if recenter && UserTrackingModes(rawValue: userTrackingModeId) == UserTrackingModes.none { mapView.fitAllAnnotations() } - } - case .allPositions: - if annotationCount != mapView.annotations.count { - if recenter { + case .allPositions: + if recenter && UserTrackingModes(rawValue: userTrackingModeId) == UserTrackingModes.none { mapView.fit(annotations: positions, andShow: true) } else { mapView.addAnnotations(positions) @@ -161,13 +171,6 @@ struct MapViewSwiftUI: UIViewRepresentable { func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { switch annotation { - - case _ as MKClusterAnnotation: - let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "nodeGroup") as? MKMarkerAnnotationView ?? MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "WaypointGroup") - annotationView.markerTintColor = .brown - annotationView.displayPriority = .defaultLow - annotationView.tag = -1 - return annotationView case let positionAnnotation as PositionEntity: let reuseID = String(positionAnnotation.nodePosition?.num ?? 0) + "-" + String(positionAnnotation.time?.timeIntervalSince1970 ?? 0) let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "node") as? MKMarkerAnnotationView ?? MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: reuseID ) @@ -210,7 +213,6 @@ struct MapViewSwiftUI: UIViewRepresentable { annotationView.glyphImage = UIImage(systemName: "location.viewfinder") } else if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.sensor { annotationView.glyphImage = UIImage(systemName: "sensor") - } let pf = PositionFlags(rawValue: Int(positionAnnotation.nodePosition?.metadata?.positionFlags ?? 3)) @@ -254,9 +256,8 @@ struct MapViewSwiftUI: UIViewRepresentable { } else { annotationView.glyphText = String(UnicodeScalar(Int(waypointAnnotation.icon)) ?? "📍") } - annotationView.clusteringIdentifier = "waypointGroup" annotationView.markerTintColor = UIColor(.accentColor) - annotationView.displayPriority = .defaultHigh + annotationView.displayPriority = .required annotationView.titleVisibility = .adaptive let leftIcon = UIImageView(image: annotationView.glyphText?.image()) leftIcon.backgroundColor = UIColor(.accentColor) diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index 4a1428dd..5e1ebdb2 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -56,7 +56,7 @@ struct AppSettings: View { Text("phone.gps.interval.description") .font(.caption) - .listRowSeparator(.visible) + .foregroundColor(.gray) } Picker("map.usertrackingmode", selection: $userSettings.meshMapUserTrackingMode) { ForEach(UserTrackingModes.allCases) { utm in @@ -64,6 +64,9 @@ struct AppSettings: View { } } .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")) { @@ -75,18 +78,21 @@ struct AppSettings: View { } .pickerStyle(DefaultPickerStyle()) - Picker("map.centering", selection: $userSettings.meshMapCenteringMode) { - ForEach(CenteringMode.allCases) { cm in - Text(cm.description) + if userSettings.meshMapUserTrackingMode == 0 { + + Picker("map.centering", selection: $userSettings.meshMapCenteringMode) { + ForEach(CenteringMode.allCases) { cm in + Text(cm.description) + } } + .pickerStyle(DefaultPickerStyle()) + + Toggle(isOn: $userSettings.meshMapRecentering) { + + Label("map.recentering", systemImage: "camera.metering.center.weighted") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } - .pickerStyle(DefaultPickerStyle()) - - Toggle(isOn: $userSettings.meshMapRecentering) { - - Label("map.recentering", systemImage: "camera.metering.center.weighted") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } } HStack { @@ -128,12 +134,3 @@ struct AppSettings: View { } } } - -struct AppSettings_Previews: PreviewProvider { - - static var previews: some View { - Group { - AppSettings() - } - } -}