MKMap I can't open a URL in my mapview swift 4 - swift

Hello guys I am working with MKMap and populating some pins into a map everything is good with that in the pin I can show name and last name + when I click the pin shows an URL but the idea is when I tap the URL to open the web browser and redirect to the URL and for some reason is not working.
Here is a screenshot of my method that I use to open my url:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
if let toOpen = view.annotation?.subtitle! {
if let url = URL(string: toOpen) {
if #available(iOS 10, *) {
UIApplication.shared.open(url, options: [:],
completionHandler: {
(success) in
print("Open \(url): \(success)")
})
} else {
_ = UIApplication.shared.openURL(url)
}
}
}
}
}
this is my method where I define the pin properties, a weird thing is that I am giving my pin a color blue but still show it in red.
private func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.pinTintColor = UIColor.blue
pinView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
} else {
pinView!.annotation = annotation
}
return pinView
}
here is my Github project. I would like to know what I am doing wrong because I can tap my pin shows the information but it doesn't open the URL.
Thanks for your time guys!

Maybe your viewForAnnotation method is not called. Replace
private func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
with
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

I solved it.
I realized that my mapView functions weren't working at all. I comment the code and move stuff and those two functions weren't doing anything.
So after awhile watching videos and reading, I found that I need it to use the mapView delegate to the controller itself, so I could use my mapView functions and just adding this line of code:
override func viewDidLoad() {
super.viewDidLoad()
//MIRACLE HAPPENS HERE
mapView.delegate = self
}
Just with that line of code in my viewDidLoad function, all my code inside the mapView function started working.

Related

MapView doesn't show Annotations after adding viewForAnnotations

so I am using MapKit for the first time and I am trying to show annotations with a callout.
The annotations appear but as soon as I add the delegate function viewFor annotation (func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {}) the annotations do not appear anymore. Is there anything I am missing in that function ?
Edit
I forgot to mention that the View Controller is the delegate of MKMapViewDelegate
#objc func addAnnotationOnLongPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == .ended {
let point = gesture.location(in: self.mapView)
let coordinate = self.mapView.convert(point, toCoordinateFrom: self.mapView)
//Now use this coordinate to add annotation on map.
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
usf.fetchJSON(urlString: usf.setNewLocation(lat: annotation.coordinate.latitude, lon: annotation.coordinate.longitude)) { (data, success) in
if success {
annotation.title = data?.name
annotation.subtitle = "HAa"
} else {
let alertUI = self.usf.createAlertOK(title: "Something went wrong!", message: "Cannot get the current weather! \n Check your connectivity or try again later")
self.present(alertUI, animated: true, completion: nil)
}
}
let previousAnnotations = self.mapView.annotations
if !previousAnnotations.isEmpty{
self.mapView.removeAnnotation(previousAnnotations[0])
}
self.mapView.addAnnotation(annotation)
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
print("in viewFor annotation") // prints out
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "TempAnnotationView")
annotationView.canShowCallout = true
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
self.mapView.showAnnotations(mapView.annotations, animated: false)
return annotationView
}
You are instantiating a MKAnnotationView. If you have some custom image, then set the image property of this MKAnnotationView. Or, easier, just use MKPinAnnotationView or MKMarkerAnnotationView instead.
And as Sh_Kahn noted, make sure you create you annotation inside the closure, not after it.
You need
if success {
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = data?.name
annotation.subtitle = "HAa"
self.mapView.addAnnotation(annotation)
}
The api that gets name is asynchronous so you have to add the filled annotation inside the callback

Custom MKAnnotationView want to be tapped again

I'm having problems with my custom MKAnnotationView.
I download the coordinate where to place it from my REST api and I place the pin on map with this code:
private func refreshMapView(andCenterMap: Bool){
DispatchQueue.main.async { [weak self] in
guard let self = self else {
return
}
self.mapView.addAnnotations(self.spotsArray)
if andCenterMap {
self.centerMap(at: self.mapView.userLocation.coordinate)
}
}
}
Ones the pin is placed I zoom automatically the map.
Here the code for the custom annotation creation:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Do not touch the annotation of the user location
if annotation.isKind(of: MKUserLocation.self){
return nil
}
let annoIdentifier = "SPOT"
var annotationView: MKAnnotationView?
if let dequeued = mapView.dequeueReusableAnnotationView(withIdentifier: annoIdentifier) {
annotationView = dequeued
}else{
let av = MKAnnotationView(annotation: annotation, reuseIdentifier: annoIdentifier)
annotationView = av
}
// Changing the image of the pin
annotationView!.annotation = annotation
if let image = UIImage(named: "map_pin") {
annotationView!.image = image
let deltaY = image.size.height/2
annotationView!.centerOffset = CGPoint(x: 0.0, y: -deltaY)
}else{
annotationView!.image = nil
}
return annotationView
}
As you can notice, my custom pin is using this image (#1x, #2x, #3x)
Ones tapped I want to show the "detail view" and I use the annotation as a sender in order to have all the information I need on the next view.
Here the code:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let ann = view.annotation else {
return
}
if ann.isKind(of: MKUserLocation.self){
return
}
self.performSegue(withIdentifier: "showDetailSegue", sender: ann)
}
So I present all the data I need to and I can go back to the "map view".
The problem is:
In the map view, if I want to select the same annotation again, this is not tappable anymore.
I can see it (in the right place) but I can select it again only if I zoom a little bit and I tap "around" the annotation.
Any suggestione how to solve this issue?
Thanks in advance!
Add this end of didSelect
mapView.deselectAnnotation(ann,animated:false)

