6

I am currently working on adding cluster annotations to my map view. Everything is working fine with the exception of a couple of things. First of all, I need to refresh the annotations on the map when the user leaves and returns to the view. At the moment this sort of works... The problem is that rather than removing the annotations and adding the new ones, the new ones are just being added in addition of the old ones so there are multiplying each time.

The next issue is that each annotation displays the distance the user is from that annotation, however the annotations are set up before the user's location is found. I assume I will just have to remove and replace the annotations once the location is found, but then I run into the issue of the annotations duplicating again.

This is my code:

override func viewDidAppear(animated: Bool) {
    println(rideArray.count)

    setUpMapView()
}

func setUpMapView() {
    rideArray = ((DataManager.sharedInstance.rideArray) as NSArray) as! [Ride]

    myLocation = mapView.userLocation.coordinate as CLLocationCoordinate2D

    zoomRegion = MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2D(latitude: parkPassed.latitude!, longitude: parkPassed.longitude!), 1000, 1000)
    mapView.setRegion(zoomRegion, animated: true)
    mapView.delegate = self

    for ride in rideArray {
        println("Location: \(mapView.userLocation.coordinate.latitude)")

        var subtitle = ""
        if mapView.userLocation.location == nil {
            subtitle = "Distance unavailable"
        } else {
            let userLocation = CLLocation(latitude: mapView.userLocation.coordinate.latitude, longitude: mapView.userLocation.coordinate.longitude)
            let annotationLocation = CLLocation(latitude: ride.latitude!, longitude: ride.longitude!)

            var distance = Int(CLLocationDistance(annotationLocation.distanceFromLocation(userLocation)))

            if distance > 1000 {
                distance = distance / 1000
                subtitle = "\(distance) kilometers"
            } else {
                subtitle = "\(distance) meters"
            }
        }

        let annotation = RideAnnotation(coordinate: CLLocationCoordinate2DMake(ride.latitude!, ride.longitude!), title: ride.name!, subtitle: subtitle)
        self.qTree.insertObject(annotation)
    }

    var leftSwipe = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
    var rightSwipe = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))

    leftSwipe.direction = .Left
    rightSwipe.direction = .Right

    view.addGestureRecognizer(leftSwipe)
    view.addGestureRecognizer(rightSwipe)
}

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
    if annotation.isKindOfClass(QCluster.classForCoder()) {
        let PinIdentifier = "PinIdentifier"
        var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(ClusterAnnotationView.reuseId()) as? ClusterAnnotationView

        if annotationView == nil {
            annotationView = ClusterAnnotationView(cluster: annotation)
        }
        annotationView!.cluster = annotation

        return annotationView
    } else if annotation.isKindOfClass(RideAnnotation.classForCoder()) {
        var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier("RideAnnotation")

        if pinView == nil {
            pinView = MKAnnotationView(annotation: annotation, reuseIdentifier: "RideAnnotation")
            pinView?.canShowCallout = true
            pinView?.rightCalloutAccessoryView = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as! UIButton
            pinView?.rightCalloutAccessoryView.tintColor = UIColorFromRGB(0x424242)

            let rideTimeView = UIView()
            rideTimeView.frame = CGRectMake(5, 5, 50, 50)
            rideTimeView.layer.cornerRadius = 25

            let waitTimeLabel = UILabel()
            waitTimeLabel.frame = CGRectMake(0, 0, 50, 50)
            if DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime! == "Closed" {
                waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)"
                rideTimeView.backgroundColor = getColorFromNumber(80)
                waitTimeLabel.font = UIFont(name: "Avenir", size: 15)
            } else {
                waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)m"
                rideTimeView.backgroundColor = getColorFromNumber(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!.toInt()!)
                waitTimeLabel.font = UIFont(name: "Avenir", size: 20)
            }
            waitTimeLabel.textAlignment = NSTextAlignment.Center
            waitTimeLabel.textColor = UIColor.whiteColor()
            waitTimeLabel.adjustsFontSizeToFitWidth = true
            waitTimeLabel.numberOfLines = 1

            rideTimeView.addSubview(waitTimeLabel)

            pinView.leftCalloutAccessoryView = rideTimeView
            pinView?.image = UIImage(named: "rideMapAnnotation")
        } else {
            pinView?.annotation = annotation

            let rideTimeView = UIView()
            rideTimeView.frame = CGRectMake(5, 5, 50, 50)
            rideTimeView.layer.cornerRadius = 25

            let waitTimeLabel = UILabel()
            waitTimeLabel.frame = CGRectMake(0, 0, 50, 50)
            if DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime! == "Closed" {
                waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)"
                rideTimeView.backgroundColor = getColorFromNumber(80)
                waitTimeLabel.font = UIFont(name: "Avenir", size: 15)
            } else {
                waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)m"
                rideTimeView.backgroundColor = getColorFromNumber(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!.toInt()!)
                waitTimeLabel.font = UIFont(name: "Avenir", size: 20)
            }
            waitTimeLabel.textAlignment = NSTextAlignment.Center
            waitTimeLabel.textColor = UIColor.whiteColor()
            waitTimeLabel.adjustsFontSizeToFitWidth = true
            waitTimeLabel.numberOfLines = 1

            rideTimeView.addSubview(waitTimeLabel)

            pinView.leftCalloutAccessoryView = rideTimeView
        }
        return pinView
    }
    return nil
}

