I have a problem where I'm using Xamarin.Forms.Maps and everything works well on the iOS simulator, I open the map, from there I navigate to the Custom pins and all of them are displayed with no issue, however the problem happens when I use a physical device ( iOS 14.6 and wasn't working on the last update neither ).
When I launch the app everything works the same however if I try to go to the nearest pin the app throws an exception System.NullReferenceException: "Object reference not set to the instance of an object", that's only when I try to display multiple pins on the map.
I have a method in my app that redirects me to a pin if clicked on it and that works, it sends me to the pin but if I zoom out the app breaks again. When I debug and set a breakpoint at the method that breaks it is the GetCustomPin method in the GetViewForAnnotation that throws the exception and the problem is that it doesn't pass the argument to the GetCustomPin method if it's for multiple pins but it does if it's for one ( I don't know if it has to do with the fact that it might be working on a background thread when it's for multiple pins ) but it works for Android so it's not a problem in the shared project code.
Heres the code:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace App.iOS
{
public class CustomMapRenderer : MapRenderer
{
UIView customPinView;
List<CustomPin> customPins;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
var nativeMap = Control as MKMapView;
nativeMap.GetViewForAnnotation = null;
nativeMap.CalloutAccessoryControlTapped -= OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView -= OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView -= OnDidDeselectAnnotationView;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MKMapView;
customPins = formsMap.CustomPins;
nativeMap.OverrideUserInterfaceStyle = UIUserInterfaceStyle.Dark;
//nativeMap.WeakDelegate = this;
nativeMap.GetViewForAnnotation = GetViewForAnnotation;
nativeMap.CalloutAccessoryControlTapped += OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView += OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView += OnDidDeselectAnnotationView;
}
}
protected override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation)
{
MKAnnotationView annotationView = null;
if (annotation is MKUserLocation)
return null;
var customPin = GetCustomPin(annotation as MKPointAnnotation);
if (customPin == null)
{
return null;
}
annotationView = mapView.DequeueReusableAnnotation(customPin.Name);
if (annotationView == null)
{
...
annotationView.CalloutOffset = new CGPoint(0, 0);
annotationView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile("icon.png"));
annotationView.RightCalloutAccessoryView = new UIImageView(UIImage.FromFile("icon.png"));
((CustomMKAnnotationView)annotationView).Name = customPin.Name;
((CustomMKAnnotationView)annotationView).Url = customPin.Url;
annotationView.CanShowCallout = true;
}
else
{
annotationView.Annotation = annotation;
}
configureDetailView(annotationView);
return annotationView;
}
void configureDetailView(MKAnnotationView annotationView)
{
int width = 350;
int height = 250;
var snapshotView = new UIView();
snapshotView.TranslatesAutoresizingMaskIntoConstraints = false;
NSDictionary views = NSDictionary.FromObjectAndKey(snapshotView, new NSString("snapshotView"));
snapshotView.AddConstraints(NSLayoutConstraint.FromVisualFormat("H:[snapshotView(250)]", new NSLayoutFormatOptions(), null, views));
snapshotView.AddConstraints(NSLayoutConstraint.FromVisualFormat("V:[snapshotView(80)]", new NSLayoutFormatOptions(), null, views));
var options = new MKMapSnapshotOptions();
options.Size = new CGSize(width, height);
options.MapType = MKMapType.SatelliteFlyover;
options.Camera = MKMapCamera.CameraLookingAtCenterCoordinate(annotationView.Annotation.Coordinate, 250, 65, 0);
var snapshotter = new MKMapSnapshotter(options);
snapshotter.Start((snapshot, error) =>
{
if (snapshot != null)
{
UILabel label = new UILabel();
UILabel label2 = new UILabel();
UILabel label3 = new UILabel();
UILabel label4 = new UILabel();
//UIButton uIButton = new UIButton(UIButtonType.System);
//uIButton.SetTitle("Save", UIControlState.Normal);
//uIButton.Frame = new CGRect(200, 10, 150, 15);
label.Frame = new CGRect(15, 0, 150, 20);
label2.Frame = new CGRect(15, 20, 210, 20);
label3.Frame = new CGRect(15, 40, 210, 20);
label4.Frame = new CGRect(15, 60, 210, 20);
// Add your custom controls here
snapshotView.AddSubviews(label, label2, label3, label4);
}
});
annotationView.DetailCalloutAccessoryView = snapshotView;
}
void OnCalloutAccessoryControlTapped(object sender, MKMapViewAccessoryTappedEventArgs e)
{
CustomMKAnnotationView customView = e.View as CustomMKAnnotationView;
if (!string.IsNullOrWhiteSpace(customView.Url))
{
UIApplication.SharedApplication.OpenUrl(new Foundation.NSUrl(customView.Url));
}
}
void OnDidSelectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
CustomMKAnnotationView customView = e.View as CustomMKAnnotationView;
customPinView = new UIView();
customPinView.Frame = new CGRect(0, 0, 200, 84);
customPinView.Center = new CGPoint(0, -(e.View.Frame.Height + 75));
e.View.AddSubview(customPinView);
}
void OnDidDeselectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
if (!e.View.Selected)
{
customPinView.RemoveFromSuperview();
customPinView.Dispose();
customPinView = null;
}
}
CustomPin GetCustomPin(MKPointAnnotation annotation)
{
var position = new Position(annotation.Coordinate.Latitude, annotation.Coordinate.Longitude);
foreach (var pin in customPins)
{
if (pin.Position == position)
{
return pin;
}
}
return null;
}
}
}
I have no idea how to solve this, thank you for your help.