Swift distinguishing between callout buttons

Hi I'm mostly new to coding and this is my first project that I've worked on. I'm making an app that displays pools which are hiring around your address and wanted to make it so that pressing on the callouts will bring you to a unique ViewController depending on which one you pressed.
From what I've seen so far online it looks like it needs to be done in an extension of the ViewController but was stuck on how to differentiate between callouts (as of right now I have it so that pressing it will show the directions, but I'd rather show more information that just that, which to my limited knowledge would mean displaying another view controller unless anyone else has any other ideas)
extension MapViewController : MKMapViewDelegate
{
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if let annotation = annotation as? Pools {
let identifier = "pin"
var view: MKPinAnnotationView
if let dequeuedView =
mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView{
dequeuedView.annotation = annotation
view = dequeuedView
}
else {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure) as UIView
}
return view
}
return nil
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let location = view.annotation as! Pools
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
location.mapItem().openInMaps(launchOptions: launchOptions)
}
}
Any help would be greatly appreciated!

[Swift]How an annotationview always show callout

I want to show the callout of the annotationview always show, I mean when the mapview is loaded, all the annotations‘ callout is shown.
How can I do this?
At anytime, there can be only one callout on the map. Framework reuses the previous callout when a new is to be presented, and here is showing a single callout view when annotation is added, like so
let annotation = MKPointAnnotation()
annotation.title = "Title for Callout"
mapView.addAnnotation(annotation)
This causes the delegate method viewForAnnotation to be fired:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let view = MKAnnotationView(annotation: annotation, reuseIdentifier: "AnnotId")
view.canShowCallout = true
return view
}
Framework will add the above returned view and the didAddViews delegate is fired, now show the callout view by selecting the annnotation, like so
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
if let annotation = views.first(where: { $0.reuseIdentifier == "AnnotId" })?.annotation {
mapView.selectAnnotation(annotation, animated: true)
}
}
Add annotation on map: mapView.addAnnotation(myAnnotation)
Select annotation mapView.selectAnnotation(myAnnotation, animated: false)
Add private function private func bringMyAnnotationToFront() with code if let myAnnotation = mapView.annotations.first(where: { $0 is MKPointAnnotation }) { mapview.selectAnnotation(myAnnotation, animated: false) }
Set MKMapViewDelegate and implement two methods with bringMyAnnotationToFront:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView), func mapView(MKMapView, didDeselect: MKAnnotationView)

How to draw "rotating" arrow as annotation?

Ok, my new task is this: inside a tracker app I have a map which I'm trying to annotate with custom heading arrow. I need this arrow to be rotated to a certain angle.
I have all the data (i.e. dergrees and locations) and this method:
headingImageView?.transform = CGAffineTransform(rotationAngle: CGFloat(rotationAngle))
So I tried:
Creating an annotation with
let annotation = UserAnnotation(coordinate: locations.last!.coordinate)
mapView.addAnnotation(annotation)
Conforming to MKMapViewDelegate protocol
extension TrackViewController: MKMapViewDelegate
Implementing methods like :viewFor: and also :rendererFor:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if !overlay.isKind(of: MKPolyline.classForCoder()) {
return MKPolylineRenderer(overlay: overlay)
}
let polyline = overlay as! MKPolyline
let renderer = MKPolylineRenderer(polyline: polyline)
renderer.strokeColor = .blue
renderer.lineWidth = 3
return renderer
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is UserAnnotation {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "Online")
annotationView.image = UIImage(named: "arrow")
return annotationView
}
}
And so it kind of works, but I need to rotate an image every time location is updated. How should I do that? Any advice would be appreciated
you can use this code :
but this is important to know you have to put (radian) for rotationAngle
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is UserAnnotation {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "Online")
annotationView.image = UIImage(named: "arrow")
annotationView.transform = CGAffineTransform(rotationAngle: CGFloat(//what you want with type of radian))
return annotationView
}
}
Well, for now I did that:
Globally declared image of an arrow and an optional annotation
let arrow = #imageLiteral(resourceName: "arrow")
var rotatedArrow: UIImage?
var oldAnnotation: MKPointAnnotation?
Rotated image to a certain angle when I get this angle
rotatedArrow = arrow.imageRotated(by: CGFloat(locations.last!.course))
Passed this rotated image to the delegate's method as
annotationView.image = rotatedArrow
return annotationView
Stored and deleted previous annotation
But it all looks soooo ugly