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.
Related
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)
I am currently working on creating callouts for annotations I have added to my mapview via MapKit. The annotations work out well but currently callouts aren't being displayed even though I am using the right code to enable them (I believe).
HERE is my viewFor annotation code block.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
if let annotation = annotation as? ClusterAnnotation {
let identifier = "cluster"
return mapView.annotationView(annotation: annotation, reuseIdentifier: identifier)
} else {
let identifier = "pin"
let annotationView = mapView.annotationView(of: MKMarkerAnnotationView.self, annotation: annotation, reuseIdentifier: identifier)
annotationView.isEnabled = true
annotationView.canShowCallout = true
annotationView.accessibilityLabel = "hi"
annotationView.isHidden = false
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.markerTintColor = UIColor.customBlue()
annotationView.glyphImage = UIImage(named: "person")
return annotationView
}
}
Extension code block for annotationView function.
extension MKMapView {
func annotationView<T: MKAnnotationView>(of type: T.Type, annotation: MKAnnotation?, reuseIdentifier: String) -> T {
guard let annotationView = dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) as? T else {
return type.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
annotationView.annotation = annotation
return annotationView
}
}
The annotation enlarges as I select it, and in the didSelect code block it runs the print statement I run through it. Not exactly sure what is going on that's not allowing the callout to show even though I've literally enabled just about everything.
Please Used This Code.
This code working fine for me.
This Code support Swift4 and Swift5.
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"
let nibName = "MyAnnotationView" //My XIB Name
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MyAnnotationView // get My XIB
var annotationView: MyAnnotationView?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MyAnnotationView {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MyAnnotationView(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: 75, height: 80)
let customView = annotationView.subviews.first as! MyAnnotationView
customView.frame = annotationView.frame
}
return annotationView
}
}
This Code OutPut :
Happy To Help You.
From the source code documentation:
// If YES, a standard callout bubble will be shown when the annotation is selected.
// The annotation must have a title for the callout to be shown.
#property (nonatomic) BOOL canShowCallout;
Without the Title value being set the other items will not show up.
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? {
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
}
I am very new to swift (coming from python) I am struggling with creating a MKAnnotationView. I followed a tutorial by ray wenderlich but the code appears to be outdated. The function call does not seem to work or produce the "i" intended to be in the annotation of the pin. Here is the code I am currently using:
import MapKit
extension ViewController: MKMapViewDelegate {
// 1
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if let annotation = annotation as? Artwork {
let identifier = "pin"
var view: MKPinAnnotationView
if let dequeuedView = MapView.dequeueReusableAnnotationView(withIdentifier: identifier)
as? MKPinAnnotationView { // 2
dequeuedView.annotation = annotation
view = dequeuedView
} else {
// 3
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
let button = UIButton(type:.detailDisclosure)
view.rightCalloutAccessoryView = button as UIView
}
return view
}
return nil
}
}
I am running Xcode 8.1, any help will be greatly appreciated.
Am using Xcode 8.1 and Swift 3.0 . For Some reason Delegate methods are not firing until you set delegate in storyboard:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if annotation is MKUserLocation {return nil}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
let calloutButton = UIButton(type: .DetailDisclosure)
pinView!.rightCalloutAccessoryView = calloutButton
pinView!.sizeToFit()
}
else {
pinView!.annotation = annotation
}
return pinView
}
for button Action
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
print("button tapped")
}
}
I am attaching sample project for your issue
Map Sample Project Swift 3. 0 Xcode 8.1