func reloadAnnotations(){
    if self.isViewLoaded() == false {
        return
    }

    self.cacheArray.removeAll(keepCapacity: false)

    let mapRegion = self.mapView.region
    let minNonClusteredSpan = min(mapRegion.span.latitudeDelta, mapRegion.span.longitudeDelta) / 5
    let objects = self.qTree.getObjectsInRegion(mapRegion, minNonClusteredSpan: minNonClusteredSpan) as NSArray

    for object in objects {
        if object.isKindOfClass(QCluster){
            let c = object as? QCluster
            let neighbours = self.qTree.neighboursForLocation((c?.coordinate)!, limitCount: NSInteger((c?.objectsCount)!)) as NSArray
            for neighbour in neighbours {
                let tmp = self.rideArray.filter({
                    return $0.name == (neighbour.title)!!
                })

                if find(self.cacheArray, tmp[0]) == nil {
                    self.cacheArray.insert(tmp[0], atIndex: self.cacheArray.count)
                }
            }
        } else {
            let tmp = self.rideArray.filter({
                return $0.name == (object.title)!!
            })

            if find(self.cacheArray, tmp[0]) == nil {
                self.cacheArray.insert(tmp[0], atIndex: self.cacheArray.count)
            }
        }
    }

    let annotationsToRemove = (self.mapView.annotations as NSArray).mutableCopy() as! NSMutableArray
    annotationsToRemove.removeObject(self.mapView.userLocation)
    annotationsToRemove.removeObjectsInArray(objects as [AnyObject])
    self.mapView.removeAnnotations(annotationsToRemove as [AnyObject])
    let annotationsToAdd = objects.mutableCopy() as! NSMutableArray
    annotationsToAdd.removeObjectsInArray(self.mapView.annotations)

    self.mapView.addAnnotations(annotationsToAdd as [AnyObject])
}

func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
    viewChanged = true
    self.reloadAnnotations()
}

I apologise for the extensive amount of code, but it all had to be included.

Anyone have any suggestions as to how I can remove the annotations and re-add them once the view appears?

Thanks!

EDIT:

The second issue is now resolved. Here is a bit more detail in regards to the situation and issue:

Basically each annotation represents a ride in a theme park and displays the ride name, current wait time and distance to that ride. At the moment since I am calling setUpMapView() when the view appears, all of the ride annotations are being added to the qTree each time, but are not being removed. This is the issue that I am trying to resolve, but I can't find a way to remove them from the qTree.

user3746428
  • 11,047
  • 20
  • 81
  • 137
  • Just wondering, do you really need to use clustering for the rides. I mean how many rides are there in the theme park? I am not trying to knock your project, just saying maybe it is a bit overboard. If you are worried about performance, you could just load the annotations that in in the current visible view. – the_pantless_coder Jul 30 '15 at 12:44
  • There are around 40 at the moment, but I'll be adding restaurants and other facilities at some point so that might increase to 100-150. – user3746428 Jul 30 '15 at 21:37

2 Answers2

2

