iosswiftmkmapviewmkannotationviewmkmapviewdelegate

Position Call Out View Above Pin Swift ios MKMapView


I have a MKAnnotation with a custom CallOut View which is added in the following code :

func mapView(mapView: MKMapView,
             didSelectAnnotationView view: MKAnnotationView)
{

    if view.annotation is MKUserLocation
    {
        return
    }


    let customAnnotation = view.annotation as! MyAnnotation

    if (applicable) {
        let calloutView = CustomCallout()
        var r : MKMapRect = largeMapView.visibleMapRect
        let p : MKMapPoint = MKMapPointForCoordinate(customAnnotation.coordinate)
        r.origin.x = p.x - r.size.width * 0.5
        r.origin.y = p.y - r.size.height * 0.75

        // 3

        calloutView.translatesAutoresizingMaskIntoConstraints = false
        calloutView.imageView.translatesAutoresizingMaskIntoConstraints = false

        calloutView.clipsToBounds = true
        calloutView.imageView.clipsToBounds = true

        let mapPointCoordinate : CLLocationCoordinate2D = MKCoordinateForMapPoint(p)
        let pointAsCGPoint : CGPoint = self.largeMapView.convertCoordinate(mapPointCoordinate, toPointToView: self.largeMapView)
        calloutView.frame = CGRectMake(pointAsCGPoint.x, pointAsCGPoint.y, viewWidth! * 0.6, (viewHeight! - 110) * 0.6)
        calloutView.addSubview(calloutView.imageView)
        view.addSubview(calloutView)


        view.addConstraint(NSLayoutConstraint(item: calloutView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: (viewHeight! - 110) * 0.6))
        view.addConstraint(NSLayoutConstraint(item: calloutView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: viewWidth! * 0.6))
        calloutView.addConstraint(NSLayoutConstraint(item: calloutView.imageView, attribute: .Top, relatedBy: .Equal, toItem: calloutView, attribute: .Top, multiplier: 1, constant: 0))
        calloutView.addConstraint(NSLayoutConstraint(item: calloutView.imageView, attribute: .Bottom, relatedBy: .Equal, toItem: calloutView, attribute: .Bottom, multiplier: 1, constant: 0))
        calloutView.addConstraint(NSLayoutConstraint(item: calloutView.imageView, attribute: .Leading, relatedBy: .Equal, toItem: calloutView, attribute: .Leading, multiplier: 1, constant: 0))
        calloutView.addConstraint(NSLayoutConstraint(item: calloutView.imageView, attribute: .Trailing, relatedBy: .Equal, toItem: calloutView, attribute: .Trailing, multiplier: 1, constant: 0))


        largeMapView.setVisibleMapRect(r, animated: true)

    }


}

When the user presses the pin, I move the map to be horizontally centered and vertically in a 25-75 ratio to the pressed pin. Now I'm trying to center the CustomCallOut to be centered right above the pin, but no matter what I do it seems to erratically position itself on the screen.


Solution

  • When using auto-layout, you don't have to adjust frame values, nor do you need to concern yourself with map coordinates. Just add constraints relative to the annotation view.

    For example, this adds a blank gray callout view that is 60x30 right above the MKPinAnnotationView:

    func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
        let calloutView = UIView()
        calloutView.translatesAutoresizingMaskIntoConstraints = false
        calloutView.backgroundColor = UIColor.lightGrayColor()
        view.addSubview(calloutView)
    
        NSLayoutConstraint.activateConstraints([
            calloutView.bottomAnchor.constraintEqualToAnchor(view.topAnchor, constant: 0),
            calloutView.widthAnchor.constraintEqualToConstant(60),
            calloutView.heightAnchor.constraintEqualToConstant(30),
            calloutView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor, constant: view.calloutOffset.x)
        ])
    }
    

    So, set the bottom anchor of the callout to be relative to the MKAnnotationView, and set the centerX to be offset by calloutOffset.

    Clearly, you can set the contents and size of your callout to be whatever you want, but I hope this illustrates the way to get the custom callout view centered over the annotation view.