How can I make an annotation like maps app has - ios

I have an app with map and a lot of annotations that are made with custom image, and my problem is that when i drag the map or zoom the annotation shows another point (lat , lon).
So I was wondering because I see this bug in other applications too, is there any way to fix it (to be as the maps annotation that always shows the right lat and lon).
This is the code I use for annotation:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
var reuseId = ""
if annotation is FBAnnotationCluster {
reuseId = "Cluster"
var clusterView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
clusterView = FBAnnotationClusterView(annotation: annotation, reuseIdentifier: reuseId, configuration: FBAnnotationClusterViewConfiguration.default())
return clusterView
}else{
reuseId = "pin"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView!.image = UIImage(named: "locat.png")
}
else {
annotationView!.annotation = annotation
}
let cpa = annotation as! FBAnnotation
annotationView!.image = nil
let image = UIImage(named:cpa.nameImage)
annotationView!.image = image
return annotationView
}
}
If you have any question about what I'm asking or anything else please feel free to leave a comment.
Thanks in advance!
EDIT
I upload it an video showing my problem
EDIT2
And here is a video from maps that showing that their annotation is not losing its lat and lon.

Your problem is the MKAnnotationView.centerOffset you need to adjust this property in the func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
As is defined in MKAnnotationView Documentation
centerOffset Property The offset (in points) at which to display the
view.
var centerOffset: CGPoint
Discussion By default, the
center point of an annotation view is placed at the coordinate point
of the associated annotation. You can use this property to reposition
the annotation view as needed. This x and y offset values are measured
in points. Positive offset values move the annotation view down and to
the right, while negative values move it up and to the left.
Something like this
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView!.image = UIImage(named: "locat.png")
//this is an example you must to get in account the form of your current view and what you want, I think your values are CGPoint(x: 0.5, y: 1)
annotationView?.centerOffset = CGPoint(x: 0, y: -((UIImage(named: "locat.png")?.size.height)!/2))
}
else {
annotationView!.annotation = annotation
}
Hope this helps you

You are not losing the lat long but your lat long are on center of your annotation image you have to change anchorPoint.
annotationView?.layer.anchorPoint = CGPoint(x: 0.5, y: 1.0)
in
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

Related

How to use a custom XIB for MKAnnotationView?

I want to use a custom XIB to use instead of MKAnnotation View, but I can't find any sources online that can help me.
To be clear, I don't want a callout, I don't want an image in place of the pin, I want to replace the pin completely with an xib.
For reference, I want my pins to look something like this -
https://i.stack.imgur.com/gbdFI.png
This would be the pin design, with the image in the middle changing for all the users.
I've never worked on MKMapView and I've just displayed MKPinAnnotationView on the map after watching some tutorials about it.
Can anybody please guide me on how to achieve this
EDIT:
Using Alexander Nikolaychuk's link, I was able to create MKPinAnnotationViews using a XIB.
For reference, here is the code that worked for me :
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "CustomPinAnnotation"
let nibName = "CustomPinAnnotation"
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! CustomPinAnnotation
var annotationView: CustomPinAnnotation?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? CustomPinAnnotation {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = CustomPinAnnotation(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.addSubview(viewFromNib)
annotationView = av // extend scope to be able to return at the end of the func
}
// after we manage to create or dequeue the av, configure it
if let annotationView = annotationView {
annotationView.canShowCallout = true // callout bubble
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
let customView = annotationView.subviews.first as! CustomPinAnnotation
customView.frame = annotationView.frame
customView.profilePicImageView.image = UIImage(named: "dummy4")
}
return annotationView
}
}
Now, there are 2 issues left in my app
I have all the data that I want to show on the map in an array modal. How can I use the profile picture URL from my modal and display it onto the imageView in the annotation View? Since viewFor annotation does not iterate and doesn't have any index value, its challenging for me to pass my modal here.
Even though my xIB looks like this :
https://i.stack.imgur.com/Wg4ZU.png
It looks like this on the MapView: https://i.stack.imgur.com/e7CAr.jpg
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MKPinAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationIdentifier") as? MKPinAnnotationView {
//Here you can setup
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
}
You don't need to initialize in on your own. It uses a custom CollectionViewCells. Try to use this example. (This is a delegate function FYI)

How to show custom annotation even if it's on the user's current location with Swift

I've tow UIViewControllers the first one displays an MKMapKitView with annotations and the user's current location and in the second viewController I'll be constructing the annotations that will be displayed to the first viewController.
I found out that if the user's location and the annotation are the same the mapView will only display the user's current location.
Here's my code to create an annotation:
func addAnnotiation(for locationCoordinate: CLLocationCoordinate2D) {
let annotationPoint = MKPointAnnotation()
annotationPoint.coordinate = locationCoordinate
mapView.addAnnotation(annotationPoint)
}
My MKMapViewDelegate method:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "Annotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = true
} else {
annotationView!.annotation = annotation
}
return annotationView
}
How can I solve this problem so if the annotation and the user's location are the same it will show the annotation also and not only the user's current location?
The problem was very simple. I somehow forgot to make mapView.delegate = self. Everything is working just fine right now!

