1

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.

Alk
  • 5,215
  • 8
  • 47
  • 116
  • Define "erratic"... is it left, right, upside down, sideways, etc? – l'L'l Aug 07 '16 at 23:41
  • That's the thing, sometimes its to the side of the pin, other times its above the pin, other times the pin is basically in the middle of the view... erratic is the only way I can define it – Alk Aug 07 '16 at 23:42
  • Is it perhaps where you are initially tapping before the pin is re-centered? – l'L'l Aug 07 '16 at 23:43
  • It is related to that, but not exactly, for example if the pin is in the upper section of the view before it is tapped, the callout would move down way below the new position of the pin, similarly if its in the lower half it would move up way too much. – Alk Aug 07 '16 at 23:46
  • It's strange you even need to do all that in the first place — `MKCoordinateRegion` generally will set the view up properly and display the label, callout, etc. accordingly. – l'L'l Aug 08 '16 at 00:01
  • That centers the pin in the middle of the screen, doesn't do anything for the CallOut though...maybe because it's custom and not the default one, not exactly sure? – Alk Aug 08 '16 at 00:08
  • I use it with MKPointAnnotation usually, so a callout might not work the same. – l'L'l Aug 08 '16 at 00:10
  • I'm following this : http://sweettutos.com/2016/03/16/how-to-completely-customise-your-map-annotations-callout-views/ He uses `calloutView.center = CGPointMake(view.bounds.size.width / 2, -calloutView.bounds.size.height*0.52)` to center the callOut (not sure why) but I'm guessing that only works if you don't move the map because it doesn't work for me. – Alk Aug 08 '16 at 00:12

1 Answers1

1

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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044