swiftmkmapviewmapkitmkannotationmkpinannotationview

How can I display a MKPinAnnotationView data (e.g. title, subtitle) in a fixed-size component below the map instead of the popup above the pin?


I have a map in my swift ios app and it is full of markers. Therefore I've decided to use marker clustering taken from here: https://github.com/ribl/FBAnnotationClusteringSwift

After implementing it in my app I see pins and clusters, so that's cool.

This is how I do it - first I call my webservice and fetch all the data for each pin and put it into the array:

func fetchRequests(radius: Double, lat: Double, lon: Double, completionHandler: (() -> Void)?){
    Alamofire.request(.GET, "http://mywebservice.com/fetchdata", parameters: ["param": "1"])
        .responseJSON { response in
            switch response.result {
            case .Success:

                self.array.removeAll()
                if let jsonData = response.result.value as? [[String: AnyObject]] {
                    for requestJSON in jsonData {
                        if let request = SingleRequest.fromJSON(JSON(requestJSON)){

                            let pinOne = FBAnnotation()
                            pinOne.coordinate = CLLocationCoordinate2D(latitude: request.latitude, longitude: request.longitude)
                            pinOne.title = request.title
                            pinOne.subtitle = request.discipline
                            self.array.append(pinOne)
                        }
                    }
                }
                self.clusteringManager.setAnnotations(self.array)
                completionHandler!()

                case .Failure(let error):
                print("SWITCH ERROR")
                print(error)
            }

    }
}

Then I'm actually calling the function from above and doing:

let annotationArray = self?.clusteringManager.clusteredAnnotationsWithinMapRect(self!.mapView.visibleMapRect, withZoomScale:scale)
self?.clusteringManager.displayAnnotations(annotationArray!, onMapView:(self?.mapView)!)

and at the end I'm putting data on map while checking whether each pin is a cluster or a normal pin:

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
    var reuseId = ""
    if annotation.isKindOfClass(FBAnnotationCluster) {
        reuseId = "Cluster"
        var clusterView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
        clusterView = FBAnnotationClusterView(annotation: annotation, reuseIdentifier: reuseId, options: nil)

        return clusterView
    } else {
        reuseId = "Pin"
        var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
        pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        pinView!.calloutOffset = CGPoint(x: -5, y: 5)
        pinView!.canShowCallout = true
        pinView!.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure) as UIView

        return pinView   
    } 
}

So far so good. It works as expected, after clicking each pin I see the popup above it with a title and subtitle. But I would like to achieve a different effect.

When user taps a pin - instead of a popup above it - some square component appears below the map. I was looking for some similar solutions to give you as an example and I found it in the app called Periscope - this is how their map looks before user clicks any event:

enter image description here

and this is what happens right after clicking any point on the map:

enter image description here

This component of course disappears as soon as user scrolls the map.

Is that achievable in Swift?


Solution

  • You could try to add a UITapGestureRecognizer to your MKMapView. Then for the action, create a function that will display the View at the bottom.

    GestureRecognizer added in viewDidLoad():

    let showTap : UIGestureRecognizer = UITapGestureRecognizer(target: self, action: "tapShowView:")
        myMapView.addGestureRecognizer(showTap)
    

    Then your function could be something like this.

     func tapShowView(sender:UIGestureRecognizer) {
    
       //Animate UIView in......
    
       //Or just add your UIView.
        let viewHeight = CGFloat(80)
        let bottomView = UIView(frame: CGRect(x: 0, y: self.view.frame.height - viewHeight, width: self.view.frame.width, height: viewHeight))
            bottomView.backgroundColor = .whiteColor()
    
       //add buttons, navbar etc....
    
        self.myMapView.addSubview(bottomView)
        //OR if you add it in viewDidload and set to bottomView.hidden = true
        bottomView.hidden = false
    
    }
    

    please ignore any syntax errors, I am not sitting at comp with Xcode on it. But this should give you a push. If you have any questions ill be back on when I wake up in a few hours(1:31 am here).. happy coding.

    EDIT:

    I think I miss read your question.....If you want to show the "BottomView" when user touches an annotation. You can call the "tapShowView" method from the MapViewDelegate method.

        func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
    
        if view.annotation!.isKindOfClass(FBAnnotationCluster) {
    
            print("cluster selected")
    
        } else {
    
            print("single pin selected")
            //Call method here to display BottomView.
            self.tapShowView() //Edit tapShowView to remove sender
        }
    
    }
    

    Then to prevent any callout showing when you touch an annotation in this delegate method..

       func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
    

    ADD:

     annotationView?.canShowCallout = true   
    

    EDIT 2: If you need to toggle the view when the user scrolls map etc. You can do that in the region did change delegate method.

     func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool){
      //Just toggle the hidden property of the view.
      BottomView.hidden = true
     }
    

    I would suggest you have a good look at the MKMapView delegate functions. There is a whole bunch of cool stuff you can do with them. Anytime you add something like a UITableView/UICollectionView/MKMapView etc... Take a few minutes to scroll through the delegate functions. Its really amazing how powerful some of the delegate functions are. If you need anything else, Ill be around the rest of the day.