go to a SwiftUI view by tapping a pin annotation - mapkit

I want to go to new SwiftUI View by tapping a pin annotation
I have made a mapView and pin annotation on it I want to go to a SwiftUI view by Tapping on this pin how can I go to SwiftUi view in Uiviewrepresentable View
this is my mapView
func updateUIView(_ view: MKMapView, context: Context) {
let locationManager = CLLocationManager()
let path = Datas.path
var locationsDic: [[String: Any]] = [["latitude": 0, "longitude": 0],["latitude": 0, "longitude": 0],["latitude": 0, "longitude": 0]]
view.showsUserLocation = true
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
let locValue:CLLocationCoordinate2D = locationManager.location!.coordinate
let coordinate = CLLocationCoordinate2D(
latitude: locValue.latitude, longitude: locValue.longitude)
locationsDic[0]["latitude"] = locValue.latitude.advanced(by: 0.0)
locationsDic[0]["longitude"] = locValue.longitude.advanced(by: 0.0)
(locationsDic as NSArray).write(toFile: path, atomically: true)
let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
let region = MKCoordinateRegion(center: coordinate, span: span)
view.setRegion(region, animated: true)
let originAnnotation = MKPointAnnotation()
originAnnotation.title = "origin"
originAnnotation.coordinate = CLLocationCoordinate2D(latitude: 37.332072300, longitude: -122.011138100)
view.addAnnotation(originAnnotation)
})
}
locationsDic = NSArray(contentsOfFile: path) as! [[String: Any]]}}

Related

How to use other objc function's variable? (Long Press & Add a Button)

Is there any way to use other objc function's variable? Here is the scenario: When I long-press on some point of the view, then menu items come up, and when I press Add button, then I want to create a circle button on that position
//Menu Items
#objc func longPressForView(sender: UILongPressGestureRecognizer) -> CGPoint {
if sender.state == .began {
let menu = UIMenuController.shared
becomeFirstResponder()
let menuItemAdd = UIMenuItem(title: "Add", action: #selector(addCircleMenuItemAction))
let menuItemDelete = UIMenuItem(title: "Delete", action: #selector(handleMenuItemAction))
menu.menuItems = [menuItemAdd, menuItemDelete]
let location = sender.location(in: sender.view)
let menuLocation = CGRect(x: location.x, y: location.y, width: 0, height: 0)
menu.showMenu(from: sender.view!, rect: menuLocation)
//I tried to return this location but I don't know how to use it
return location
}
}
//Click Menu Items' Add Button to add a circle on that position.
#objc func addCircleMenuItemAction() {
let longPressedlocation = longPressForView(sender: )
print("Add item tapped")
view.addSubview(addCircle)
addCircle.layer.cornerRadius = CGFloat(circleAddDefaultSize/2)
addCircle.translatesAutoresizingMaskIntoConstraints = false
addCircle.backgroundColor = .black
//Here is where I want to use the location to add a circle
addCircle.frame = CGRect(origin: longPressedlocation?????, size: CGSize(width: circleAddDefaultSize,height: circleAddDefaultSize))
You can create a global variable and use that inside both functions, something like this
//Menu Items
var location: CGPoint
#objc func longPressForView(sender: UILongPressGestureRecognizer) -> CGPoint {
if sender.state == .began {
let menu = UIMenuController.shared
becomeFirstResponder()
let menuItemAdd = UIMenuItem(title: "Add", action: #selector(addCircleMenuItemAction))
let menuItemDelete = UIMenuItem(title: "Delete", action: #selector(handleMenuItemAction))
menu.menuItems = [menuItemAdd, menuItemDelete]
let location = sender.location(in: sender.view)
let menuLocation = CGRect(x: location.x, y: location.y, width: 0, height: 0)
menu.showMenu(from: sender.view!, rect: menuLocation)
//I tried to return this location but I don't know how to use it
self.location = location
return location
}
}
#objc func addCircleMenuItemAction() {
print("Add item tapped")
view.addSubview(addCircle)
addCircle.layer.cornerRadius = CGFloat(circleAddDefaultSize/2)
addCircle.translatesAutoresizingMaskIntoConstraints = false
addCircle.backgroundColor = .black
//Here is where I want to use the location to add a circle
addCircle.frame = CGRect(origin: self.location, size: CGSize(width: circleAddDefaultSize,height: circleAddDefaultSize))
}
the problem with your code is, when you long-press the view, the longPressForView gets executed and when you hit the button, it gets executed again inside the function: let longPressedlocation = longPressForView(sender: ), in this line. so the output won't be the same.

Without Bridging to ObjectiveC, Can We Get the Coordinates of a Tap Solely in SwiftUI?

I have the following code:
struct MyLocationMap: View {
#EnvironmentObject var userData: UserData
#State var annotationArray: [MyAnnotation] = [MyAnnotation(coordinates: CLLocationCoordinate2D(latitude: CurrentLocation().coordinates?.latitude ?? CLLocationCoordinate2D().latitude, longitude: CurrentLocation().coordinates?.longitude ?? CLLocationCoordinate2D().longitude), type: .waypoint)]
var body: some View {
MakeMapView(annotationArray: annotationArray)
.gesture(
LongPressGesture(minimumDuration: 1)
.onEnded { _ in
print("MapView pressed!\n--------------------------------------\n")
//Handle press here
})
}
}
import SwiftUI
import CoreLocation
import MapKit
struct MakeMapView : UIViewRepresentable {
typealias UIViewType = MKMapView
let annotationArray: [MyAnnotation]?
func makeUIView(context: UIViewRepresentableContext<MakeMapView>) -> MKMapView{
MKMapView()
}
func updateUIView(_ mapView: MKMapView, context: Context) {
mapView.showsUserLocation = true
if let coordinates = CurrentLocation().coordinates {
// updateAnnotations(from: mapView)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
mapView.showsUserLocation = true
mapView.showsCompass = true
mapView.showsScale = true
mapView.mapType = .satellite
let span = MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002)
let region = MKCoordinateRegion(center: coordinates, span: span)
mapView.setRegion(region, animated: true)
})
}
}
What I am having trouble with is implementing func convert(_ point: CGPoint, toCoordinateFrom view: UIView?) -> CLLocationCoordinate2D to obtain the lat/lon of the gesture solely using SwiftUI/Combine. Is it possible? If not, can I implement it with what I have?
I have reviewed the post at
How to handle touch gestures in SwiftUI in Swift UIKit Map component?
and
Add single pin to Mapkit with SwiftUI
Once I have the coordinates, dropping the pin is straightforward, but I can't wrap my head around getting the coordinates.
Thanks.
A DragGesture with no minimumDistance activates immediately and has location and startLocation in its onChanged listener, you can use that to grab location like so:
struct GesturesView: View {
#State private var location: CGPoint = .zero
var body: some View {
let drag = DragGesture(minimumDistance: 0).onChanged {
self.location = $0.startLocation
}
let longPress = LongPressGesture().onEnded { _ in
self.doSomething(with: self.location)
}
let gesture = longPress.simultaneously(with: drag)
return Rectangle()
.frame(width: 300, height: 300)
.padding()
.gesture(gesture)
}
func doSomething(with location: CGPoint) {
print("Pressed at", location)
}
}

