I'm trying to increase the size of a MKPointAnnotation after I touch it.
Right now I use the following code:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
print("annotation deselected")
if let garageAnnantion = view.annotation as? GarageAnnoation {
view.image = UIImage(named: "greenPinBig")
}
}
This works, however. It will make the view animate the size in a weird way.
Any idea of how I can fix this?
Related
Ok, so I have a MapKit app, and just finished setting up the MKAnnotationView stuff. My MKAnnotationView class looks like this:
class JumpSpotAnnotationView: MKMarkerAnnotationView {
override var annotation: MKAnnotation? {
willSet {
// Extra safe, making sure there's no errors
guard (newValue as? JumpSpotAnnotation) != nil else {
print("The JumpSpotAnnotation or JumpSpotAnnotationView has something wrong if you are reading this. (JumpSpotAnnotationView)")
return
}
// Setting up UI for the little Callout bubble that appears when you tap the annotation to see more info
canShowCallout = true
calloutOffset = CGPoint(x: 0, y: 0)
rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
//detailCalloutAccessoryView
markerTintColor = .blue
}
}
}
And my mapView viewFor function in my view controller looks like this:
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Make sure the annotation entering the screen is a JumpSpotAnnotation, and exists
guard let annotation = annotation as? JumpSpotAnnotation else {
print("The JumpSpotAnnotation or JumpSpotAnnotationView has something wrong if you are reading this. (mapView viewFor func)")
return nil
}
// Downcast the dequeued view as a JumpSpotAnnotationView, and make sure it has the same identifier as the registered JumpSpotAnnotationView above in viewDidLoad
let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: "JumpSpotAnnotation") as? JumpSpotAnnotationView
dequeuedView?.annotation = annotation
// Return annotation views here
return dequeuedView
}
}
Honestly, I put the guard let's in each of those, because I saw someone else do it that way and assumed it would be safer. I'm not really sure what their purpose is, other than making sure an annotation is actually entering the view, and is of the right type of annotation that I specified (I think it does that, at least).
Anyway, when I actually add the annotations by pressing a button in my app, everything works flawlessly, exactly as I want it to, but the print statements inside of the guard let's are showing up in the debugger. I have no idea what's causing them, nor any idea why my code is still working after they've triggered, when the fact that the guard let's have executed should stop the code below them from executing, and mess up my app. Can anyone offer ideas or explanations? I should add that the print statement from the mapView viewFor func appears once, as soon as the app loads up, then, the print statement from my JumpSpotAnnotationView appears each time I add an annotation.
I want to make sure I'm not missing some huge error that I'll regret down the line.
There are some MKAnnotations that are not the class of JumpSpotAnnotation when func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? works. It must be from MKMapView so dont worry about it. Your guard let works well.
When I place a pin on top of an overlay, the title of the pin appears to be obscured. When the pin is placed over a point that is not on top of the overlay, the title appears below the pin.
The string lastCoordName is the title of the pin; this string is passed from the previous view controller to the current one (shown below).
Here are some images to describe what I'm talking about...
Above image shows the title right below the pin ("360") when the pin is off the overlay.
But the title goes away when the pin is moved to the correct position on top of the overlay.
The title is still shown when the polyline is drawn horizontally and terminates off the overlay, so the problem isn't that the line is covering the title.
Here is the custom pin class:
class CustomPin : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
init(coordinate: CLLocationCoordinate2D, title: String) {
self.coordinate = coordinate
self.title = title
super.init()
}
}
The relevant part of viewDidLoad()
let pin = CustomPin(coordinate: endPoint, title: lastCoordName)//uses lastCoordName from previous vc (insead of looking up name of last node given coord)
mapView.addAnnotation(pin)
mapView.selectAnnotation(pin, animated: true)
And the mapViewController extension
extension mapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is SchoolMapOverlay {
return SchoolMapOverlayView(overlay: overlay, overlayImage: #imageLiteral(resourceName: "GBSF1"))
} else if overlay is MKPolyline {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.strokeColor = UIColor(red:0.2, green:0.48, blue:1.00, alpha:1.0)
lineView.lineWidth = 10.0
return lineView
}
return MKOverlayRenderer()
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
pin.canShowCallout = true
return pin
}
}
Is the overlay covering the pin title? If so, why are the pin and its title not on the same level (above the overlay)?
You can set the layer the overlay is drawn on with
mapView.add(tileOverlay, level: .aboveRoads)
This will draw it above roads but below labels. Problem is, it's below all labels; so depending on what you're showing in your map, you're gonna have to play with labels a bit.
MKOverlayLevel Documentation
So I got an MKCirlce (MKOverlay), and I've added it like this:
self.current_location_overlay = MKCircle(center: self.current_location!, radius: 200)
self.mk_map_view.add(self.current_location_overlay!)
I've set the map delegate and added this MKMapViewDelegate method:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let overlay = overlay as? MKCircle {
let circle_renderer = MKCircleRenderer(circle: overlay)
circle_renderer.fillColor = UIColor.red.withAlphaComponent(0.2)
circle_renderer.setNeedsDisplay()
return circle_renderer
} else {
return MKOverlayRenderer(overlay: overlay)
}
}
Tho the overlay is not appearing at start, only after panning or zooming the map it appears.
Seems like others have had this problem, tho no solution on this post seems to help.
Dose anyone know how to solve this?
I have been stuck on this issue for the past day. I have created a custom MKAnnotation subclass to display various custom pins on an MKMapView. I recursively call a function that keeps animating these pins around the map. My goal is to stop all of these animations in place when the user taps on a button. I have tried
self.view.layer.removeAllAnimations()
and
self.map.layer.removeAllAnimations()
and other hacky solutions, but none seem to work.
Below is the code that creates the animation/pin movement
func animate(duration:Double, newLocation:CLLocationCoordinate2D){
UIView.animate(withDuration: duration, animations: {
self.coordinate = newLocation
}) { (done) in
self.finished_segment()
}
}
Any suggestions are much appreciated.
For anyone stuck on this issue. The problem was that I had to remove the animation from the MKAnnotationView associated with the annotation. I basically created a member variable in the custom class that I set in the mapView annotation delegate method as seen below.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "annotationView") ?? MKAnnotationView()
if let a = annotation as? Alien{
annotationView.image = a.image
a.annotationView = annotationView
}
return annotationView
}
And to remove the animation from the map. Simply call
self.annotationView.layer.removeAllAnimations()
How do you always show the annotation callouts, i.e. don't hide them when you tab the map view?
Resetting the annotations also will bring the callout to view state true.
[mapView removeAnnotation: currentMarker];
[mapView addAnnotation:currentMarker];
The callout is shown when an MKAnnotationView is selected and the view's canShowCallout property is set to YES.
It is then hidden when that MKAnnotationView is deselected. This occurs by tapping another annotation view, or by tapping outside of the currently selected annotation view.
As the delegate of MKMapView (conforming to MKMapViewDelegate), you are told when an annotation view is selected and deselected, but it's too late to do anything about it.
If you want to not deselect an annotation view, you should subclass MKAnnotationView and override the setSelected:animated: method and stop the annotation view from being deselected.
Thanks, #Zumry Mohammed for this idea. This solution in swift works for me:
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
guard let ann = view.annotation else {return}
mapView.removeAnnotation(ann)
mapView.addAnnotation(ann)
mapView.selectAnnotation(ann, animated: false)
}
I just set isSelected property to true on viewFor annotation method and that is all.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let annotationV = MKAnnotationView(annotation: annotation, reuseIdentifier: nil)
annotationV.image = UIImage(named: "ZeusSurveyMarkerTaskIcon", in: Bundle(for: ZsurveysGeofenceLocationMapView.self), compatibleWith: nil)
annotationV.canShowCallout = true
annotationV.isSelected = true
return annotationV
}