Adding multiple polylines to an MKMapView (Xamarin

2019-06-11 19:28发布

问题:

I'm extending the example provided @ https://developer.xamarin.com/recipes/cross-platform/xamarin-forms/maps/map-overlay/polyline/ to add multiple polylines to my map.

Everything works fine with a single polyline, but when I add the second, or add a polyline alongside an annotation, GetOverlayRenderer errors out with the error

"Value cannot be null.Parameter name: polyline"

It's erring out on line:

polylineRenderer = new MKPolylineRenderer (overlay as MKPolyline);

And when I look at the overlay object, it is:

{MapKit.MKOverlayWrapper}
BoundingMapRect: {{{62634939.7333333, 111606556.311973}, {745654.044444449, 848491.772016793}}}
Coordinate: {CoreLocation.CLLocationCoordinate2D}
Handle: 0x791ca560
Non-public members: 

I don't understand why it works with one polyline, but when I add anything else to the map, it fails (first time through the method, also)

Here's the entire method where the error is occurring. I can provide more code but it works FINE with one line, just not with two. (I loop through a list of objects... if there's 1, it's fine.. if there is 2, it fails).

MKOverlayRenderer GetOverlayRenderer (MKMapView mapView, IMKOverlay overlay)
    {

        if (polylineRenderer == null) {
            try
            {

            polylineRenderer = new MKPolylineRenderer (overlay as MKPolyline);
            polylineRenderer.FillColor = UIColor.Blue;
            polylineRenderer.StrokeColor = UIColor.Red;
            polylineRenderer.LineWidth = 3;
            polylineRenderer.Alpha = 0.4f;
            }
            catch (Exception ex) {
            }
        }
        return polylineRenderer;
    }

回答1:

For the delegate your setting for the nativeMap.OverlayRenderer remember that this is called for every overlay that you add.

When this delegate is invoked it will be passed the IMKOverlay it relates to.

Of which then you can either return a previously stored MKOverlayRenderer, or create a new one.

The reason your code is failing, when you attempt to add two overlays, is because you are trying to return the same renderer for both overlays, i.e. polylineRenderer variable in your code.

Just for demonstration purposes I've modified your GetOverlayRenderer to the following. This isn't a good design, but illustrates the point I'm making above.

When you run this now, you will see one of your MKPolyline in Red, and the other in a Yellow color.

Add this in at class scope:-

private bool cblnIsTest = false;

and then modify your GetOverlayRenderer to the following:-

MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlay)
{
    MKPolylineRenderer polylineRenderer2 = new MKPolylineRenderer(overlay as MKPolyline);

    if (!cblnIsTest)
    {
        cblnIsTest = true;
        //
        polylineRenderer2.StrokeColor = UIColor.Yellow;
    }
    else
    {
        polyline Renderer.StrokeColor = UIColor.Red;
    }
    polylineRenderer2.LineWidth = 3;
    polylineRenderer2.Alpha = 0.4f;
    //
    return polylineRenderer2;
}

Update 1:-

As mentioned above, the code is not good design, and is to illustrate a point only.

Yes - you can re-use MKOverlayRenderer's, however remember renderers are assigned to a specific IMKOverlay that is passed into your delegate.

You are trying to use the same renderer for multiple IMKOverlay's. It's just not possible.

Although you have an existing renderer, you can't assign it to a different IMKOverlay that is passed into your delgate as the Overlay property of the MKOverlayRenderer is read-only, and that's the reason why.

HTH.



回答2:

Please try this:

public override MKOverlayView GetViewForOverlay(MKMapView mapView, IMKOverlay overlay)
        {
            if (overlay is MKPolyline)
            {
                // return a view for the polygon
                MKPolyline l_polyline = overlay as MKPolyline;
                MKPolylineView l_polylineView = new MKPolylineView(l_polyline);
                MKPolylineRenderer l_polylineRenderer = new MKPolylineRenderer(l_polyline);

                l_polylineView.FillColor = UIColor.Blue;
                l_polylineView.StrokeColor = UIColor.Red;

                return l_polylineView;
            }

            return null;
        }


回答3:

@Pete, now I get it, and that did the trick. For my ugly demo, here's what I landed at. It creates a new renderer every time it's called... not good form, but it demonstrates the code you made - essentially I moved my renderer from being scoped across the class to being recreated every time it's requested... bad form, and I'll clean this up to reuse renderers down the road, but it gets the job done for demonstration / understanding.

    MKOverlayRenderer GetOverlayRenderer (MKMapView mapView, IMKOverlay overlay)
    {
        //Moved the renderer here instead of being scoped at the class
        MKPolylineRenderer polylineRenderer= new MKPolylineRenderer (overlay as MKPolyline);
        polylineRenderer.FillColor = UIColor.Blue;
        polylineRenderer.StrokeColor = UIColor.Red;
        polylineRenderer.LineWidth = 3;
        polylineRenderer.Alpha = 0.4f;
        return polylineRenderer;
    }

And there they are!



回答4:

The end result here was that the overlay passed to the method is a wrapper of sorts, and you need to get the "handle" property to get the actual overlay.

public override MKOverlayView GetViewForOverlay (MKMapView mapView, IMKOverlay overlayWrapper)
    {
    if (overlayWrapper != null) {
    var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as MKOverlay;
    // Do Overlay Functions here
    }

The source of this came from https://forums.xamarin.com/discussion/31616/what-is-mkoverlaywrapper-its-breaking-my-map-renderer