How to draw "rotating" arrow as annotation? - swift

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

Related

How to change markerTintColor of specific annotationView in swift4

I created a property list for location details, title, subtitle ,type and coordinates. What I am trying to do is change the annotationView color according to the type of the location.In my code the compiler move directly to else statement. Could someone explain how can I solve this?
func readRecordFromPlist(){
//read record from plist code
}
func mapView(_ MapView: MKMapView, viewFor annotation: MKAnnotation) ->
MKAnnotationView? {
let obj = userNSObj()
if obj.type == "Type1" {
let view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "pin")
view.markerTintColor = .blue
return view
}else{
let view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "pin")
view.markerTintColor = .purple
return view
}
}

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

I need to show my location with the normal blue dot and another point with a different marker

I changed the design of a marker in the mapView, but in doing this I changed the marker of my location and it was not what I wanted. I just want to change the marker of MIT, and the one of my location to leave it only with the normal blue marker that appears on all maps.
Here I leave some code that could be failing.
mapView.register(MKMarkerAnnotationView.self,
forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
let mitCoordinate = CLLocationCoordinate2D(latitude: -41.471373559102965, longitude: -72.94215917587279)
let mitAnnotation = SchoolAnnotation(coordinate: mitCoordinate, title: "MIT", subtitle: "mit - USA")
mapView.addAnnotation(mitAnnotation)
mapView.setRegion(mitAnnotation.region, animated: true)
extension BusinessViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let schoolAnnotationView
= mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier) as? MKMarkerAnnotationView {
schoolAnnotationView
.animatesWhenAdded = true
schoolAnnotationView
.titleVisibility = .adaptive
schoolAnnotationView
.titleVisibility = .adaptive
return schoolAnnotationView
}
return nil
}
Here's an image:
The problem is that your implementation of
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
does not look to see whether this annotation is your MIT annotation or your user location annotation. You need to start with an if condition and check that. If it's the user location, you return nil to get the default marker.

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

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.

Custom MKPointAnnotation isn't responding to user interaction

I am making a Swift application that uses MKPointAnnotations, and I recently ran into an issue where I needed to store metadata in my annotations, so I created the custom class below:
class BRETTFAnnotation: MKPointAnnotation {
var tag: Int64
var name: String
init(lat : Double, lon:Double, t : Int64, n: String) {
self.tag = t
self.name = n
super.init()
self.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon)
}
}
My MKAnnotationView viewfor MKAnnotation method is shown below:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let newAnnotation = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "reuse")
newAnnotation.canShowCallout = true
let right = self.button(title: "Yes")
right?.addTarget(self, action: #selector(clickedToConfirmNewPoint), for: .touchUpInside)
newAnnotation.rightCalloutAccessoryView = right
let left = self.button(title: "No")
left?.addTarget(self, action: #selector(clickedToCancelNewPoint), for: .touchUpInside)
newAnnotation.leftCalloutAccessoryView = left
return newAnnotation
}
The problem I am running into is when ever I click on my custom BRETTFAnnotation (which I add to my MKMapView) nothing happens. When I was just using the MKPointAnnotation (instead of the BRETTFAnnotation) when I clicked on the map the two buttons on the MKAnnotationView would show. I am trying to get the MKPinAnnotationView to show on touch using my BRETTFAnnotation instead of the MKPointAnnotation.
How can I continue to use my custom annotation and show the callout when the user clicks on the annotation at the same time?
Edit 1: Since it is probably useful the code below is how I make the annotation and add it to the mapView.
let location = gestureRecognizer.location(in: mapView)
let coordinate = mapView.convert(location,toCoordinateFrom: mapView)
print("adding lat,long \(coordinate.latitude),\(coordinate.longitude)")
lastPoint = BRETTFAnnotation(lat: coordinate.latitude, lon: coordinate.longitude, t: 1, n: "")
let annotationView = MKPinAnnotationView(annotation: lastPoint, reuseIdentifier: "reuse")
mapView.addAnnotation(lastPoint)
I fix this problem by making my BRETTFAnnotation a subclass of NSObject and MKAnnotation instead of MKPointAnnotation. Doing this allowed my custom class to receive user interaction and show the callouts.
When you use your own MKAnnoation you can handle your actions in didSelect. Just implement the following code.
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let yourAnnotation = view.annotation as? BRETTFAnnotation {
//handle your meta data or/and show UIViews or whatever
}
}
with
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
//getting called when you tap on map or on another annotation (not the selected annotation before)
//hide UIViews or do whatever you want
}
That does work for me:
class ViewController: UIViewController, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
print("didSelect")
if let annoation = view.annotation as? MyAnnoation {
print("metatag \(annoation.metaTag)")
}
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
print("didDeselect")
}
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
let annotation = MyAnnoation(n: "name", m: "metaTag")
annotation.coordinate = CLLocationCoordinate2D(latitude: 50.0, longitude: 8.0)
mapView.addAnnotation(annotation)
}
}
class MyAnnoation: MKPointAnnotation {
var name: String?
var metaTag: String?
init(n: String, m: String) {
self.name = n
self.metaTag = m
}
}