How I automatically rotate annotationView image following map rotating in MKMapKit? - swift

My code does rotate annotationViewImage, when the map rotated.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "mark")
annotationView.image = UIImage(named: "pinArrow")
let ang = mapView.camera.heading
let radi = Double.pi * Double(ang / 360)
let resultAng = 2 * Float(radi)
annotationView.transform = CGAffineTransform(rotationAngle: CGFloat(-resultAng))
annotationView.canShowCallout = true
return annotationView
}
above code, when the map rotates, do not implement the above method.
So add below code
func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
print("visible")
let anns = mapView.annotations
mapView.removeAnnotations(anns)
mapView.addAnnotations(anns)
}
and then, when map rotate, so do annotationView image.
But so a few some slow and loading time a few some long.
Because map moving delegate of above code, so many doing and repeat.
I seek a more simple way without loading slow.
How rotate sync map and annotationView image?

Related

MKPointAnnotation how to hide marker image

I have a MKPointAnnotation that I have setup and it displays where I would like it to however it comes with the default big red pin icon over the dot and I would like to hide that image and display nothing instead. I've tried working with the section I have commented below with "*****" and what I thought might work is setting view.image=nil and this did not change anything and I also tried view.frame.size=CGSize(width: 0, height: 0) which had no effect. Any other suggestions for how to accomplish this?
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotation = annotation
let identifier = "marker"
var view: MKMarkerAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
as? MKMarkerAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
//************************
view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.frame.size=CGSize(width: 0, height: 0)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
return view
}
This was actually easier to solve than I was expecting, all I needed to do was set the markerTintColor to a color with alpha of 0.
view.markerTintColor=UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0,alpha:0.5).withAlphaComponent(0)
is what solved it for me in this case.
Here is the apple documentation on the subject:
https://developer.apple.com/documentation/mapkit/mapkit_annotations/annotating_a_map_with_custom_data
They seem to recommend coding a custom Annotation as seen:
class SanFranciscoAnnotation: NSObject, MKAnnotation {
// This property must be key-value observable, which the `#objc dynamic` attributes provide.
#objc dynamic var coordinate = CLLocationCoordinate2D(latitude: 37.779_379, longitude: -122.418_433)
// Required if you set the annotation view's `canShowCallout` property to `true`
var title: String? = NSLocalizedString("SAN_FRANCISCO_TITLE", comment: "SF annotation")
// This property defined by `MKAnnotation` is not required.
var subtitle: String? = NSLocalizedString("SAN_FRANCISCO_SUBTITLE", comment: "SF annotation") }
You can try creating a custom annotation in this way with a clear color or with a size of (0,0). Assign your view to the custom annotation you code.
var view: SanFranciscoAnnotation
If this doesn't work, I will dig up some old code from an app I wrote with custom annotations and share that.

annotation Display Priority doesn't do what I expect, how to keep custom pin on screen

I use the code below on view did load to add a custom annotation icon for the map center that the user started at so that if they scroll away they can always see their starting point.
if let lat = curBlip.blip_lat, let lon = curBlip.blip_lon {
let mapCenter = CLLocationCoordinate2DMake(lat, lon)
let mapSpan = MKCoordinateSpanMake(0.01, 0.01)
let mapRegion = MKCoordinateRegionMake(mapCenter, mapSpan)
self.map.setRegion(mapRegion, animated: true)
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = "Your Blips Location"
annotation.subtitle = "Subtitle Placeholder"
self.map.addAnnotation(annotation)
When the view loads I load that annotation so its always first and I set a bool named "set" to true after the first annotation to ensure that it gets the custom icon. The issue I am having is that even though I have the annotation set to display priority required the annotation disappears when I move the map away. How can I make that annotation always persist or is there a better way to set a "this is where the map started" circle that doesn't go away?
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if set {
return nil
} else {
let view = MKAnnotationView(annotation: annotation, reuseIdentifier: "annotationId")
view.image = UIImage(named: "locationArea50")
view.canShowCallout = true
view.displayPriority = .required
set = true
return view
}
Then after I scroll the map away a little bit, I suspect scrolling far enough that the system has to make them reappear the annotation disappears. I assume this has to do with how the grouping of annotations works but that blue annotation is special and I want it to always be present, which is what I thought displayPriority did.
The default value of displayPriority is .required .
So for correct overlapping you need to downgrade priority of red annotations:
redAnnotation.displayPriority = .defaultHigh

Customizing annotation pins

I would like to customize my annotations (pins) so I would be able to change its image. I used a function (mapview) to change the picture, so I don’t know if I should use it again to change the picture of another group of pins; let´s say I have two locations, a restaurant and a hospital, so I want to put a plate and a red cross respectively. Here is my function code:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "identifier")
if annotationView == nil{
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "identifier")
annotationView!.canShowCallout = true
annotationView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
let pinImg = UIImage(named: "curz")
let size = CGSize(width: 50, height: 50)
UIGraphicsBeginImageContext(size)
pinImg!.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
annotationView!.image = resizedImage
}
else {
annotationView!.annotation = annotation
}
return annotationView
}
You can return different annotation views for different places by comparing the coordinate information. ex:
if ((annotation.coordinate.latitude == theCoordinate1.latitude)
&& (annotation.coordinate.longitude == theCoordinate1.longitude))
{
return someAnnotationView
}
follow this link Adding different images to different annotation views in xcode
The correct way to return different views for different annotation types (or groups of different annotation types) is to subclass the annotation and assign a different reuse identifier for each one (during init). This identifier is similar to the table view cell identifier and is used to reuse (rather than create from scratch) a cell when it scrolls out of sight. The same is true for annotations, rather than create a new one each time a 'discarded' annotation can be reused.
Then in mapview viewfor annotation you can test for annotation.identifier And assign the correct image.

