I'm trying to show a set of parse objects in a MKMapView as Annonations. This seem to work, the problem is that the objects images are swapped, which is kind of random? i've started creating a subclass of the MKPointAnnotation in order to save the image. How come the images are swapped around?
Custom MKPointAnnonation class
class AnnonationClass: MKPointAnnotation {
var itemId: NSString?
var itemImage: PFFile?
var itemdescription: NSString?
}
getting the parse objects and adding them using the setGeoPoint method
override func objectsDidLoad(error: NSError!) {
super.objectsDidLoad(error)
if error == nil {
self.pointMapView?.removeAnnotations(pointMapView?.annotations)
for object in objects {
var theObject = object as PFObject
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd" // superset of OP's format
let str = dateFormatter.stringFromDate(object.createdAt)
self.setGeoPoint(theObject.objectForKey("location") as PFGeoPoint, titleString: theObject.objectForKey("title") as NSString, imageFile: theObject.objectForKey("image") as PFFile, theId: theObject.objectId as NSString, descString: theObject.objectForKey("description") as NSString, dateString: str)
}
}
}
Setting the coordinates, title and saving the imageFile in the itemImage
func setGeoPoint(geoPoint:PFGeoPoint, titleString:NSString, imageFile:PFFile, theId:NSString, descString:NSString, dateString:NSString) {
var coordinate = CLLocationCoordinate2DMake(geoPoint.latitude, geoPoint.longitude) as CLLocationCoordinate2D
var pinView:AnnonationClass = AnnonationClass()
pinView.itemImage = imageFile
pinView.setCoordinate(coordinate)
pinView.title = titleString
pinView.subtitle = dateString
self.pointMapView!.addAnnotation(pinView)
}
creating the image and setting it
func mapView(mapView: MKMapView!, viewForAnnotation annotation: AnnonationClass!) -> MKAnnotationView! {
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.pinColor = .Purple
var leftImage: PFImageView = PFImageView(frame: CGRectMake(0, 0, 44, 44))
leftImage.file = annotation.itemImage
leftImage.loadInBackground()
pinView!.leftCalloutAccessoryView = leftImage
}
else {
pinView!.annotation = annotation
}
return pinView
}
In your viewForAnnotation, when pinView != nil, i.e. when reusing a view, it could well be that the image is set from the previous time that view was used. I think it is analogous to setting check marks on table view cells instead of using the data source to properly set properties for the cell contents.
Try setting the image view whether or not the pinView is reused.
func mapView(mapView: MKMapView!, viewForAnnotation annotation: AnnonationClass!) -> MKAnnotationView! {
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.pinColor = .Purple
}
else {
pinView!.annotation = annotation
}
var leftImage: PFImageView = PFImageView(frame: CGRectMake(0, 0, 44, 44))
leftImage.file = annotation.itemImage
leftImage.loadInBackground()
pinView!.leftCalloutAccessoryView = leftImage
return pinView
}
Related
I am trying to add a button to my Callouts so that I can transition to another view controller. The callouts however, won't show the accessory view and I am unsure why. I grab some data from firebase and feed it into the proper arrays, then I have the annotation posted with the call out. I am not sure why the button is not showing up.
This code takes place within ViewDidLoad(){...}
bookRef.observeSingleEvent(of: .value, with: {snapshot in
if let userDict = snapshot.value as? [String:AnyObject]{
for each in userDict as [String:AnyObject]{
let bookLats = each.value["bookLat"] as! String
let bookLngs = each.value["bookLng"] as! String
let bookTitle = each.value["title"] as! String
let Author = each.value["Author"] as! String
let Comment = each.value["Comment"] as! String
let Genre = each.value["Genre"] as! String
let User = each.value["User"] as! String
let bookPhoto = each.value["bookPhoto"] as! String
let userID = each.value["userID"] as! String
let userLocation = each.value["userLocation"] as! String
let bookRate = each.value["bookRating"] as! String
let userToBePushed = each.value["pushingID"] as! String
self.bookLatArrayDouble.append((bookLats as NSString).doubleValue)
self.bookLngArrayDouble.append((bookLngs as NSString).doubleValue)
self.bookTitlesArray.append(bookTitle)
}
}
for i in 0...(self.bookLatArrayDouble.count-1){
let locationCoordinates = CLLocationCoordinate2D(latitude: self.bookLatArrayDouble[i], longitude: self.bookLngArrayDouble[i])
var point = BookAnnotation(coordinate: locationCoordinates)
point.title = self.bookTitlesArray[i]
self.Map.addAnnotation(point)
}
I also used this function.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// If annotation is not of type RestaurantAnnotation (MKUserLocation types for instance), return nil
var annotationView = self.Map.dequeueReusableAnnotationView(withIdentifier: "Pin")
if annotationView == nil{
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "Pin")
annotationView?.canShowCallout = true
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}else{
annotationView?.annotation = annotation
}
return annotationView
}
Edit: imgage requested
I made a very silly of error... I forgot to put Map.delegate.self so it makes sense that the function was never actually called. The button now shows Thank you all for your help!
Your flow control is weird
why just when if annotationView == nil then you add CalloutAccessoryView ??
try this code:
if annotationView == nil{
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "Pin")
}else{
annotationView?.annotation = annotation
}
annotationView?.canShowCallout = true
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
And I'm guess that self.Map.dequeueReusableAnnotationView(withIdentifier: "Pin") may return nil because I don't see your code for that
Return Value
An annotation view with the specified identifier, or nil if no such object exists in the reuse queue.
but when it != nil. you need to add Accessory into it too
I have setup a map in which data is loaded from the parse backend. In map I want to display the images that are already saved in Assets folder. Image should be equal to Player Name . There latitude and longitude is already saved in the Parse.
The position of the player is displayed correctly but the image is not being showed correctly , if the last name in the playerName column is Sachin then the image is same for all the player annotation pin.
I want to apply like if playerName == annotation.title the image of rahul should be displayed .
Console Print :
26.6481591686873 , 77.1777485870544 Sachin
27.655116868732 , 77.17778705437 Rahul
28.6479996556687 , 77.1779252453013 Matt
28.6480628887157 , 77.1779590059193 Virat
28.1578689896 , 76.989079 Sachin
var playerName:String!
override func viewDidLoad() {
super.viewDidLoad()
query.findObjectsInBackgroundWithBlock { (posts, error) in
if error == nil {
let myPosts = posts! as [PFObject]
for posts in myPosts {
let latitude12 = posts["playerLat"] as! Double
let longitude12 = posts["playerLong"] as! Double
let playName = posts["playerName"] as! String
self.playerName = posts["playerName"] as! String
print(latitude12,"," , longitude12, playerName)
let annotation = MKPointAnnotation()
//let customAnno = MKAnnotationView()
let locationCoordinate = CLLocationCoordinate2DMake(latitude12, longitude12)
annotation.coordinate = locationCoordinate
//customAnno.enabled = true
let anno = self.mapView.dequeueReusableAnnotationViewWithIdentifier("places")
if anno == nil {
// if annotation.title == "Sachin" {
//}
annotation.title = playerName
self.mapView.addAnnotations([annotation])
} else {
anno?.annotation = annotation
}
}
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "customAnnotationView"
let annotation1 = MKPointAnnotation()
// custom image annotation
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if (annotationView == nil) {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
}
else {
annotationView!.annotation = annotation
}
if annotation1.title == "Rahul" {
annotationView!.image = UIImage(named: "Rahul")}
else if annotation1.title == "Sachin"{
annotationView!.image = UIImage(named: "Sachin")
}
return annotationView
}
I have tried all codes but the image is same for all the players image.
I am facing a problem about show multiple annotation on MKMapView. My question is that, I have three type annotation like as(select_StarFlag, simple_Flag, currentLocation_Flag). all flag are show on map but there are interchange the position between them, when I scroll the map, some Flag overlap each other. I am using following code
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if (annotation.isKindOfClass(MKUserLocation)) {
//if annotation is not an MKPointAnnotation (eg. MKUserLocation),
//return nil so map view draws"Blue Dot" for standard user location
return nil
}
if (annotation.isKindOfClass(MKPointAnnotation)){
let reuseId = "test"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as MKAnnotationView!
if (anView == nil) {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.canShowCallout = true
var data = Double(annotation.coordinate.latitude)
var data1 = Double(annotation.coordinate.longitude)
var flag = Bool(false)
for geoData in ysDataBaseGeolocationArray{
var geolocationlat = Double()
var geolocationlong = Double()
geolocationlat = Double(geoData.objectAtIndex(0) as NSNumber)
geolocationlong = Double(geoData.objectAtIndex(1) as NSNumber)
if data == geolocationlat && data1 == geolocationlong {
println(geolocationlat)
println(geolocationlong)
flag = true
break
}
}
if flag {
anView.image = UIImage(named:"select_StarFlag")
}
else if data == Double(ysLatitude) && data1 == Double(ysLongitude){
if ysValueFlag != false{
anView.image = UIImage(named:"currentLocation_Flag")
}else {
anView.image = UIImage(named:"simple_Flag")
}
}
else {
anView.image = UIImage(named:"simple_Flag")
}
}
else {
//we are re-using a view, update its annotation reference..
anView.canShowCallout = true
anView.annotation = annotation
}
return anView
}
return nil
}
So I have set up my MKAnnotations in MapView and it's working as I want it to. Now I would like to experiment further and try to change the colors of the pins, what would be the most efficient way to implement this within the following code:
override func viewDidAppear(animated: Bool) {
var annotationQuery = PFQuery(className: "Post")
currentLoc = PFGeoPoint(location: MapViewLocationManager.location)
//annotationQuery.whereKey("Location", nearGeoPoint: currentLoc, withinMiles: 10)
annotationQuery.whereKeyExists("Location")
annotationQuery.findObjectsInBackgroundWithBlock {
(points, error) -> Void in
if error == nil {
// The find succeeded.
println("Successful query for annotations")
// Do something with the found objects
let myPosts = points as! [PFObject]
for post in myPosts {
let point = post["Location"] as! PFGeoPoint
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(point.latitude, point.longitude)
annotation.title = post["title"] as! String!
annotation.subtitle = post["username"] as! String!
func mapView(aMapView: MKMapView!,
viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
let reuseId = "pin"
var pinView = aMapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
println("Pinview was nil")
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.pinColor = .Green
return pinView
}
self.mapView.addAnnotation(annotation)
}
} else {
// Log details of the failure
println("Error: \(error)")
}
}
I can't seem to tackle this on my own even though it seem's simple enough. I am also confused about the viewForAnnotation method, from what I could gather it seems like i need to use it, but nothing i'm trying is working.
I know this is a late answer but for anyone else searching, check out the tutorial here which shows exactly how to colour pins
Ray Wenderlich MapView
I wonder how to set different images to annotation pins on mapview. The difference between the following questions
viewForAnnotation confusion and customizing the pinColor iteratively
Swift different images for Annotation
is that my array of images is generated dynamically with regard to server response. There is no fixed size of the array, so switch/case construction is not a good idea. Moreover, I'm not sure how to apply the solution with custom class aforementioned in topic above. I'm aware that it would be better to post a comment to one of the questions asked before, but unfortunately I'm too rookie at the moment to do that(too few points).
This is the for loop performed inside functions that shows map:
for var r=0;r<arrayOfRestaurants.count;r++
{
var summonRestaurant:NSDictionary = arrayOfRestaurants[r] as NSDictionary
var nearbyRestaurant = Restaurant(nearbyRestaurants:summonRestaurant)
var latRestaurant=(nearbyRestaurant.latitude as NSString).doubleValue
var longRestaurant=(nearbyRestaurant.longitude as NSString).doubleValue
var locationOfRestaurant = CLLocationCoordinate2D(
latitude: latRestaurant as CLLocationDegrees, longitude: longRestaurant as CLLocationDegrees)
var lunchArray: NSArray = nearbyRestaurant.lunch as NSArray
var annotation = MKPointAnnotation()
annotation.setCoordinate(locationOfRestaurant)
annotation.title = nearbyRestaurant.name + " " + nearbyRestaurant.distance + " km"
map.addAnnotation(annotation)
}
And here is viewForAnnotation delegate method(quite identical to the method used in aforementioned threads):
func mapView(map: 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 = map.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.pinColor = .Purple
pinView!.image = globalImageArray[0]
}
else {
pinView!.annotation = annotation
}
return pinView
}
As you can see, I assigned a certain image to pinView which is globalImageArray[0], but I look for a solution that let me iterate over the globalImageArray and assign a certain image to each pin.
I'd be glad to receive any help, thanks in advance!
First, you need to create your own class that adopts the MKAnnotation protocol for your annotations -
class RestaurantAnnotation : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String
var subtitle: String
var image: UIImage?
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}
Then, use instances of this class when you add the annotation and set the image -
for var r=0;r<arrayOfRestaurants.count;r++
{
var summonRestaurant:NSDictionary = arrayOfRestaurants[r] as NSDictionary
var nearbyRestaurant = Restaurant(nearbyRestaurants:summonRestaurant)
var latRestaurant=(nearbyRestaurant.latitude as NSString).doubleValue
var longRestaurant=(nearbyRestaurant.longitude as NSString).doubleValue
let locationOfRestaurant = CLLocationCoordinate2D(
latitude: latRestaurant as CLLocationDegrees, longitude: longRestaurant as CLLocationDegrees)
var lunchArray: NSArray = nearbyRestaurant.lunch as NSArray
let title = nearbyRestaurant.name + " " + nearbyRestaurant.distance +" km"
var annotation = RestaurantAnnotation(coordinate, title:title, subtitle:"")
annotation.image = globalImageArray[r]
map.addAnnotation(annotation)
}
Now, in your view for annotation you can access the image -
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if !(annotation is RestaurantAnnotation) {
return nil
}
let reuseId = "restaurant"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.canShowCallout = true
}
else {
anView.annotation = annotation
}
let restaurantAnnotation = annotation as RestaurantAnnotation
if (restaurantAnnotation.image != nil) {
anView.image = restaurantAnnotation.image!
anView.image.layer.setCornerRadius(8.0)
anView.image.layer.clipsToBounds=true
}
else {
// Perhaps set some default image
}
return anView
}