Swiftui how to use MKOverlayRenderer?

I want draw a route on the map.
but struct without using delegate.
struct MapView : UIViewRepresentable {
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
}
how can I do?
You need to specify a delegate if you want mapView(_:rendererFor:) to be called:
struct MapView: UIViewRepresentable {
#Binding var route: MKPolyline?
let mapViewDelegate = MapViewDelegate()
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
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)
}
}
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)
}
}
class MapViewDelegate: NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.fillColor = UIColor.red.withAlphaComponent(0.5)
renderer.strokeColor = UIColor.red.withAlphaComponent(0.8)
return renderer
}
}
Used like so:
struct ContentView : View {
#State var route: MKPolyline?
var body: some View {
MapView(route: $route)
.onAppear {
self.findCoffee()
}
}
}
private extension ContentView {
func findCoffee() {
let start = CLLocationCoordinate2D(latitude: 37.332693, longitude: -122.03071)
let region = MKCoordinateRegion(center: start, latitudinalMeters: 2000, longitudinalMeters: 2000)
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "coffee"
request.region = region
MKLocalSearch(request: request).start { response, error in
guard let destination = response?.mapItems.first else { return }
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: start))
request.destination = destination
MKDirections(request: request).calculate { directionsResponse, _ in
self.route = directionsResponse?.routes.first?.polyline
}
}
}
}
Yielding:

Apple Maps Not Showing Map Overlay

