简体   繁体   中英

Xamarin Forms - Multiple Polygons on Map using Custom Map Renderer

I have followed this Xamarin forms tutorial on highlighting a region on a map using a Custom Map Renderer. This tutorial shows you how to add one polygon to the a map in Xamarin Forms, but doesn't explain how to extend the code to allow multiple polygons on a map.

How do I tweak this implementation to allow multiple polygons on a map for iOS? Here is my iOS code:

[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapOverlay.iOS
{
    public class CustomMapRenderer : MapRenderer
    {
        MKPolygonRenderer polygonRenderer;

        protected override void OnElementChanged(ElementChangedEventArgs<View> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null) {
                var nativeMap = Control as MKMapView;
                if (nativeMap != null) {
                    nativeMap.RemoveOverlays(nativeMap.Overlays);
                    nativeMap.OverlayRenderer = null;
                    polygonRenderer = null;
                }
            }

            if (e.NewElement != null) {
                var formsMap = (CustomMap)e.NewElement;
                var nativeMap = Control as MKMapView;

                nativeMap.OverlayRenderer = GetOverlayRenderer;

                CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[formsMap.ShapeCoordinates.Count];

                int index = 0;
                foreach (var position in formsMap.ShapeCoordinates)
                {
                    coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
                    index++;
                }

                var blockOverlay = MKPolygon.FromCoordinates(coords);
                nativeMap.AddOverlay(blockOverlay);
            }
        }




 MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
      {
          if (polygonRenderer == null && !Equals(overlayWrapper, null)) {
              var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
              polygonRenderer = new MKPolygonRenderer(overlay as MKPolygon) {
                  FillColor = UIColor.Red,
                  StrokeColor = UIColor.Blue,
                  Alpha = 0.4f,
                  LineWidth = 9
              };
          }
          return polygonRenderer;
      }

    }
}

There are two problems with your code:

  1. The first problem is in your if (e.NewElement != null){...} code. If you want to have multiple polygons, formsMap.ShapeCoordinates should be of a List<List<Position>> type. So it can have multiple polygons' coordinate groups. As you code shows, it uses List<Position> . And your code triggers nativeMap.AddOverlay(blockOverlay); only once which adds only one overlay into your map.

  2. Change the if (polygonRenderer == null && !Equals(overlayWrapper, null)) in GetOverlayRenderer to if (!Equals(overlayWrapper, null)) . Otherwise, every time the map adds a overlay and triggers the method, it will return the first polygonRenderer .

Here's my code for example:

  1. Code in MainPage.xaml in PCL:

     <ContentPage.Content> <local:CustomMap x:Name="customMap" MapType="Street" WidthRequest="{x:Static local:App.ScreenWidth}" HeightRequest="{x:Static local:App.ScreenHeight}" /> </ContentPage.Content> 
  2. I added two polygons' position lists in MainPage.xaml.cs in PCL:

     List<Position> pos = new List<Position> { new Position(39.939889, 116.423493), new Position(39.930622, 116.423924), new Position(39.930733,116.441135), new Position(39.939944, 116.44056) }; List<Position> posi = new List<Position> { new Position(39.934633, 116.399921), new Position(39.929709, 116.400208), new Position(39.929792, 116.405994), new Position(39.934689,116.405526) }; customMap.ShapeCoordinates.Add(pos); customMap.ShapeCoordinates.Add(posi); customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(39.934689, 116.405526), Distance.FromMiles(1.5))); 
  3. Code in CustomMap.cs in PCL:

     public class CustomMap : Map { public List<List<Position>> ShapeCoordinates { get; set; } public CustomMap() { ShapeCoordinates = new List<List<Position>>(); } } 
  4. Code in CustomMapRenderer.cs in iOS platform:

     class CustomMapRenderer : MapRenderer { MKPolygonRenderer polygonRenderer; protected override void OnElementChanged(ElementChangedEventArgs<View> e) { base.OnElementChanged(e); if (e.OldElement != null) { var nativeMap = Control as MKMapView; if (nativeMap != null) { nativeMap.RemoveOverlays(nativeMap.Overlays); nativeMap.OverlayRenderer = null; polygonRenderer = null; } } if (e.NewElement != null) { var formsMap = (CustomMap)e.NewElement; var nativeMap = Control as MKMapView; nativeMap.OverlayRenderer = GetOverlayRenderer; foreach (List<Position> positionList in formsMap.ShapeCoordinates) { CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[positionList.Count]; int index = 0; foreach (var position in positionList) { coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude); Console.WriteLine(position.Latitude +" : "+ position.Longitude); index++; } var blockOverlay = MKPolygon.FromCoordinates(coords); nativeMap.AddOverlay(blockOverlay); } } } MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper) { if (!Equals(overlayWrapper, null)) { var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay; polygonRenderer = new MKPolygonRenderer(overlay as MKPolygon) { FillColor = UIColor.Red, StrokeColor = UIColor.Blue, Alpha = 0.4f, LineWidth = 9 }; } return polygonRenderer; } } 

It works like this:

在此处输入图片说明

I usually refer to this extended map control when writing my own custom renderer.

TKCustomMap Github Repo

Looking at the iOS renderer for polygons they simply loop through the polygons on the Forms Map and add another overlay to the iOS Map ie

       foreach (var position in formsMap.ShapeCoordinates)
        {
            coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
            var blockOverlay = MKPolygon.FromCoordinates(coords);
            nativeMap.AddOverlay(blockOverlay);
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM