From 96589b8a1a10d7b8b5173d4396cf151f53ee857d Mon Sep 17 00:00:00 2001 From: Joshua Pirihi Date: Fri, 24 Dec 2021 20:06:21 +1300 Subject: [PATCH] Add custom node annotations with basic callout views --- Meshtastic Client.xcodeproj/project.pbxproj | 24 +++++ .../UserInterfaceState.xcuserstate | Bin 28207 -> 40835 bytes .../Map/Custom/PositionAnnotationView.swift | 63 ++++++++++++ MeshtasticClient/Views/Map/MapView.swift | 96 ++++++++++++++++++ MeshtasticClient/Views/Nodes/NodeMap.swift | 9 +- 5 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 MeshtasticClient/Views/Map/Custom/PositionAnnotationView.swift create mode 100644 MeshtasticClient/Views/Map/MapView.swift diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index ae67097f..39370ae8 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + C9483F6D2773017500998F6B /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9483F6C2773017500998F6B /* MapView.swift */; }; + C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */; }; DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; }; DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E65252767A01F00E45FC5 /* NodeDetail.swift */; }; DD47E3CE26F103C600029299 /* NodeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CD26F103C600029299 /* NodeList.swift */; }; @@ -67,6 +69,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + C9483F6C2773017500998F6B /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; + C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionAnnotationView.swift; sourceTree = ""; }; DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = ""; }; DD2E65252767A01F00E45FC5 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = ""; }; DD47E3CD26F103C600029299 /* NodeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeList.swift; sourceTree = ""; }; @@ -141,6 +145,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + C9483F6B2773016700998F6B /* Map */ = { + isa = PBXGroup; + children = ( + C9A7BC0E27759A6800760B50 /* Custom */, + C9483F6C2773017500998F6B /* MapView.swift */, + ); + path = Map; + sourceTree = ""; + }; + C9A7BC0E27759A6800760B50 /* Custom */ = { + isa = PBXGroup; + children = ( + C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */, + ); + path = Custom; + sourceTree = ""; + }; DD47E3CA26F0E50300029299 /* Nodes */ = { isa = PBXGroup; children = ( @@ -261,6 +282,7 @@ DDC2E18726CE24E40042C5E4 /* Views */ = { isa = PBXGroup; children = ( + C9483F6B2773016700998F6B /* Map */, DDC2E18D26CE25CB0042C5E4 /* Helpers */, DD47E3D726F2F21A00029299 /* Bluetooth */, DD47E3CA26F0E50300029299 /* Nodes */, @@ -506,11 +528,13 @@ DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */, DD9D8F2F2764403B00080993 /* Meshtastic.xcdatamodeld in Sources */, DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */, + C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */, DD47E3CE26F103C600029299 /* NodeList.swift in Sources */, DD47E3D626F17ED900029299 /* CircleText.swift in Sources */, DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */, DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */, DD539500276C452400AD86B1 /* Preferences.swift in Sources */, + C9483F6D2773017500998F6B /* MapView.swift in Sources */, DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */, DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */, DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */, diff --git a/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate b/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate index 0cf935596330218c3fcaee2a3d3ba117033cec78..5d4768a39ba9b50dbd71b8cd1573204eba8a6ae0 100644 GIT binary patch literal 40835 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 Od+I(3PyK)968{6xLDfe9 delta 14843 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 diff --git a/MeshtasticClient/Views/Map/Custom/PositionAnnotationView.swift b/MeshtasticClient/Views/Map/Custom/PositionAnnotationView.swift new file mode 100644 index 00000000..8b76d074 --- /dev/null +++ b/MeshtasticClient/Views/Map/Custom/PositionAnnotationView.swift @@ -0,0 +1,63 @@ +// +// PositionAnnotationView.swift +// MeshtasticClient +// +// Created by Joshua Pirihi on 24/12/21. +// + +import UIKit +import MapKit + +class PositionAnnotation: NSObject, MKAnnotation { + + // This property must be key-value observable, which the `@objc dynamic` attributes provide. + @objc dynamic var coordinate = CLLocationCoordinate2D(latitude: 0, longitude: 0) + + // Required if you set the annotation view's `canShowCallout` property to `true` + var title: String? = "Title" + + var shortName: String? + + // This property defined by `MKAnnotation` is not required. + //var subtitle: String? = NSLocalizedString("SAN_FRANCISCO_SUBTITLE", comment: "SF annotation") +} + +class PositionAnnotationView: MKAnnotationView { + + private let annotationFrame = CGRect(x: 0, y: 0, width: 32, height: 32) + private let label: UILabel + + override init(annotation: MKAnnotation?, reuseIdentifier: String?) { + self.label = UILabel(frame: annotationFrame.offsetBy(dx: 0, dy: 0)) + super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) + self.frame = annotationFrame + self.label.font = UIFont.preferredFont(forTextStyle: .caption2) + self.label.textColor = .white + self.label.textAlignment = .center + self.backgroundColor = .clear + self.addSubview(label) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) not implemented!") + } + + public var name: String = "" { + didSet { + self.label.text = name + } + } + + override func draw(_ rect: CGRect) { + guard let context = UIGraphicsGetCurrentContext() else { return } + + let circleRect = CGRect(x: 1, y: 1, width: 30, height: 30) + + context.setFillColor(CGColor(red: 0, green: 0.5, blue: 1.0, alpha: 1.0)) + + context.fillEllipse(in: circleRect) + + } + + +} diff --git a/MeshtasticClient/Views/Map/MapView.swift b/MeshtasticClient/Views/Map/MapView.swift new file mode 100644 index 00000000..34fed2e6 --- /dev/null +++ b/MeshtasticClient/Views/Map/MapView.swift @@ -0,0 +1,96 @@ +// +// MapView.swift +// MeshtasticClient +// +// Created by Joshua Pirihi on 22/12/21. +// + +import Foundation +import UIKit +import MapKit +import SwiftUI +import CoreData + +struct MapView: UIViewRepresentable { + //@Binding var route: MKPolyline? + var nodes: FetchedResults + + let mapViewDelegate = MapViewDelegate() + + func makeUIView(context: Context) -> MKMapView { + let map = MKMapView(frame: .zero) + map.userTrackingMode = .follow + map.mapType = .satellite + map.register(PositionAnnotationView.self, forAnnotationViewWithReuseIdentifier: NSStringFromClass(PositionAnnotationView.self)) + return map + } + + func updateUIView(_ view: MKMapView, context: Context) { + view.delegate = mapViewDelegate // (1) This should be set in makeUIView, but it is getting reset to `nil` + view.translatesAutoresizingMaskIntoConstraints = false // (2) In the absence of this, we get constraints error on rotation; and again, it seems one should do this in makeUIView, but has to be here + //addRoute(to: view) + showNodePositions(to: view) + } +} + +private extension MapView { + //func addRoute(to view: MKMapView) { + // if !view.overlays.isEmpty { + // view.removeOverlays(view.overlays) + // } + + //guard let route = route else { return } + //let mapRect = route.boundingMapRect + //view.setVisibleMapRect(mapRect, edgePadding: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10), animated: true) + //view.addOverlay(route) + //} + func showNodePositions(to view: MKMapView) { + if !view.annotations.isEmpty { + view.removeAnnotations(view.annotations) + } + + for node in self.nodes { + //try and get the last position + if (node.positions?.count ?? 0) > 0 { + let annotation = PositionAnnotation() + annotation.coordinate = (node.positions!.lastObject as! PositionEntity).coordinate ?? CLLocationCoordinate2D(latitude: 0, longitude: 0) + annotation.title = node.user?.longName ?? "Unknown" + annotation.shortName = node.user?.shortName?.uppercased() ?? "???" + + view.addAnnotation(annotation) + } + } + } +} + +class MapViewDelegate: NSObject, MKMapViewDelegate { + + func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { + + guard !annotation.isKind(of: MKUserLocation.self) else { + // Make a fast exit if the annotation is the `MKUserLocation`, as it's not an annotation view we wish to customize. + return nil + } + + var annotationView: MKAnnotationView? + + if let annotation = annotation as? PositionAnnotation { + annotationView = self.setupPositionAnnotationView(for: annotation, on: mapView) + } + + return annotationView + } + + private func setupPositionAnnotationView(for annotation: PositionAnnotation, on mapView: MKMapView) -> PositionAnnotationView { + let identifier = NSStringFromClass(PositionAnnotationView.self) + + let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? PositionAnnotationView ?? PositionAnnotationView() + + annotationView.name = annotation.shortName ?? "???" + + annotationView.canShowCallout = true + + + return annotationView + } +} diff --git a/MeshtasticClient/Views/Nodes/NodeMap.swift b/MeshtasticClient/Views/Nodes/NodeMap.swift index 9a76385d..0074f354 100644 --- a/MeshtasticClient/Views/Nodes/NodeMap.swift +++ b/MeshtasticClient/Views/Nodes/NodeMap.swift @@ -51,12 +51,12 @@ struct NodeMap: View { - Map(coordinateRegion: regionBinding, + /*Map(coordinateRegion: regionBinding, interactionModes: [.all], showsUserLocation: true, userTrackingMode: .constant(.follow), annotationItems: self.locationNodes.filter({ nodeinfo in - return nodeinfo.positions != nil && nodeinfo.positions!.count > 0 + return nodeinfo.positions != nil && nodeinfo.positions!.count > 0// && (nodeinfo.positions?.lastObject as? AnyObject)?.coordinate != nil }) ) { locationNode in @@ -67,7 +67,10 @@ struct NodeMap: View { } ) - } + }*/ + + MapView(nodes: self.locationNodes) + //} .frame(maxHeight: .infinity) .ignoresSafeArea(.all, edges: [.leading, .trailing])