I have a question regarding creating a Apple Map Overlay. I am trying to set on odd shape overlay from a JSON file. I have researched this on Stack Overflow, and have tried many of the solutions, but none seem to work. My code is below:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate, UIGestureRecognizerDelegate {
#IBOutlet weak var mapView: MKMapView!
var coordinate: CLLocationCoordinate2D?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
mapView.showsUserLocation = true
mapView.delegate = self
mapView.mapType = .standard
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.TapGesture))
mapView.addGestureRecognizer(gestureRecognizer)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolygon {
let polygonView = MKPolygonRenderer(overlay: overlay)
polygonView.strokeColor = UIColor.black
polygonView.lineWidth = 0.5
polygonView.fillColor = UIColor.blue
return polygonView
}
return MKOverlayRenderer()
}
#objc func TapGesture(gesRect: UITapGestureRecognizer) {
let location = gesRect.location(in: mapView)
coordinate = mapView.convert(location,toCoordinateFrom: mapView)
let locCoord = mapView.convert(location, toCoordinateFrom: mapView)
print("Tapped at lat: \(locCoord.latitude) long: \(locCoord.longitude)")
print("Tapped at: \(location)")
self.retreiveShape() { (full_shape) in
if let shape = full_shape {
let polygon = MKPolygon.init(coordinates: shape, count: shape.count)
self.mapView.addOverlay(polygon)
} else {
print("ARRAY EMPTY")
}
}
}
func retreiveShape(completion: #escaping ([CLLocationCoordinate2D]?) -> ()) {
let path = Bundle.main.path(forResource: "shape", ofType: "json")
var coord_array = [CLLocationCoordinate2D]()
do {
let data = try Data.init(contentsOf: URL.init(fileURLWithPath: path!))
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let dictionary = json as? [String: Any] {
if let shape = dictionary["shape"] as? Array<Any> {
for regions in shape {
guard let region = regions as? Array<Array<Array<Double>>> else {
print("NOT HAPPENING")
return
}
for sections in region {
for coord in sections {
print("LATITUDE: \(coord[0])", "LONGITUDE: \(coord[1])")
let coordinatesToAppend = CLLocationCoordinate2D(latitude: coord[0], longitude: coord[1])
coord_array.append(coordinatesToAppend)
}
}
}
completion(coord_array)
}
}
} catch let error {
print(error)
}
}
The shape.json file is below:
{
"shape":[
[
[
[-81.621199, 30.282314],
[-81.613987, 30.281941],
[-81.611277, 30.284743],
[-81.602735, 30.284026],
[-81.601978, 30.292561],
[-81.596275, 30.290861],
[-81.592406, 30.290182],
[-81.571146, 30.28763],
[-81.55922, 30.286602],
[-81.559148, 30.291132],
[-81.558633, 30.294747],
[-81.55881, 30.312887],
[-81.558601, 30.312888],
[-81.558622, 30.316235],
[-81.558313, 30.316828],
[-81.552252, 30.320252],
[-81.548471, 30.321618],
[-81.527882, 30.323989],
[-81.529486, 30.328076],
[-81.537635, 30.336704],
[-81.537706, 30.337221],
[-81.538717, 30.338277],
[-81.539343, 30.338462],
[-81.542809, 30.341686],
[-81.547286, 30.345211],
[-81.552498, 30.348839],
[-81.552559, 30.352445],
[-81.577566, 30.352039],
[-81.578098, 30.353324],
[-81.578161, 30.35642],
[-81.577294, 30.3596],
[-81.576996, 30.366609],
[-81.58011, 30.366553],
[-81.580875, 30.37062],
[-81.580844, 30.373862],
[-81.581462, 30.374486],
[-81.578114, 30.374236],
[-81.572908, 30.374611],
[-81.562232, 30.372303],
[-81.551965, 30.366559],
[-81.548676, 30.365568],
[-81.540187, 30.378172],
[-81.538175, 30.380467],
[-81.538213, 30.387239],
[-81.536613, 30.388739],
[-81.512612, 30.392739],
[-81.505211, 30.390739],
[-81.490911, 30.392139],
[-81.49085, 30.389014],
[-81.489978, 30.389207],
[-81.488818, 30.38775],
[-81.489203, 30.389266],
[-81.487056, 30.390019],
[-81.481446, 30.391262],
[-81.479505, 30.39117],
[-81.477708, 30.390635],
[-81.476792, 30.390609],
[-81.476244, 30.391002],
[-81.473212, 30.389422],
[-81.472125, 30.388436],
[-81.472225, 30.388071],
[-81.474072, 30.386758],
[-81.475085, 30.384287],
[-81.474394, 30.381898],
[-81.473246, 30.38059],
[-81.473337, 30.380112],
[-81.47295, 30.379864],
[-81.472643, 30.380053],
[-81.471914, 30.379532],
[-81.471629, 30.378346],
[-81.470845, 30.377256],
[-81.468671, 30.376016],
[-81.466871, 30.374481],
[-81.465402, 30.374424],
[-81.464374, 30.373764],
[-81.465116, 30.373015],
[-81.467728, 30.372493],
[-81.469102, 30.371435],
[-81.470279, 30.369931],
[-81.472008, 30.370608],
[-81.473695, 30.370041],
[-81.471862, 30.370238],
[-81.470952, 30.369737],
[-81.471715, 30.369462],
[-81.470506, 30.369378],
[-81.469456, 30.368207],
[-81.468051, 30.367707],
[-81.46754, 30.366828],
[-81.466905, 30.366464],
[-81.467432, 30.366219],
[-81.466928, 30.365735],
[-81.465222, 30.365136],
[-81.464909, 30.364103],
[-81.46316, 30.362764],
[-81.463369, 30.36188],
[-81.462197, 30.361235],
[-81.461151, 30.36123],
[-81.46117, 30.360531],
[-81.461878, 30.360305],
[-81.461619, 30.359642],
[-81.461873, 30.358669],
[-81.461645, 30.358376],
[-81.460504, 30.358329],
[-81.46288, 30.357969],
[-81.462786, 30.357137],
[-81.461247, 30.355282],
[-81.460556, 30.352518],
[-81.46184, 30.340222],
[-81.462497, 30.339325],
[-81.465064, 30.337897],
[-81.471588, 30.328301],
[-81.472988, 30.318258],
[-81.469123, 30.319481],
[-81.450496, 30.320896],
[-81.443818, 30.302908],
[-81.442451, 30.301512],
[-81.438991, 30.299798],
[-81.437921, 30.298031],
[-81.437696, 30.284657],
[-81.438134, 30.283427],
[-81.439935, 30.281191],
[-81.440578, 30.279729],
[-81.440309, 30.276152],
[-81.441217, 30.271746],
[-81.440891, 30.270368],
[-81.440247, 30.269313],
[-81.438555, 30.267721],
[-81.43765, 30.266188],
[-81.43705, 30.257116],
[-81.441869, 30.256519],
[-81.45385, 30.252008],
[-81.466184, 30.251073],
[-81.472173, 30.251296],
[-81.491372, 30.251034],
[-81.507105, 30.253603],
[-81.510744, 30.253761],
[-81.530261, 30.250144],
[-81.56957, 30.249854],
[-81.584658, 30.251369],
[-81.586895, 30.251326],
[-81.589607, 30.250593],
[-81.593308, 30.248471],
[-81.605497, 30.260294],
[-81.621493, 30.282334],
[-81.621199, 30.282314]
]
]
]
}
It should create an odd shape overlay in the Southside of Jacksonville,FL, but it isn't. When the completion block is called the Coordinates are added to the array, but the map overlay isn't showing. Any thoughts?
Well this is somewhat embarrassing. I did as was suggested in the comments, and tried having the shape with nine vertices. It still didn't work. I then changed the coord from:
print("LATITUDE: \(coord[0])", "LONGITUDE: \(coord[1])")
let coordinatesToAppend = CLLocationCoordinate2D(latitude: coord[0], longitude: coord[1])
to:
print("LATITUDE: \(coord[1])", "LONGITUDE: \(coord[0])")
let coordinatesToAppend = CLLocationCoordinate2D(latitude: coord[1], longitude: coord[0])
It works perfectly. Turns out I had the Latitude and Longitude wrong.

