im trying to draw a path on map which user walked. It works fine on android but ios, just draw at start little line. After "OnElementPropertyChanged" triggered not draw anything.
RouteCoordinates are created on CustomMap based Map class as BindableProperties. I think my property changed method can not reach right display layer or thread.
My custom renderer for IOS:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace PawsApp.iOS.Renderers
{
public class CustomMapRenderer : MapRenderer
{
MKPolylineRenderer polylineRenderer;
CustomMap formsMap;
MKMapView nativeMap;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
if (nativeMap != null)
{
nativeMap.RemoveOverlays(nativeMap.Overlays);
nativeMap.OverlayRenderer = null;
polylineRenderer = null;
}
}
if (e.NewElement != null)
{
formsMap = (CustomMap)e.NewElement;
nativeMap = Control as MKMapView;
UpdatePolyLine();
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (this.Element == null || this.Control == null)
return;
//if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
if ((e.PropertyName == "RouteCoordinates" || e.PropertyName == "VisibleRegion"))
{
formsMap = (CustomMap)sender;
nativeMap = Control as MKMapView;
UpdatePolyLine();
}
}
private void UpdatePolyLine()
{
//var nativeMap = Control as MKMapView;
nativeMap.OverlayRenderer = GetOverlayRenderer;
CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[formsMap.RouteCoordinates.Count];
int index = 0;
foreach (var position in formsMap.RouteCoordinates)
{
coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
index++;
}
var routeOverlay = MKPolyline.FromCoordinates(coords);
nativeMap.AddOverlay(routeOverlay);
}
//[Foundation.Export("mapView:rendererForOverlay:")]
MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
{
if (polylineRenderer == null)
{
var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
//var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as MKPolyline;
polylineRenderer = new MKPolylineRenderer(overlay as MKPolyline)
{
FillColor = UIColor.Yellow,
StrokeColor = UIColor.Red,
LineWidth = 3,
Alpha = 0.4f
};
}
return polylineRenderer;
}
}
}
After trying it on my side, I find if you want to refresh the polyLine in the Map in iOS, you have to:
polyline
;ativeMap.OverlayRenderer
everytime.I changed the customer renderer code like this:
public class CustomMapRenderer : MapRenderer
{
MKPolylineRenderer polylineRenderer;
CustomMap formsMap;
MKMapView nativeMap;
MKPolyline polyline;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
if (nativeMap != null)
{
nativeMap.RemoveOverlays(nativeMap.Overlays);
nativeMap.OverlayRenderer = null;
polylineRenderer = null;
}
}
if (e.NewElement != null)
{
formsMap = (CustomMap)e.NewElement;
nativeMap = Control as MKMapView;
UpdatePolyLine();
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (this.Element == null || this.Control == null)
return;
//if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
if ((e.PropertyName == "RouteCoordinates" || e.PropertyName == "VisibleRegion"))
{
formsMap = (CustomMap)sender;
nativeMap = Control as MKMapView;
UpdatePolyLine();
}
}
private void UpdatePolyLine()
{
//var nativeMap = Control as MKMapView;
if (polyline != null)
{
nativeMap.RemoveOverlay(polyline);
polyline.Dispose();
}
nativeMap.OverlayRenderer = GetOverlayRenderer;
CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[formsMap.RouteCoordinates.Count];
int index = 0;
foreach (var position in formsMap.RouteCoordinates)
{
coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
index++;
}
var routeOverlay = MKPolyline.FromCoordinates(coords);
nativeMap.AddOverlay(routeOverlay);
polyline = routeOverlay;
}
//[Foundation.Export("mapView:rendererForOverlay:")]
MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
{
if (polylineRenderer != null)
{
polylineRenderer = null;
}
var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
//var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as MKPolyline;
polylineRenderer = new MKPolylineRenderer(overlay as MKPolyline)
{
FillColor = UIColor.Yellow,
StrokeColor = UIColor.Red,
LineWidth = 3,
Alpha = 0.4f
};
return polylineRenderer;
}
}
And in the xamarin.forms porject, reset the customMap.RouteCoordinates
every time:
Device.StartTimer(TimeSpan.FromSeconds(3), () =>
{
Device.BeginInvokeOnMainThread(() =>
{
a -= 0.000321;
b += 0.000222;
customMap.RouteCoordinates = new List<Position>
{
new Position (37.797534, -122.401827),
new Position(37.797510, -122.402060),
new Position(37.790269, -122.400589),
new Position(37.790265, -122.400474),
new Position(37.790228, -122.400391),
new Position(37.790126, -122.400360),
new Position(37.789250, -122.401451),
new Position(a, b)
};
});
return true;
});
I also upload my demo here and you can check it. Let me know if it works.