How add description to MKPolyline & MKPolygon?

How add annotations to polyline and polygon in Swift & MapKit? By Point is simple.
S.,
I'm not sure what you're asking here, but I assume you want to display an annotation somewhere on the polyline.
First the intro how to get the the polyline:
So, lets assume you have an array of CLLocation objects that will draw the polyline on the map. We call this array of location objects: myLocations and it's of type [CLLocation]. Now somewhere in your app you call a method that creates the polyline, we call this method createOverlayObject(locations: [CLLocation]) -> MKPolyline.
Your call could look like this:
let overlayPolyline = createOverlayObject(myLocations)
The method you called then could look like this:
func createOverlayObject(locations: [CLLocation]) -> MKPolyline {
//This method creates the polyline overlay that you want to draw.
var mapCoordinates = [CLLocationCoordinate2D]()
for overlayLocation in locations {
mapCoordinates.append(overlayLocation.coordinate)
}
let polyline = MKPolyline(coordinates: &mapCoordinates[0], count: mapCoordinates.count)
return polyline
}
This was the first part, don't forget to implement the mapView(_: rendererForOverlay overlay:) to get the line rendered. this part could look something like this:
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
//This function creatss the renderer for the polyline overlay. This makes the polyline actually display on screen.
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = mapLineColor //The color you want your polyline to be.
renderer.lineWidth = self.lineWidth
return renderer
}
Now the second part get the annotation somewhere on the map. This is actually straight forward if you know what the coordinates are where you want to put your annotation. creating and displaying the annotation is straightforward again, assuming you have defined a map view called myNiceMapView:
func createAnnotation(myCoordinate: CLLocationCoordinate2D) {
let myAnnotation = MKPointAnnotation()
myAnnotation.title = "My nice title"
startAnnotation.coordinate = myCoordinate
self.myNiceMapView.addAnnotations([myAnnotation])
}
Don't forget to implement mapView(_: MKMapView, viewForAnnotation annotation:) -> MKAnnotationView? method, which might look like:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
//This is the mapview delegate method that adjusts the annotation views.
if annotation.isKindOfClass(MKUserLocation) {
//We don't do anything with the user location, so ignore an annotation that has to do with the user location.
return nil
}
let identifier = "customPin"
let trackAnnotation = MKAnnotationView.init(annotation: annotation, reuseIdentifier: identifier)
trackAnnotation.canShowCallout = true
if annotation.title! == "Some specific title" { //Display a different image
trackAnnotation.image = UIImage(named: "StartAnnotation")
let offsetHeight = (trackAnnotation.image?.size.height)! / 2.0
trackAnnotation.centerOffset = CGPointMake(0, -offsetHeight)
} else { //Display a standard image.
trackAnnotation.image = UIImage(named: "StopAnnotation")
let offsetHeight = (trackAnnotation.image?.size.height)! / 2.0
trackAnnotation.centerOffset = CGPointMake(0, -offsetHeight)
}
return trackAnnotation
}
Now the challenges is finding the right coordinate where to put your annotation. I can't find anything better than that you have a CLLocationCoordinate2D that references the location you want to put the annotation. Then with a for-in loop find the location where you want to put your annotation, something like this:
for location in myLocations {
if (location.latitude == myReferenceCoordinate.latitude) && (location.longitude == myReferenceCoordinate.longitude) {
self.createAnnotation(location: CLLOcationCoordinate2D)
}
}
Hope this answers your question.

iOS 8 SDK, Swift, MapKit Drawing a Route

I need to draw route between two points and I'm using MKDirectionsRequest for my purpose.
Getting a route is OK, but I have trouble with drawing it.
In iOS 8 SDK there's no function
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
There is only this one:
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer!
And for some reason, I can't understand why that method isn't called.
Delegate for MapView is set and MapKit is imported.
Here is the rendererForOverlay function that is implemented:
func rendererForOverlay(overlay: MKOverlay!) -> MKOverlayRenderer! {
println("rendererForOverlay");
var overlayRenderer : MKOverlayRenderer = MKOverlayRenderer(overlay: overlay);
var overlayView : MKPolylineRenderer = MKPolylineRenderer(overlay: overlay);
view.backgroundColor = UIColor.blueColor().colorWithAlphaComponent(0.5);
return overlayView;
}
The map view isn't calling your rendererForOverlay method because it is not named correctly.
The method must be named exactly:
mapView(mapView:rendererForOverlay)
but in your code it's named:
rendererForOverlay(overlay:)
In addition, you should check that the type of the overlay argument is MKPolyline and set the strokeColor of the polyline renderer.
(The view.backgroundColor in the existing code is actually changing the background color of the view controller's view -- not the polyline.)
Example:
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
println("rendererForOverlay");
if (overlay is MKPolyline) {
var pr = MKPolylineRenderer(overlay: overlay);
pr.strokeColor = UIColor.blueColor().colorWithAlphaComponent(0.5);
pr.lineWidth = 5;
return pr;
}
return nil
}