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
}
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 have built an app that has a split view: a table and a map. I want to change the marker image according to the type of animal. I add the code below but the marker hasn't changed.
LocationListController.swift
override func viewDidLoad() {
super.viewDidLoad()
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Animal")
do{
animalList = try managedObjectContext.fetch(fetchRequest) as! [Animal]
}
catch {
fatalError("Failded to fetch teams: \(error)")
}
let location: FencedAnnotation = FencedAnnotation (newTitle: "*****", newSubtitle: "*****", lat: -33, long: 145.045374, newType: "Start", newPhoto: "none")
filteredList = animalList
for animal in filteredList{
let annotation = FencedAnnotation(newTitle: animal.name!, newSubtitle: animal.animaldescription!, lat: animal.latitude, long: animal.longtitude, newType: animal.type!, newPhoto: animal.photo!)
self.locationList.add(annotation)
self.mapViewController?.addAnnotation(annotation: annotation)
}
geoLocation = CLCircularRegion(center: location.coordinate, radius: 500, identifier: location.title!)
geoLocation!.notifyOnExit = true
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.stopMonitoring(for: geoLocation!)
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search Animal"
navigationItem.searchController = searchController
tableView.reloadData()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
mapView.swift
func addAnnotation(annotation: FencedAnnotation) {
self.mapView.addAnnotation(annotation)
}
func removeAnnotation(annotation: FencedAnnotation) {
self.mapView.removeAnnotation(annotation)
}
func focusOn(annotation: MKAnnotation) {
self.mapView.centerCoordinate = annotation.coordinate
self.mapView.selectAnnotation(annotation, animated:true)
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let reuseIdentifier = "pin"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
annotationView?.canShowCallout = true
} else {
annotationView?.annotation = annotation
}
let customPointAnnotation = annotation as! FencedAnnotation
if customPointAnnotation.type == "cat"{
annotationView?.image = #imageLiteral(resourceName: "cat")
}
if customPointAnnotation.type == "dog"{
annotationView?.image = #imageLiteral(resourceName: "dog")
}
if customPointAnnotation.type == "fish"{
annotationView?.image = #imageLiteral(resourceName: "fish")
}
if customPointAnnotation.type == "monkey"{
annotationView?.image = #imageLiteral(resourceName: "monkey")
}
if customPointAnnotation.type == "fish"{
annotationView?.image = #imageLiteral(resourceName: "bird")
}
if customPointAnnotation.type == "fish"{
annotationView?.image = #imageLiteral(resourceName: "mouse")
}
else{
annotationView?.image = #imageLiteral(resourceName: "placeholder")
}
annotationView?.canShowCallout = true
return annotationView
}
I fetch the list from core data and use a customer MKAnnotation to add the annotation.
I have news objects and I want to customize the news annotation according to the news category. Without the switch statement, if I just write annotationView?.image = UIImage(named: "ic_sports"), all annotations; images shows the sport image. How can I get the news category id for that annotation so I can change the annotation image according to that id?
The console prints Log annotation is news for all of the annotations.
News:
class News: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
var categoryId: Int
var title: String?
// Other variables and init function ...
func getCategoryId() -> Int {
return categoryId
}
}
MapViewController:
function parse(json: JSON) {
// ...
let news = News(categoryId: data["category_id"].intValue,
title: data["title"].stringValue,
coordinate: coordinate)
self.mapView.addAnnotation(news)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is News {
print("Log annotation is news")
} else {
print("Log annotation NOT news")
}
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "newsAnnotationView")
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "newsAnnotationView")
}
switch(annotation.getCategoryId()) {
case 1: // Other
annotationView?.image = UIImage(named: "ic_other")
break;
case 2: // sports
annotationView?.image = UIImage(named: "ic_sports")
break;
case 3: // education
annotationView?.image = UIImage(named: "ic_education")
break;
case 4: // crime
annotationView?.image = UIImage(named: "ic_crime")
break;
case 5: // health
annotationView?.image = UIImage(named: "ic_health")
break;
default:
break;
}
annotationView?.canShowCallout = true
return annotationView
}
I have solved this by creating a news object and give it the value of annotation as News, then use news.getCategoryId()
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let news = annotation as! News
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "newsAnnotationView")
if annotationView == nil {
annotationView = MKAnnotationView(annotation: news, reuseIdentifier: "newsAnnotationView")
}
switch(news.getCategoryId()) {
case 1: // Other
annotationView?.image = UIImage(named: "ic_other")
break;
case 2: // sports
annotationView?.image = UIImage(named: "ic_sports")
break;
case 3: // education
annotationView?.image = UIImage(named: "ic_education")
break;
case 4: // crime
annotationView?.image = UIImage(named: "ic_crime")
break;
case 5: // health
annotationView?.image = UIImage(named: "ic_health")
break;
default:
break;
}
annotationView?.canShowCallout = true
return annotationView
}
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 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