Tried several ways to access parent EnvironmentObject from nested child function, but could not seem to get a method that works. Currently settled on mis-using UserDefaults, but there must be a proper way of doing this. Inside the code, I made comment where access works, and where it doesn’t. Here’s the code:
struct EditMap: UIViewRepresentable {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var model: ContentModel
@EnvironmentObject var netStat:NetStatus // internet ok?
@EnvironmentObject var tgtLink:TargetLink
@EnvironmentObject var newLink:NewLink
@State private var myMapView: MKMapView?
@State var setMapRegion:Bool = true
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "timeStamp", ascending: false)])
var globalLinks: FetchedResults<Link>
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
mapView.mapType = .mutedStandard
mapView.showsUserLocation = false
let theLink = globalLinks[0]
// newLink access works ok at this level..
// so I assume the top level '.environmentObject(NewLink())'
// sets up the original instance of EnvironmentObject OK.
let center = CLLocationCoordinate2D(latitude: Double(newLink.lat) ?? theLink.eLat, longitude: Double(newLink.long) ?? theLink.eLong)
let span = MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
let region = MKCoordinateRegion(center: center, span: span)
mapView.setRegion(region, animated: false)
DispatchQueue.global().async {
self.myMapView = mapView
}
let gRecognizer = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.addAnnotationOnTapGesture(sender:)))
mapView.addGestureRecognizer(gRecognizer)
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
uiView.removeAnnotations(uiView.annotations)
}
static func dismantleUIView(_ uiView: MKMapView, coordinator: ()) {
uiView.removeAnnotations(uiView.annotations)
}
// MARK - Coordinator class
func makeCoordinator() -> Coordinator {
return Coordinator(map: self)
}
class Coordinator: NSObject, MKMapViewDelegate {
@State private var shouldAnimate = false
// tried adding @EnvironmentObject var newLink:NewLink here, but no help.
var newLat:Double = 51.47
var newLong:Double = 0.0
var newName:String = ""
let greenwichCoord = CLLocationCoordinate2D(latitude: 51.447, longitude: 0)
var map: EditMap
init(map: EditMap) {
self.map = map
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// tried adding @EnvironmentObject var newLink:NewLink here, but also no help.
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: Constants.annotationReuseId)
let markerView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: Constants.annotationReuseId)
markerView.glyphImage = UIImage(named: "EarthStationRight")
markerView.markerTintColor = UIColor(Color("darkGreen"))
markerView.glyphTintColor = UIColor(Color("whiteAndDark"))
annotationView = markerView
let newCenter = annotation.coordinate
let region = MKCoordinateRegion(center: newCenter, span: mapView.region.span )
mapView.setRegion(region, animated: true)
newLat = annotation.coordinate.latitude
UserDefaults.standard.set(fourStr(newLat), forKey: "newLat")
newLong = annotation.coordinate.longitude
UserDefaults.standard.set(fourStr(westLong(newLong)), forKey: "newLong")
let targetLocation = CLLocation(latitude: newLat, longitude: newLong)
let geoCoder = CLGeocoder()
DispatchQueue.global().async {
geoCoder.reverseGeocodeLocation(targetLocation) { [self] (placemarks, error) in
if error == nil && placemarks != nil {
let testPlacemark = placemarks?.first
let thePlacemark = testPlacemark!
let locality = String(thePlacemark.locality ?? "")
let area = String(thePlacemark.administrativeArea ?? "")
let country = String(thePlacemark.country ?? "")
let state = String(thePlacemark.isoCountryCode ?? "")
newName = SiteService.locStr(country: country, locality: locality, state: state, area: area)
// The following works by using defaults and later retrieving the default values using .onDisappear at top level 'EditMapView'.
// But this seems like a mis-use of UserDefaults.
UserDefaults.standard.set(newName, forKey: "newName")
// There must be some way to access newLink.name from nested Coordinator & mapView levels.
// But I always get "Fatal error: No ObservableObject of type NewLink found. A
// View.environmentObject(_:) for NewLink may be missing as an ancestor of this view."
}
}
}
return annotationView
}
@objc func addAnnotationOnTapGesture(sender: UITapGestureRecognizer) {
if sender.state == .ended {
shouldAnimate = false
sender.isEnabled = false
let point = sender.location(in: map.myMapView)
let coordinate = map.myMapView?.convert(point, toCoordinateFrom: map.myMapView)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate ?? greenwichCoord
annotation.title = "Select Site"
map.myMapView?.removeAnnotations(map.myMapView!.annotations)
map.myMapView?.addAnnotation(annotation)
// TODO: Wait routine sloppy. Prevents multiple annotations. Better way?
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
sender.isEnabled = true
}
shouldAnimate = false
}
}
}
}