How to edit glyph image icons Swift 4?

I am new to swift 4.2, and I was wondering can I edit the glyph-image pin icon in swift 4. For example, instead of the red marker with the pin could it be an as red marker with fast food icon? Thanks for the help.
Here is my code:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
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.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
return view
}
You can't edit it, but you can certainly replace it. Just set the MKMarkerAnnotationView's glyphImage.

Autosize annotation.title [duplicate]

I have a mapView with annotations displaying titles and subtitles. The subtitles are sometimes longer than the width of the annotation, so I am wondering if i can make them multiline?
It's coded like this so far:
func annotate(newCoordinate, title: String, subtitle: String) {
let annotation = MKPointAnnotation()
annotation.coordinate = newCoordinate
annotation.title = title
annotation.subtitle = subtitle
self.map.addAnnotation(annotation)
}
Then i have a few options set in
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {...}
which are not relevant here.
Is it posible to make a custom annotation view?
I've tried a couple of things, but nothing worked. The closest I can get is adding a button to display the longer subtitle separately, but i'd rather have it inside the annotation.
Is it possible?
I figured it out, I added a label in viewForAnnotation and it just worked
¯\_(ツ)_/¯
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
}
else {
pinView!.annotation = annotation
}
//THIS IS THE GOOD BIT
let subtitleView = UILabel()
subtitleView.font = subtitleView.font.fontWithSize(12)
subtitleView.numberOfLines = 0
subtitleView.text = annotation.subtitle!
pinView!.detailCalloutAccessoryView = subtitleView
return pinView
}

Weird custom annotation behaviour when adding/removing annotations

I am in the middle of developing an application for iOS and have come across an issue with custom annotations on my map view.
Here is the idea:
1) I load in the users location to the map, from this location I send a request to my back end server to retrieve all nearby points of interest.
2) I have a custom annotation as follows:
class CustomPointAnnotation: MKPointAnnotation {
var imageName: String?
var activeImageName : String?
var trainCrossingId : Int?
var notificationCount : UILabel = UILabel()
var labelIsHidden : Bool = true
}
when I add the annotations onto the map, I am dynamically setting the notificationCount label to use data that I have retrieved from my Firebase Database.
3) A user is able to increase/decrease the size of the radius around their current location. With this new radius selected, I use mapView.removeAnnotations(mapView.annotations) to remove all annotations and then re-add these annotations with the new data retrieved from the server using the selected radius.
The Issue:
When initially loading the view, the annotations are working as expected with the correct labels set etc.
However, once a user updates the radius and I remove/add the previous annotations, the annotations and their corresponding labels are not showing as expected.
For example, this is what the map looks like when first entering into the view:
And then once I change the size of the radius:
The expected data is what is shown in the initial image (when first entering the view), but when the radius is updated the z-value of my annotations are not respected, and notificationCount is not correct (eg, the third point found in the second diagram should not have a label). This is weird as I have set up print statements and break points to monitor each annotation in func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {...} and the values are correct and what I expect, however the view is not displaying these values.
Any ideas as to what might be going wrong here? Thanks in advance!
Edit to include the following mapView delegate:
// Custom annotation view
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if !(annotation is CustomPointAnnotation) {
return nil
}
let reuseId = "trainPin"
var anView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
}
else {
anView?.annotation = annotation
}
let cpa = annotation as! CustomPointAnnotation
let icon : UIImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 32, height: 32))
icon.image = UIImage(named:cpa.imageName)
icon.layer.zPosition = 1
anView?.rightCalloutAccessoryView = cpa.annotationButton
anView?.addSubview(cpa.notificationCount)
anView?.addSubview(icon)
anView?.frame = CGRect(x: 0, y:0, width:32, height:32)
anView?.canShowCallout = true
return anView
}
I believe that as anView will be reused, the view will have many subviews as you remove and add annotations. Try to remove the subviews first then add again:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if !(annotation is CustomPointAnnotation) {
return nil
}
let reuseId = "trainPin"
var anView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
}
else {
anView?.annotation = annotation
}
let cpa = annotation as! CustomPointAnnotation
let icon : UIImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 32, height: 32))
icon.image = UIImage(named:cpa.imageName)
icon.layer.zPosition = 1
anView?.rightCalloutAccessoryView = cpa.annotationButton
for view in anView?.subviews {
view.removeFromSuperView()
}
anView?.addSubview(cpa.notificationCount)
anView?.addSubview(icon)
anView?.frame = CGRect(x: 0, y:0, width:32, height:32)
anView?.canShowCallout = true
return anView
}

Resources