MKMapView has the method removeAnnotation:, which will remove a single annotation, and removeAnnotations: which will remove an array of annotations. It also has an annotations property which lets you the current array of annotations. You should be able to select and remove the annotations that you want to remove and then add new ones to replace them.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • I can remove the annotations, but they are still stored in the qTree I believe, so when I add the new ones they are added into the qTree with the old ones. – user3746428 Jul 27 '15 at 23:13
  • Ok, what is a qTree? What do these annotations represent, and how are you creating duplicates? Are you creating new annotations locally? Is there some key saved in the annotations that you could use to recognize duplicates? Please edit your original question with a lot more details (not code, descriptive information) about the problem you are trying to solve. – Duncan C Jul 28 '15 at 23:31
  • The annotations basically have the name of a ride, the wait time for that ride, and the distance that the user is from that ride. The duplicates are being created because I am calling `setUpMapView()` each time the view appears, and all of the annotations from the ride are being added to the qTree (and map) each time. I need a way to remove the old annotations from the qTree (and map although I know how to do that bit already) when the view disappears. – user3746428 Jul 29 '15 at 11:41
-1

I think you could try finding user location like this

let locationManager: CLLocationManager = CLLocationManager()

(write this as a class property, not inside any viewDidLoad!)

then u instantly have locationManager coordinates.

self.mapView.removeAnnotations(annotationsToRemove as [AnyObject])  

could you print the "annotationsToRemove as [AnyObject]" and tell us what is the content ? actually all this code is suspicious:

let annotationsToRemove = (self.mapView.annotations as NSArray).mutableCopy() as! NSMutableArray
annotationsToRemove.removeObject(self.mapView.userLocation)
annotationsToRemove.removeObjectsInArray(objects as [AnyObject])
self.mapView.removeAnnotations(annotationsToRemove as [AnyObject])

you define a constant, then edit it with removeObjects and then pass it to removeAnnotations ? strange man, please get as many prints on these lines as you can and tell what they are so we can help

Ammo
  • 570
  • 1
  • 8
  • 22
  • 1
    What does this have to do with the OPs question? – Duncan C Jul 28 '15 at 23:35
  • The next issue is that each annotation displays the distance the user is from that annotation, however the annotations are set up before the user's location is found. I assume I will just have to remove and replace the annotations once the location is found, but then I run into the issue of the annotations duplicating again. – Ammo Jul 29 '15 at 00:53
  • so the first part of my answer is to tell the OP how to find users location first before setting annotations. – Ammo Jul 29 '15 at 00:54
  • annotationsToRemove as [AnyObject] //<- Could you print it ? AAAnd this is to help with the other problem - the annotations not being removed. self.mapView.removeAnnotations(annotationsToRemove as [AnyObject]) dear sir, please delete your stackoverflow account or start reading the question before answering it next time, goodbye – Ammo Jul 29 '15 at 00:58
  • Ok, that begins to show hints of a coherent answer. Why not go edit your answer to be clear about what you are saying rather than being terse to the point of total mystery? – Duncan C Jul 29 '15 at 00:59
  • Edit your answer to make sense and I'll be happy to up-vote it. As it is what you posted is unintelligible. – Duncan C Jul 29 '15 at 00:59
  • Same with your "could you print it?" bit. Are you asking the OP to try to log the content of `annotationsToRemove`? If so say that, clearly and coherently, using complete sentences. If that's what you meant it's a good suggestion, but I'm not sure if that **is** what you meant. If you're not going to take the time to post coherent answers, don't post answers at all. – Duncan C Jul 29 '15 at 01:02
  • So First of all - the OPs problem is very very very specific and you are just flooding the answer with questions to the OP about qTree, it is obvious that in this case, it is some clustering library. If it is not obvious for you (not a surprise, since you've just barely looked at the question) try googling it instead of chitchatting with OP. Also sir your answer isnt accepted because it doesn't have to do anything with the OPs question. OP uses the methods you explain in his code and he clearly uses them to removeAnnotations so you just stated a fact obvious for the OP and the readers – Ammo Jul 29 '15 at 01:13
  • Ok, it's a clustering library. So that is the actual meat of the issue, and the OP doesn't provide enough information for us to be able to help him/her. That's what my follow-up questions are for. – Duncan C Jul 29 '15 at 01:22
  • Yeah, I apologise for not clearing explaining what the qTree is. As you discovered it is a clustering library. Using the location manager did help resolve the first issue, so now I just need a solution for updating the annotations each time the view reappears. – user3746428 Jul 29 '15 at 11:34