Crash when wrapping latitude and longitude in map

I get the current user map location and present it on a map with his location as the center of map.
It crash on the line
let currentLatitude = (locationManager.location?.coordinate.latitude)!
let currentLongitude = (locationManager.location?.coordinate.longitude)!
with the error "Could not inset legal attribution from corner 4"
I think its something related to the force wrap for latitude and longitude. What shall i do to fix this error??
Here is my code:
// Location Manager settings
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
let currentLatitude = (locationManager.location?.coordinate.latitude)!
let currentLongitude = (locationManager.location?.coordinate.longitude)!
//Map settings
mapMyLocation.showsUserLocation = true
mapMyLocation.delegate = self
let locationcoordinates = CLLocationCoordinate2D(latitude: currentLatitude, longitude: currentLongitude)
let zoomSpan = MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
let region = MKCoordinateRegion(center: locationcoordinates, span: zoomSpan)
mapMyLocation.setRegion(region, animated: true)
Try the following code
if let currentLatitude = locationManager.location?.coordinate.latitude,
let currentLongitude = locationManager.location?.coordinate.longitude {
//Map settings
mapMyLocation.showsUserLocation = true
mapMyLocation.delegate = self
let locationcoordinates = CLLocationCoordinate2D(latitude: currentLatitude, longitude: currentLongitude)
let zoomSpan = MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
let region = MKCoordinateRegion(center: locationcoordinates, span: zoomSpan)
mapMyLocation.setRegion(region, animated: true)
}