I am trying to add custom images to my mapView but am not having any luck so far. I keep getting a SIGABRT error when the map is loaded. here is my code:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKindOfClass(NiteOwlAnnotations) {
let reuseId = "test"
var annoView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if annoView == nil {
annoView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
annoView?.image = UIImage(named: "bar-map-icon")
annoView?.canShowCallout = true
} else {
annoView?.annotation = annotation
}
return annoView
/* let annoView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "Default")
// change color of pins.
annoView.pinTintColor = UIColor.blackColor()
annoView.animatesDrop = false
annoView.image = UIImage(named: "bar-map-icon")
annoView.backgroundColor = UIColor.clearColor()
annoView.canShowCallout = true
return annoView
*/
} else if annotation.isKindOfClass(MKUserLocation) {
return nil
}
return nil
}// end func
//--------------------------------------------------------------
func createAnnotationForLocation(location: CLLocation) {
let niteOwl = NiteOwlAnnotations(coordinate: location.coordinate)
map.addAnnotation(niteOwl)
}
I have looked for more information on this and still no luck yet:
Change pin image on MKMapView in Swift
Related
I'm trying to present a view controller with an object within annotation didSelect function. But it's returning nil when I try to print the post.id within the newly presented viewcontroller. If I print post.id within the addAnnotation function it returns fine.
func addAnnotations(post: Post, title: String?, locationName: String?, coords: [CLLocation]) {
for coord in coords {
let CLLCoordType = CLLocationCoordinate2D(latitude: coord.coordinate.latitude,
longitude: coord.coordinate.longitude);
let anno = MKPointAnnotation()
anno.coordinate = CLLCoordType
if title == "" {
anno.title = locationName
} else {
anno.title = title
}
mapView.addAnnotation(anno)
}
}
let activityPreview = ActivityPreviewLauncher()
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
//Returns an error
guard let post = view.annotation?.post else { return }
activityPreview.showActivityPreview()
activityPreview.homeController = self
activityPreview.post = post
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation{
return nil
} else{
let pinIdent = "Pin"
var pinView: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: pinIdent) as? MKPinAnnotationView {
dequeuedView.annotation = annotation
pinView = dequeuedView
} else {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: pinIdent)
}
return pinView
}
}
Value of type 'MKAnnotation' has no member 'post'. You have to subclass MKPointAnnotation
class MyAnno: MKPointAnnotation {
var post: Post? = nil
}
in func addAnnotations override your code to
let CLLCoordType = CLLocationCoordinate2D(latitude: coord.coordinate.latitude,
longitude: coord.coordinate.longitude);
let anno = MyAnno()
anno.coordinate = CLLCoordType
anno.post = post
if title == "" {
anno.title = locationName
} else {
anno.title = title
}
mapView.addAnnotation(anno)
then in didSelect check type of your annotation
guard
let anno = view.annotation as? MyAnno,
let post = anno.post
else { return }
smth like this
I am attempting to show multiple objects on my map annotations from Care Data. Currently, I can get title and subtitle to show info with this function
func getData() -> [MKAnnotation]? {
do {
storedLocations = try context.fetch(Fish.fetchRequest())
var annotations = [MKAnnotation]()
for storedLocation in storedLocations {
let newAnnotation = MyAnnotation(coordinate: CLLocationCoordinate2D(latitude: storedLocation.latitude, longitude: storedLocation.longitude))
newAnnotation.coordinate.latitude = storedLocation.latitude
newAnnotation.coordinate.longitude = storedLocation.longitude
newAnnotation.title = storedLocation.species
newAnnotation.subtitle = storedLocation.length
newAnnotation.newTime = storedLocation.time
newAnnotation.newBait = storedLocation.bait
newAnnotation.newNotes = storedLocation.notes
newAnnotation.newWaterTempDepth = storedLocation.water
newAnnotation.newWeatherCond = storedLocation.weather
// print(storedLocation.length)
let newLength = storedLocation.length
let newTime = newAnnotation.newTime
// let newBait = storedLocation.bait
// let newNotes = storedLocation.notes
// let newWaterTempDepth = storedLocation.water
// let newWeatherCond = weatherCond
myLength = newLength!
myTime = newTime!
//
annotations.append(newAnnotation)
}
return annotations
}
catch {
print("Fetching Failed")
}
return nil
}
I am trying to show all of the storedLocation data on the annotation.
By using this extension I found here I am able to add multiple lines to the annotation.
However it is not from the info stored in core data. This is how I set up the func for the extension
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard !(annotation is MKUserLocation) else {
return nil
}
let reuseIdentifier = "pin"
var annotationView = map.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
annotationView?.canShowCallout = true
annotationView?.loadCustomLines(customLines: [myLength, myTime])
} else {
annotationView?.annotation = annotation
}
annotationView?.image = UIImage(named: "fish")
return annotationView
Here is the extension.
extension MKAnnotationView {
func loadCustomLines(customLines: [String]) {
let stackView = self.stackView()
for line in customLines {
let label = UILabel()
label.text = line
stackView.addArrangedSubview(label)
}
self.detailCalloutAccessoryView = stackView
}
private func stackView() -> UIStackView {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.alignment = .fill
return stackView
}
}
here is my annotation
import UIKit
import MapKit
class MyAnnotation: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
var newLength: String?
var newTime: String?
var newBait: String?
var newNotes: String?
var newWaterTempDepth: String?
var newWeatherCond: String?
var detailCalloutAccessoryView: UIStackView?
init(coordinate: CLLocationCoordinate2D){
self.coordinate = coordinate
}
}
When I run I get
this
I am very new to programming and would greatly appreciate any help, and also an explanation of what I am doing wrong Thank you in advance
'!' means Force Unwrapping
myLength = newLength! <- CRASH
As 'newLength' is nil here, you will get a crash on the line where you force unwrap it.
e.g. You can fix the error
myLength = newLength ?? 0
myTime = newTime ?? 0
See also What does "fatal error: unexpectedly found nil while unwrapping an Optional value" mean?
So I have multiple locations stored in my database, using Geofire. I want to centre the map on a specific one.
I want to know if it is possible to access the firebase database to only retrieve the coordinates saved by GeoFire as a variable so that I can create a CLLocation and centre the map on this one location. Here is my code.
I call getJobLocation in my viewDidLoad. This is where I want to grab the job coordinates from Firebase and then call the other methods i.e. showJobsOnMap using these coordinates
func getJobLocation(jobID: String) {
...
}
func centreMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, 2000, 2000)
mapView.setRegion(coordinateRegion, animated: true)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annoIdentifier = "job"
var annotationView: MKAnnotationView?
if annotation.isKind(of: MKUserLocation.self) {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "User")
annotationView?.image = UIImage(named:"currentLocationPin")
} else if let deqAnno = mapView.dequeueReusableAnnotationView(withIdentifier: "job") {
annotationView = deqAnno
annotationView?.annotation = annotation
} else {
let MKAV = MKAnnotationView(annotation: annotation, reuseIdentifier: annoIdentifier)
MKAV.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView = MKAV
}
if let annotationView = annotationView, let _ = annotation as? JobAnnotation {
let postsRef = self.dataBaseRef.child("jobs")
postsRef.observe(.value, with: { (posts) in
for _ in posts.children {
annotationView.canShowCallout = true
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.image = UIImage(named: "jobPin")
}
})
}
return annotationView
}
func showJobsOnMap(location: CLLocation) {
let circleQuery = geoFire!.query(at: location, withRadius: 2.5)
_ = circleQuery?.observe(GFEventType.keyEntered, with: { (key, location) in
if let key = key, let location = location {
if key == self.job.postID {
let postsRef = self.dataBaseRef.child("jobs").child(key)
postsRef.observe(.value, with: { (posts) in
for _ in posts.children {
let post = Post(snapshot: posts )
let jobDate = post.postDateTime
let price = post.price
let anno = JobAnnotation(coordinate: location.coordinate, jobID: key, jobDate: jobDate, title: "£\(price) - \(jobDate)")
self.mapView.addAnnotation(anno)
}
})
}
}
})
}
I am trying to use two different pins on a map, this is the code I have:
func mapView (_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
//avoid for user location
if (annotation is MKUserLocation) {
return nil
}
let reuseId = "annId"
var anView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if anView == nil {
if(annotation.subtitle! == "Offline"){
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView!.image = UIImage(named:"offIceCream.pdf")!
anView!.canShowCallout = true
}
if(annotation.subtitle! == "Online"){
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView!.image = UIImage(named:"onIceCream.pdf")!
anView!.canShowCallout = true
}
} else {
anView!.annotation = annotation
}
return anView
}
The problem is that it is not setting the correct icon depending on the subtitle of the annotation. For some reason sometimes it works correctly and sometimes it works the opposite way (sets the online icon on the offline annotations and viceversa). Any idea why this is happening?.
Thanks in advance!
Because you forget to update the .image of already queued annotation views:
if anView == nil {
...
}
else {
anView!.annotation = annotation
if (annotation.subtitle! == "Offline") {
anView!.image = UIImage(named:"offIceCream.pdf")!
}
else if (annotation.subtitle! == "Online") {
anView!.image = UIImage(named:"onIceCream.pdf")!
}
}
A clearer way of writing the entire logic would be:
func mapView (_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if (annotation is MKUserLocation) {
return nil
}
var anView = mapView.dequeueReusableAnnotationView(withIdentifier: "annId")
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: "annId")
}
else {
anView?.annotation = annotation
}
anView?.canShowCallout = true
if (annotation.subtitle! == "Offline") {
anView?.image = UIImage(named: "offIceCream.pdf")
}
else if (annotation.subtitle! == "Online") {
anView?.image = UIImage(named: "onIceCream.pdf")
}
return anView
}
How can I set my map MKAnnotation CalloutAccessoryView to show the selected image mapped out from my realm database (with icons and buttons on either side, as the left and right calloutAccessory?). I also need it to recognize the class/member for other functions on my MapViewController here? The mapping of the data and left and right callout accessories are working fine, but I can't figure out how to add the selected image, so it show up as a large poster-style image on the callout (with the small icons & buttons on either side).
The image is SpecimenAnnotation.objectPoster
The SpecimenAnnotation (which is working fine to map the data) and 'objectPoster' value is the selected (and mapped) Annotation from the class Specimen, included in my file Specimen.swift, like this;
class Specimen: Object{
dynamic var name = ""
dynamic var objectPoster = ""
dynamic var latitude = 0.0
dynamic var longitude = 0.0
dynamic var created = NSDate()
dynamic var category: Category!
}
In my MapViewController, on the line 'annotationView.detailCalloutAccessoryView = UIImageView(image: SpecimenAnnotation.objectPoster)' I am getting a red alert error "instance member 'objectPoster' cannot be used on type 'Specimen Annotation'
I am a beginner. Would greatly appreciate your suggestions. Any ideas?
Here are the code blocks regarding this data and error:
// Create annotations for each one
for specimen in specimens { // 3
let coord = CLLocationCoordinate2D(latitude: specimen.latitude, longitude: specimen.longitude);
let specimenAnnotation = SpecimenAnnotation(coordinate: coord, poster: specimen.objectPoster, subtitle: specimen.category.name, specimen: specimen)
mapView.addAnnotation(specimenAnnotation) // 4
}
}
}
//MARK: - MKMapview Delegate
extension MapViewController: MKMapViewDelegate {
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
guard let subtitle = annotation.subtitle! else { return nil }
if (annotation is SpecimenAnnotation) {
if let annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(subtitle) {
return annotationView
} else {
let currentAnnotation = annotation as! SpecimenAnnotation
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: subtitle)
annotationView.image = UIImage(named:"IconStudent")
annotationView.enabled = true
annotationView.canShowCallout = true
// ERROR message here>
annotationView.detailCalloutAccessoryView = UIImageView(image: SpecimenAnnotation.objectPoster)
// right accessory view
let infoButton = UIButton(type: UIButtonType.InfoDark)
// Left accessory view
let image = UIImage(named: "button50")
let specimenButton = UIButton(type: .Custom)
specimenButton.frame = CGRectMake(0, 0, 30, 30)
specimenButton.setImage(image, forState: .Normal)
annotationView.rightCalloutAccessoryView = infoButton
annotationView.leftCalloutAccessoryView = specimenButton
if currentAnnotation.title == "Empty" {
annotationView.draggable = true
}
return annotationView
}
}
return nil
}
I solved it, by pulling the value into the currentAnnotation, and adding it in a UIImageView on the DetailCalloutAccessoryView
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
guard let subtitle = annotation.subtitle! else { return nil }
if (annotation is SpecimenAnnotation) {
if let annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(subtitle) {
return annotationView
} else {
let currentAnnotation = annotation as! SpecimenAnnotation
let currentImage = currentAnnotation.objectPoster!
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: subtitle)
}
annotationView.enabled = true
annotationView.canShowCallout = true
// right accessory view
let calloutInfoButton = UIButton(type: UIButtonType.InfoDark)
// Left accessory view
let calloutLeftImage = UIImage(named: "button50p")
let calloutLeftButton = UIButton(type: .Custom)
calloutLeftButton.frame = CGRectMake(0, 0, 30, 30)
calloutLeftButton.setImage(calloutLeftImage, forState: .Normal)
//show Image on callout Accessory
let url = NSURL(string: currentImage)
let data = NSData(contentsOfURL: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check
//annotationView.image = UIImage(data: data!)
let calloutImage = UIImage(data:data!)
annotationView.detailCalloutAccessoryView = UIImageView(image: calloutImage)
annotationView.rightCalloutAccessoryView = calloutInfoButton
annotationView.leftCalloutAccessoryView = calloutLeftButton
if currentAnnotation.title == "Empty" {
annotationView.draggable = true
}
return annotationView
}
}
return nil
}